Minecraft Modding: Java Tips

Learning Java

It is very difficult to mod without being fairly fluent in Java. It is actually harder to modify other people's code than to write your own because you will have to "reverse engineer" the code in order to understand it enough to begin to attempt to modify. But to reverse engineer you need to understand Java well.

While some youngsters seem to like learning with videos, I strongly suggest that programming should be learned from a book. This is for a couple reasons:
  1. You'll want to be coding at the same time you're learning, so flipping back and forth from a video to your development environment will be distracting. Much easier to have a book open beside you.
  2. People who watch videos tend to skip to the interesting parts. However, the most important parts of a programming language might not seem too interesting to a student. A book presents all the information in a very specific order that is important, and I find the tendency to skip is less. For example a topic like inheritance or scope is extremely important, but I doubt someone would generally watch a video on such a topic.
The problem with programming books is that often are a bit scary (very large) and boring (lots of text). However, I found a book that is very nice: Java In Easy Steps by Mike McGrath. It is not a large book but it covers all the topics you need in order to mod with Java, and it is colorful and simply written.

Tip: Videos are not the best way to learn programming. Use an actual book to learn programming.

After you've gone through a book like Java In Easy Steps, then you might want to get a more comprehensive programming reference book.

Tip: Jakob Jenkov provides great tutorials on Java as well.

Java Reflection And Access Modification

Most programming languages have very strict control of "scope", which means limiting the access to classes, methods and fields. In addition to security, this can also help with keeping your code bug-free because you can "encapsulate" your code such that interactions happen through well-defined interfaces.

Minecraft modding requires you to have access Minecraft code that may not provide the scope to give you access. Forge helps because it uses access transformations to provide "hooks" that allow public access to commonly modded things. For example, Forge events can let you intercept rendering, or mob drops, etc. However, sometimes you'll want to modify something that doesn't have a Minecraft public method and also doesn't have Forge hooks -- in that case you'll need to use some sort of access modification method.

The main access modification method you should consider is called Java "reflection". Reflection effectively changes the scope of fields and methods such that they accessible when they normally wouldn't be.

See Jakob Jenkov's Java Reflection Tutorial to get an understanding of how to use reflection.

Reflection sometimes is criticized for poor performance, but really that is only a consideration in very intense performance situations -- simply accessing a field or two with reflection every tick shouldn't cause any noticeable performance issue. However, if you're really concerned about the performance, it is possible to minimize the impact with a trick explained by diesieben07 -- storing the method handle in a public static field and invoke using that handle.

Warning: You cannot use normal Java reflection because the Minecraft code is obfuscated but we mod in an unobfuscated environment. Instead you must use the ReflectionHelper class provided by Forge. If you use normal reflection it will appear to work if you run from your IDE but will fail when you build the final mod JAR.

For example, at the time of writing this tutorial, the registry for Advancement criteria triggers was private so I had to use ReflectionHelper to access it as follows:

     Method method;
method = ReflectionHelper.findMethod(CriteriaTriggers.class, "register", "func_192118_a", ICriterionTrigger.class);
method.invoke(null, Triggers.TAME_BIRD);

Key Point: In the example above "register" is the unobfuscated name of the method, while "func_192118_a" is the obfuscated name. You can find the latest list of obfuscated to unobfuscated mappings spreadsheet at the MCPBot site or even use the MCP mapping viewer tool.

Note: If you copy the code above you'll find that your IDE will highlight an error because the above code throws exceptions. If you are familiar with exception handling then go ahead and handle it properly but if not you can just accept the IDE suggestion to fix it with a "try-catch". Since it throws multiple exceptions you will need to add several catch statements (just keep accepting the suggestions).

No comments:

Post a Comment