I'm trying to write a Forge mod for Minecraft 1.12.2. I want to make modifications to the main menu (which some mods do, so I know it's possible) but the things that I want to change (which again, other mods do) are private variables. The I'm doing this is by extending GuiMainMenu, but I'm having trouble with private variables. In particular, I want to bind GuiMainMenu.MINECRAFT_TITLE_TEXTURE (which is a private static final) to the texture manager so that I can draw the minecraft title texture like in the vanilla main menu, and I want to add splash text options (like so: this.splashText = "example_splash";). GuiMainMenu.splashText is a private String
Looking around thus far, it seems like people use reflection to get around this kind of issue, and that's what I've tried, with code like:
private Field splashTextField;
private static Field minecraftTitleTextureField;
static {
initField("MINECRAFT_TITLE_TEXTURES", minecraftTitleTextureField);
}
private static void initField(String fieldName, Field destinationField) {
try {
// Accessing private variable using reflection
destinationField = GuiMainMenu.class.getDeclaredField(fieldName);
destinationField.setAccessible(true);
} catch (NoSuchFieldException e) {
e.printStackTrace(); // Handle the exception appropriately
}
}
// Wrapper methods to handle IllegalAccessException
private static void setFieldValue(Object target, Field field, Object value) {
try {
field.set(target, value);
} catch (IllegalAccessException e) {
e.printStackTrace(); // Handle the exception appropriately
}
}
private static Object getFieldValue(Object target, Field field) {
try {
return field.get(target);
} catch (IllegalAccessException e) {
e.printStackTrace(); // Handle the exception appropriately
return null;
}
}
But when I do this, I get NullPointerExceptions when I try to do anything with either of these variables. What am I doing wrong?
The commenters already gave good explanation on how to fix using reflection myself, but for posterity, I've found out that the usual way to do this for minecraft forge mod development is to use access transformers, which modifies access at runtime.
This involves adding
accesstransformer.cfgto thesrc/main/resources/META-INF/folder, and adding lines as specified on the github, these docs or these docs. Then, the Access Transformer needs to be enabled in the build.gradle file by adding to the minecraft block:But, if you only do this, it will compile, and it will probably run ok when you do
./gradlew runClient, but when you copy the mod .jar file into your mods folder, it will not work. This is because the forge modloader needs to be told that there's an Access Transformer file, and what it's called, so that it knows to do the appropriate modification of access in runtime. This is necessary because the access transformation is done by forge, not by the mod itself. Building works with only the addition to the minecraft{} block because that does the necessary changes to the build environment for building to succeed. It's kind of like how in Linux, you have the linker andld.So, to get it to work at runtime, you need to add the access transformer file to the manifest, which should be done via the build.gradle file like so:
FMLAT stands for Forge ModLoader Access Transformer.