Jump to content

[1.7.10] [Solved] How to modify accessibility of fields (the advanced way)


Recommended Posts

Posted

Hi. I'm trying to make an addon for buildcraft.

 

I need to extend the TileEntity for the Quarry. My problem is, I need access to some private fields on that class. I would use reflection if it was a one time thing, but I need to access those fields pretty often. I was trying to read the tutorial for access transformers, but I couldn't understand it. So i decided to ask here for clarification.

 

Here's what I want to do:

 

public class ABuildCraftClass
{
   private int aField;

   public void aMethod()
   {
       //does stuff that requires reading aField.
   }
}

public class MyClass extends ABuildCraftClass
{
   @Override
   public void aMethod()
   {
      //I need to override this one but I will still need the value of aField.
   }
}

 

how can I do this without using reflection for every access?

WIP mods: easyautomation, easyenergy, easyelectronics, easymoney, easytrasportation, easysecurity, easymultiverse, easyfactions, easymagick, easyalchemy, easyseasons

Posted

I know how to do it using reflection, I'm just afraid it may hurt performance. it is not only one field, I simplified it for the sake of the example. it's actually 5 fields. And this method will be called often when the machine is on.

WIP mods: easyautomation, easyenergy, easyelectronics, easymoney, easytrasportation, easysecurity, easymultiverse, easyfactions, easymagick, easyalchemy, easyseasons

Posted

While it is good to worry about performance when choosing an approach to a problem, I really don't think that reflecting 5 fields every tick on an occasional tile entity will have any real impact.

 

Generally, regarding performance optimization you should first code "correctly" (meaning in simplest, common, readable way) and only optimize if you identify a problem. Otherwise you can spend a lot of time and pain on something that didn't matter in the first place.

Check out my tutorials here: http://jabelarminecraft.blogspot.com/

Posted

Also: If reflection is really (measurably) hurting the performance of your mod there is a better way (requiring Java 7 though, but seriously, who uses Java 6...):

 

private static final MethodHandle fieldGet;

static {
    Field field = SomeClass.class.getDeclaredField("field");
    field.setAccessible(true);
    fieldGet = MethodHandles.publicLookup().unreflectGetter(field);
}

// then to call it:
int value = (int) fieldGet.invokeExact((SomeClass) someClassInstance);

 

Important hints:

- It is crucial that the field storing the MethodHandle is static and final. Otherwise the JVM will at the moment not optimize this as well as it could.

 

- The casts when calling invokeExact are necessary. The "exact" part means that you if you pass e.g. a subclass of SomeClass into it you still have to explicitly specify the cast to SomeClass, otherwise it will not work.

 

- For setting repeat the process and replace unreflectGetter with unreflectSetter.

 

- For invoking methods just use unreflect on a Method instance.

 

- If you follow the first point this will optimize very nicely and there is no (yes, no, not almost no) noticeable difference in performance when compared to a direct field access.

 

nice! so this line

 

fieldGet = MethodHandles.publicLookup().unreflectGetter(field);

 

creates a public getter for the field, and I don't need reflection to read it anymore? It will work just as if SomeClass had a public getter for field?

WIP mods: easyautomation, easyenergy, easyelectronics, easymoney, easytrasportation, easysecurity, easymultiverse, easyfactions, easymagick, easyalchemy, easyseasons

Posted

wow, I think I'm actually starting to like java. this magic is awesome  ;D

WIP mods: easyautomation, easyenergy, easyelectronics, easymoney, easytrasportation, easysecurity, easymultiverse, easyfactions, easymagick, easyalchemy, easyseasons

Posted

I press the green button and minecraft loads and so does my mod and everything works fine, but then, when trying to build, I get compilation errors.

 

this is gradle.log:

 

****************************
Powered By MCP:             
http://modcoderpack.com/    
Searge, ProfMobius, Fesh0r, 
R4wk, ZeuX, IngisKahn, bspkrs
MCP Data version : unknown
****************************
:compileApiJava UP-TO-DATE
:processApiResources UP-TO-DATE
:apiClasses UP-TO-DATE
:sourceMainJava UP-TO-DATE
:compileJavawarning: [options] bootstrap class path not set in conjunction with -source 1.6
C:\Forge\build\sources\java\mod\betterquarry\tileentities\BetterQuarryTileEntity.java:94: error: incompatible types: Object cannot be converted to int
		int targetX = (int) targetXGetter.invokeExact((TileQuarry)this);
		                                             ^
C:\Forge\build\sources\java\mod\betterquarry\tileentities\BetterQuarryTileEntity.java:95: error: incompatible types: Object cannot be converted to int
		int targetY = (int) targetYGetter.invokeExact((TileQuarry)this);
		                                             ^
C:\Forge\build\sources\java\mod\betterquarry\tileentities\BetterQuarryTileEntity.java:96: error: incompatible types: Object cannot be converted to int
		int targetZ = (int) targetZGetter.invokeExact((TileQuarry)this);
		                                             ^
Note: C:\Forge\build\sources\java\mod\betterquarry\tileentities\BetterBlockMiner.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: C:\Forge\build\sources\java\mod\betterquarry\tileentities\BetterBlockMiner.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
3 errors
1 warning
FAILED

 

this is build.gradle:

 

buildscript {
    repositories {
        mavenCentral()
        maven {
            name = "forge"
            url = "http://files.minecraftforge.net/maven"
        }
        maven {
            name = "sonatype"
            url = "https://oss.sonatype.org/content/repositories/snapshots/"
        }
    }
    dependencies {
        classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
    }
}

apply plugin: 'forge'

version = "1.7.10-0.1a"
group= "mod.betterquarry.main.BetterQuarry" // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = "BetterQuarry"

minecraft {
    version = "1.7.10-10.13.4.1448-1.7.10"
    runDir = "eclipse"
}

dependencies {    
    compile files("C:/Forge/src/libs/buildcraft-7.0.20-dev.jar")
}

processResources
{
    // this will ensure that this task is redone when the versions change.
    inputs.property "version", project.version
    inputs.property "mcversion", project.minecraft.version

    // replace stuff in mcmod.info, nothing else
    from(sourceSets.main.resources.srcDirs) {
        include 'mcmod.info'
                
        // replace version and mcversion
        expand 'version':project.version, 'mcversion':project.minecraft.version
    }
        
    // copy everything else, thats not the mcmod.info
    from(sourceSets.main.resources.srcDirs) {
        exclude 'mcmod.info'
    }
}

 

I changed the method to invoke and then I got another error.

 

C:\Forge\build\sources\java\mod\betterquarry\tileentities\BetterQuarryTileEntity.java:100: error: method invoked with incorrect number of arguments; expected 2, found 1
			minerSetter.invokeExact((TileQuarry)this, (BlockMiner)(new BetterBlockMiner(worldObj, this, targetX, targetY - 1, targetZ)));

 

Which I fixed changing invokeExact to invokeWithArguments. Problem is, when I test the compiled .jar putting it into my mods folder, it throws an exception when I place my block on the world:

 

java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    mod/betterquarry/tileentities/BetterQuarryTileEntity.positionReached()V @15: invokevirtual
  Reason:
    Type 'mod/betterquarry/tileentities/BetterQuarryTileEntity' (current frame, stack[1]) is not assignable to '[Ljava/lang/Object;'
  Current Frame:
    bci: @15
    flags: { }
    locals: { 'mod/betterquarry/tileentities/BetterQuarryTileEntity' }
    stack: { 'java/lang/invoke/MethodHandle', 'mod/betterquarry/tileentities/BetterQuarryTileEntity' }

 

I don't know how to fix this! If I change back to invokeExact it won't compile. If I use invoke it won't run.

 

also how do I make those exceptions get thrown when I use eclipse's Minecraft client? I wanted to know when my code is broken before trying to build it.

WIP mods: easyautomation, easyenergy, easyelectronics, easymoney, easytrasportation, easysecurity, easymultiverse, easyfactions, easymagick, easyalchemy, easyseasons

Posted

As I said, MethodHandles are Java 7 and thus require you to compile with Java 7. You can change this like this.

 

awesome! thanks again. I put 1.8 because I have Java 8 and I think this way both gradle and eclipse will use the same compiler. It works perfectly now.

WIP mods: easyautomation, easyenergy, easyelectronics, easymoney, easytrasportation, easysecurity, easymultiverse, easyfactions, easymagick, easyalchemy, easyseasons

Posted
It will do magic™ (read sun.misc.Unsafe) stuff under the hood

 

And I have a new Skype status message.

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.

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

    • I know that this may be a basic question, but I am very new to modding. I am trying to have it so that I can create modified Vanilla loot tables that use a custom enchantment as a condition (i.e. enchantment present = item). However, I am having trouble trying to implement this; the LootItemRandomChanceWithEnchantedBonusCondition constructor needs a Holder<Enchantment> and I am unable to use the getOrThrow() method on the custom enchantment declared in my mod's enchantments class. Here is what I have so far in the GLM:   protected void start(HolderLookup.Provider registries) { HolderLookup.RegistryLookup<Enchantment> registrylookup = registries.lookupOrThrow(Registries.ENCHANTMENT); LootItemRandomChanceWithEnchantedBonusCondition lootItemRandomChanceWithEnchantedBonusCondition = new LootItemRandomChanceWithEnchantedBonusCondition(0.0f, LevelBasedValue.perLevel(0.07f), registrylookup.getOrThrow(*enchantment here*)); this.add("nebu_from_deepslate", new AddItemModifier(new LootItemCondition[]{ LootItemBlockStatePropertyCondition.hasBlockStateProperties(Blocks.DEEPSLATE).build(), LootItemRandomChanceCondition.randomChance(0.25f).build(), lootItemRandomChanceWithEnchantedBonusCondition }, OrichalcumItems.NEBU.get())); }   Inserting Enchantments.[vanilla enchantment here] actually works but trying to declare an enchantment from my custom enchantments class as [mod enchantment class].[custom enchantment] does not work even though they are both a ResourceKey and are registered in Registries.ENCHANTMENT. Basically, how would I go about making it so that a custom enchantment declared as a ResourceKey<Enchantment> of value ResourceKey.create(Registries.ENCHANTMENT, ResourceLocation.fromNamespaceAndPath([modid], [name])), declared in a seperate enchantments class, can be used in the LootItemRandomChanceWithEnchantedBonusCondition constructor as a Holder? I can't use getOrThrow() because there is no level or block entity/entity in the start() method and it is running as datagen. It's driving me nuts.
    • Hi here is an update. I was able to fix the code so my mod does not crash Minecraft. Please understand that I am new to modding but I honestly am having a hard time understanding how anyone can get this to work without having extensive programming and debugging experience as well as searching across the Internet, multiple gen AI bots (claude, grok, openai), and examining source code hidden in the gradle directory and in various github repositories. I guess I am wrong because clearly there are thousands of mods so maybe I am just a newbie. Ok, rant over, here is a step by step summary so others can save the 3 days it took me to figure this out.   1. First, I am using forge 54.1.0 and Minecraft 1.21.4 2. I am creating a mod to add a shotgun to Minecraft 3. After creating the mod and compiling it, I installed the .jar file to the proper directory in Minecraft and used 1.21.4-forge-54.1.0 4. The mod immediately crashed with the error: Caused by: java.lang.NullPointerException: Item id not set 5. Using the stack trace, I determined that the Exception was being thrown from the net.minecraft.world.item.Item.Properties class 6. It seems that there are no javadocs for this class, so I used IntelliJ which was able to provide a decompiled version of the class, which I then examined to see the source of the error. Side question: Are there javadocs? 7. This method, specifically, was the culprit: protected String effectiveDescriptionId() {      return this.descriptionId.get(Objects.requireNonNull(this.id, "Item id not set"));  } 8. Now my quest was to determine how to set this.id. Looking at the same source file, I determined there was another method:  public Item.Properties setId(ResourceKey<Item> pId) {             this.id = pId;             return this;   } 9. So now, I need to figure out how to call setId(). This required working backwards a bit. Starting from the constructor, I stubbed out the variable p which is of type Item.Properties public static final RegistryObject<Item> SHOTGUN = ITEMS.register("shotgun", () -> new ShotgunItem(p)); Rather than putting this all on one line, I split it up for readability like this: private static final Item.Properties p = new Item.Properties().useItemDescriptionPrefix().setId(rk); Here is was the missing function, setId(), which takes a type of ResourceKey<Item>. My next problem is that due to the apparent lack of documentation (I tried searching the docs on this site) I could not determine the full import path to ResourceKey. I did some random searching on the Internet and stumbled across a Github repository which gave two clues: import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; Then I created the rk variable like this: private static ResourceKey<Item> rk = ResourceKey.create(Registries.ITEM, ResourceLocation.parse("modid:shotgunmod")); And now putting it all together in order: private static ResourceKey<Item> rk = ResourceKey.create(Registries.ITEM, ResourceLocation.parse("modid:shotgunmod")); private static final Item.Properties p = new Item.Properties().useItemDescriptionPrefix().setId(rk); public static final RegistryObject<Item> SHOTGUN = ITEMS.register("shotgun", () -> new ShotgunItem(p)); This compiled and the mod no longer crashes. I still have more to do on it, but hopefully this will save someone hours. I welcome any feedback and if I missed some obvious modding resource or tutorial that has this information. If not, I might suggest we add it somewhere for people trying to write a mod that doesn't crash. Thank you !!!  
    • I will keep adding to this thread with more information in case anyone can help, or at the very least I can keep my troubleshooting organized. I decided to downgrade to 54.1.0 in the hopes that this would fix the issue but it didn't. At least now I am on a "recommended" version. The crash report did confirm my earlier post that the Exception is coming from effectiveDescriptionId(). I'll continue to see if I can find a way to set the ID manually.   Caused by: java.lang.NullPointerException: Item id not set         at java.base/java.util.Objects.requireNonNull(Objects.java:259) ~[?:?]         at TRANSFORMER/[email protected]/net.minecraft.world.item.Item$Properties.effectiveDescriptionId(Item.java:465) ~[forge-1.21.4-54.1.0-client.jar!/:?]         at TRANSFORMER/[email protected]/net.minecraft.world.item.Item.<init>(Item.java:111) ~[forge-1.21.4-54.1.0-client.jar!/:?]         at TRANSFORMER/[email protected]/com.example.shotgunmod.ShotgunItem.<init>(ShotgunItem.java:19) ~[shotgunmod-1.0.0.jar!/:1.0.0]         at TRANSFORMER/[email protected]/com.example.shotgunmod.ModItems.lambda$static$0(ModItems.java:15) ~[shotgunmod-1.0.0.jar!/:1.0.0]         at TRANSFORMER/[email protected]/net.minecraftforge.registries.DeferredRegister$EventDispatcher.lambda$handleEvent      
    • It just randomly stop working after a rebooted my dedicated server PLEASE HELP!   com.google.gson   Failed to start the minecraft server com.google.gson.JsonSyntaxException: Expected a com.google.gson.JsonObject but was com.google.gson.JsonPrimitive; at path $  
  • Topics

×
×
  • Create New...

Important Information

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