Jump to content

Recommended Posts

Posted (edited)

Right now I want to replace 2 Enchantments in Enchantments.java, VillagerEntity in EntityType.java and the Crossbow from Items.java to use my own items with custom logic, rather than the default vanilla logic. However, I can't seem to get past the public static final modifiers in said classes, even with reflection...

Registering:

// You can use EventBusSubscriber to automatically subscribe events on the contained class (this is subscribing to the MOD
    // Event bus for receiving Registry Events)
    @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
    public static class RegistryEvents {
        @SubscribeEvent
        public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegistryEvent) {
            // register a new block here
            LOGGER.info("HELLO from Register Block");
            
        }
        
        @SubscribeEvent
        public static void onItemRegistry(final RegistryEvent.Register<Item> event) {
        	crossbow = new ItemForgeCrossbow((new Item.Properties()).maxStackSize(1).group(ItemGroup.COMBAT).maxDamage(326));
        	event.getRegistry().registerAll(crossbow.setRegistryName("crossbow"));
        	try {
				ForgeServerCore.usetFinalStatic(ObfuscationReflectionHelper.findField(Items.class, "field_222114_py"), crossbow);
			} catch (Exception exception) {
				// TODO Auto-generated catch block
				LOGGER.error("Error encountered while overwriting base Items.class fields");
				exception.printStackTrace();
			}
        }
        
        @SubscribeEvent
        public static void onEntityRegistry(final RegistryEvent.Register<EntityType<?>> event) {
        	EntityType<?> villager = EntityType.Builder.<EntityVillager>create(EntityVillager::new, EntityClassification.MISC).size(0.6F, 1.95F).build("villager").setRegistryName("villager");
        	event.getRegistry().registerAll(villager);
        	try {
				ForgeServerCore.usetFinalStatic(ObfuscationReflectionHelper.findField(EntityType.class, "field_200756_av"), villager);
			} catch (IllegalAccessException exception) {
				// TODO Auto-generated catch block
				exception.printStackTrace();
			}
        }
        
        @SubscribeEvent
        public static void onEnchantRegistry(final RegistryEvent.Register<Enchantment> event) {
        	quickCharge = new CustomEnchantmentQuickCharge(Enchantment.Rarity.UNCOMMON, EquipmentSlotType.MAINHAND);
        	piercing = new CustomEnchantmentPiercing(Enchantment.Rarity.COMMON, EquipmentSlotType.MAINHAND);
        	event.getRegistry().registerAll(piercing.setRegistryName("piercing"), quickCharge.setRegistryName("quick_charge"));
        	try {
    			ForgeServerCore.usetFinalStatic(ObfuscationReflectionHelper.findField(Enchantments.class, "field_222193_H"), quickCharge);
    			ForgeServerCore.usetFinalStatic(ObfuscationReflectionHelper.findField(Enchantments.class, "field_222194_I"), piercing);
    		} catch (Exception exception) {
    			// TODO Auto-generated catch block
    			LOGGER.error("Error encountered while overwriting base Enchantments.class fields");
    			exception.printStackTrace();
    		}
        }
    }

In case this is required, these are my custom classes with modified logic:

  Reveal hidden contents
private static Unsafe unsafe;

Field field = null;
		try {
			field = Unsafe.class.getDeclaredField("theUnsafe");
		} catch (NoSuchFieldException exception) {
			// TODO Auto-generated catch block
			exception.printStackTrace();
		} catch (SecurityException exception) {
			// TODO Auto-generated catch block
			exception.printStackTrace();
		} //Internal reference
		if(field == null) {
			LOGGER.fatal("Field is null");
		}
        field.setAccessible(true);
        try {
			unsafe = (Unsafe) field.get(null);
		} catch (IllegalArgumentException exception) {
			// TODO Auto-generated catch block
			exception.printStackTrace();
		} catch (IllegalAccessException exception) {
			// TODO Auto-generated catch block
			exception.printStackTrace();
		}
        
        if(unsafe == null) {
			LOGGER.fatal("Unsafe is null");
		}

public static void usetFinalStatic(Field field, Object object) throws IllegalAccessException {
    	LOGGER.info("Original field value: " + field.get(null));
        //we need a field to update
        //this is a 'base'. Usually a Class object will be returned here.
        final Object base = unsafe.staticFieldBase(field);
        //this is an 'offset'
        final long offset = unsafe.staticFieldOffset(field);
        //actual update
        unsafe.putObject(base, offset, object);
        //ensure the value was updated
        LOGGER.info( "Updated static final value: " + field.get(null));
    }

And this is how I'm trying to modify static final fields ^

 

Why am I doing this? Because these are hardcoded variables stored in a class. Only other way is ASM

Edited by _vertig0
Posted

Use @ObjectHolder, it takes care of everything for you.

It's sad how much time mods spend saying "x is no longer supported on this forum. Please update to a modern version of Minecraft to receive support".

Posted

You also don't need to update the Items and Blocks references, Forge already does that if you register your item using the vanilla registry name.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

  • 3 months later...
Posted (edited)
  On 6/10/2020 at 6:17 PM, Novârch said:

Use @ObjectHolder, it takes care of everything for you.

Expand  

How @ObjectHolder can help in this situation? As far as I know @ObjectHolder just gives reference of the object after objects registration, so it cannot be used to replace vanilla objects, or am I misunderstanding something?

 

Anyway, I figured out how to do it using Java reflection, in this example I replaced wooden sword with custom item that extends SwordItem:

boolean isRegistered = false;
@SubscribeEvent
    public void onItemReg(RegistryEvent.Register<Item> event){
        System.out.println("Entering item registry event");
        if(!isRegistered){
            try{
                Field field = Items.class.getField("WOODEN_SWORD");
                field.setAccessible(true);
                Method method = Items.class.getDeclaredMethod("register", String.class, Item.class);
                method.setAccessible(true);

                Item itemToInsert = (Item)method.invoke(Items.class, "wooden_sword", custom_wooden_sword);

                System.out.println("Item to insert: " + itemToInsert.toString());
                System.out.println("Field: " + field.getName());

                Field modifiersField = Field.class.getDeclaredField("modifiers");
                modifiersField.setAccessible(true);
                modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

                field.set(null, itemToInsert);
                isRegistered = true;
            }catch(Throwable e){
                System.out.println("ERROR while reflecting Items.java field: " + e);
            }
        }

ATTENTION, I do not know how dangerous this method is and how it can negatively affect the operation of the game, but thanks to it I was able to completely replace the standard sword with a custom one and the game did not crash. I think somehow this can be done using standard forge solutions, but no matter how much I searched, I did not find any specific solutions, but this does not change the feeling that if someone experienced saw my code, he would have had a heart attack... (also dont beat me for this boolean, did it on a fast hand)

Edited by byalexeykh
grammar
Posted
  On 9/23/2020 at 4:55 PM, diesieben07 said:

DO NOT do this.

Just register your item with the same name.

Expand  

like this?

private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, MOD_ID);
public static final RegistryObject<Item> WOODEN_SWORD = ITEMS.register("WOODEN_SWORD", () -> custom_wooden_sword);

Most likely I'm doing something wrong, because in this case game crashes:

[19:00:27] [Render thread/ERROR] [ne.mi.fm.ja.FMLModContainer/LOADING]: Failed to load class com.byalexeykh.advancedcombatsystem.AdvancedCombatSystem
java.lang.ExceptionInInitializerError: null
	at java.lang.Class.forName0(Native Method) ~[?:1.8.0_251] {}
	at java.lang.Class.forName(Class.java:348) ~[?:1.8.0_251] {}

and

[19:00:27] [Render thread/INFO] [STDOUT/]: [net.minecraft.util.registry.Bootstrap:printToSYSOUT:110]: ---- Minecraft Crash Report ----
// Shall we play a game?

Time: 9/23/20 7:00 PM
Description: Initializing game

java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
	at net.minecraftforge.fml.javafmlmod.FMLJavaModLanguageProvider$FMLModTarget.loadMod(FMLJavaModLanguageProvider.java:78) ~[forge-1.15.2-31.2.41_mapped_snapshot_20200514-1.15.1-recomp.jar:31.2] {}
	at net.minecraftforge.fml.ModLoader.buildModContainerFromTOML(ModLoader.java:251) ~[forge-1.15.2-31.2.41_mapped_snapshot_20200514-1.15.1-recomp.jar:?] {re:classloading}

at FMLJavaModLanguageProvider.java:78 I found this:

catch (NoSuchMethodException | ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e)
            {
                LOGGER.fatal(LOADING,"Unable to load FMLModContainer, wut?", e);
                throw new RuntimeException(e);
            }

 

Posted
  On 9/23/2020 at 4:55 PM, diesieben07 said:

Just register your item with the same name.

Expand  

 

  On 9/23/2020 at 5:37 PM, diesieben07 said:

"WOODEN_SWORD" is neither a valid registry name nor is it the registry name for the vanilla wooden sword.

Expand  

It is a little unclear from the first time what is meant: the name of the variable in Items.class or the value of the "key" argument in the register method of the same class. Anyway if I change name to "wooden_sword" I'll just add a new item, the old one won't be replaced

image.png.3f6248e7bf7d7d4ee206da1303b5e158.png

 

  On 9/23/2020 at 5:37 PM, diesieben07 said:

Always post the full stack trace.

Expand  

I read it better and, indeed, in "WOODEN_SWORD" case the crash of the game is caused by "Non [a-z0-9/._-] character in path of location"
But this does not change the fact that simply registering an item under the same name does not replace it.
mc 1.15.2
forge 31.2.41

mappings 20200514-1.15.1

Posted
  On 9/24/2020 at 12:19 PM, diesieben07 said:

If you do not specify the domain for the resource location (e.g. "wooden_sword" instead of "minecraft:wooden_sword") during registration then your Mod ID will be assumed

Expand  
private static final DeferredRegister<Item> ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, "minecraft");
public static final RegistryObject<Item> wooden_sword = ITEMS.register("wooden_sword", () -> custom_wooden_sword);

Alright it worked

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • Thanks, I've now installed a slightly newer version and the server is at least starting up now.
    • i have the same issue. Found 1 Create mod class dependency(ies) in createdeco-1.3.3-1.19.2.jar, which are missing from the current create-1.19.2-0.5.1.i.jar Found 11 Create mod class dependency(ies) in createaddition-fabric+1.19.2-20230723a.jar, which are missing from the current create-1.19.2-0.5.1.i.jar Detailed walkthrough of mods which rely on missing Create mod classes: Mod: createaddition-fabric+1.19.2-20230723a.jar Missing classes of create: com/simibubi/create/compat/jei/category/sequencedAssembly/JeiSequencedAssemblySubCategory com/simibubi/create/compat/recipeViewerCommon/SequencedAssemblySubCategoryType com/simibubi/create/compat/rei/CreateREI com/simibubi/create/compat/rei/EmptyBackground com/simibubi/create/compat/rei/ItemIcon com/simibubi/create/compat/rei/category/CreateRecipeCategory com/simibubi/create/compat/rei/category/WidgetUtil com/simibubi/create/compat/rei/category/animations/AnimatedBlazeBurner com/simibubi/create/compat/rei/category/animations/AnimatedKinetics com/simibubi/create/compat/rei/category/sequencedAssembly/ReiSequencedAssemblySubCategory com/simibubi/create/compat/rei/display/CreateDisplay Mod: createdeco-1.3.3-1.19.2.jar Missing classes of create: com/simibubi/create/content/kinetics/fan/SplashingRecipe
    • The crash points to moonlight lib - try other builds or make a test without this mod and the mods requiring it
    • Do you have shaders enabled? There is an issue with the mod simpleclouds - remove this mod or disable shaders, if enabled  
    • Maybe you need to create file in assets/<modid>/items/<itemname>.json with content like this:   { "model": { "type": "minecraft:model", "model": "modname:item/itemname" } }  
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.