Minecraft Modding: General Tips For Upgrading Between Versions

Background


Minecraft modding relies on reverse-engineering of the Minecraft code (which changes with major updates) and using "hooks" provided by Forge (which is also changing frequently). Therefore, if you want your old mods to work with newer versions of Minecraft and/or Forge you need to get good at upgrading your code.

I believe it is important to become self-sufficient if you're a modder. Instead of asking all the time for other people's help, you need to learn the habits that allowed those other people to figure it out.

In upgrading your mod, there are basically five types of changes that you generally need to look for:
  1. Changes in package organization which requires changes in your imports. For example, when changing from 1.7.10 to 1.8.x the cpw.mods.fml package changed to be named the net.minecraft.fml package. Similarly, when changing from 1.8.x to 1.10.2 the net.minecraft.util package was reorganized to have sub-packages such as the net.minecraft.util.math package.
  2. Changes in class, method or field names. There are cases where the code does the same thing but has changed name.
  3. Changes in method parameters and return values. There are cases where the methods mostly do the same thing, but take some different parameters or return different values.
  4. Interfaces might change. There are cases where additional methods (or other changes) are added to an interface that may need implementation.
  5. Major changes in functionality. Examples include how networking moved to a separate thread in 1.8.x or how the event buses got merged going to 1.9.x. Other examples of major changes are the addition of the two-handed wielding, or the use of IBlockStates and JSON for block models.
In this tutorial, I'll explain how you can use your IDE (e.g. Eclipse) to figure out each of the cases above.

Use Your IDE To Help You Convert (E.g. Eclipse/IntelliJ)


The best tip I can give you when updating your code is to use your IDE well.  A good IDE like Eclipse will provide you with all sorts of warnings and suggestions that will help you figure out how to change your code.

In particular I recommend that you configure your IDE to:
  • Automatically insert @Override annotation where appropriate.  In Eclipse you can do this in the Preferences | Java | Code Style | Clean Up.
  • Automatically clean up imports.  Again in Eclipse you can do this in the Preferences | Java | Code Style | Clean Up
  • Learn how to get suggestions for corrections.  The IDE will usually flag (like with red underline) parts of your code that are problematic.  Hovering over errored code should bring up suggestions from the IDE.  Be careful though to understand the suggestion before accepting it!
  • Lean how to follow the "call hierarchy". The call hierarchy shows you what other code is calling the code that you've selected.  This is very useful when researching methods that you are unfamiliar with, and generally learning how the vanilla classes implement their functionality.
  • Learn how to inspect the "type hierarchy". The type hierarchy gives a list of all available fields and methods within the class and parent classes. This is a great starting point for exploration as you can discover methods you never knew existed and come up with creative mod ideas.

Getting Started With Updating Your Mod


Basically, you should start your upgrade by copying or cloning your project then updating the build.gradle file and your gradle-wrapper.properties file to match the new version. Then run your gradlew setupDecompWorkspace and gradlew eclipse commands and then import the project into Eclipse

You will usually see a lot of errors indicated by Eclipse. Literally every class will probably show errors. Our job then is to go through all these errors one-by-one (yes it is a lot of work) and fix them.

Tip: I recommend also old version of the mod loaded into Eclipse to make it easy to look at the type hierarchy and call hierarchy of the previous version.

Warning: Updating a mod is made difficult because there are so many errors that you can't test each fix as you make it. You have to "fix" everything (I mean make all the IDE errors go away), then do additional testing and debug to ensure it still works.


1. Fixing Updated Package Organization


These problems should be cleared first since it will clear a lot of errors. 

Identification
  • You usually know that this problem is occurring when a class that you know is still used in the latest version is showing as unrecognized. For example, when updating to 1.10.2 I noticed that BlockPos was unrecognized.

Fix
  • For these cases, I usually delete the import for that class and let Eclipse try to find the likely import. (Tip: I usually set Eclipse to update the imports every time the file is saved as mentioned earlier in this tutorial.) 
  • Alternatively, you can look through the Forge source reference library to find it yourself and then correct import.



2. Fixing Changes In Class, Method and Field Names


These are fairly easy to figure out so I usually work on these next.

Identification:

  • If a method or field that you've used in the past is now showing as unrecognized, it's name may have changed. 
  • The same can be said for class names as long as you've already cleared out the import problems.


Fix:
  • If you right-click in Eclipse while hovering over the error it may suggest a replacement. This only works if the new name is similar though. 
  • I usually do is open up the Type Hierarchy for the class and scan through the list to see if any new name looks likely to do the same thing. If you see something that looks like it might work, look at the source to confirm that the functionality is the same.
  • Lastly, you can look at other vanilla code in the new version that uses the same class, method or field that you're trying to resolve. You should be able to readily see what the new name is.

3. Fixing Changes In Method Parameters And Return Types


Warning: I want to emphasize that it is very important to always use the @Override annotation whenever your custom class is meant to override functionality of a parent class or interface. If you don't use this, you will not be able to tell if parameter or return types have changed and your method won't get called but you won't know why! In Eclipse you can turn on (I explain how earlier in this tutorial) a feature that adds the @Override automatically on save which I find useful to confirm that I've properly overridden.

Identification
  • You will probably get an error that says that the @Override isn't applicable because there is no matching parent method. 
  • In any case, if a method is showing as unrecognized even though you know it has the right name, it usually means that the "prototype" doesn't match -- meaning that the number and type of parameters may have changed, or the return value type may have changed. 
  • For example, going to 1.10.2 the Block.getMobilityFlag() method changed to take an additional parameter (IBlockState) and return a different type of value (EnumPushAction).

Fix
  • You have to change your method declaration to match the parent method's parameters and return type. 
  • If you use the changed parameter in your method's code you'll need to update the code accordingly. 
  • If the return type has changed, you'll have to update your code to return the right type of value.

4. Fixing Changes In Interfaces


Although changing interfaces is considered bad coding style, it happens and you'll have to deal with it.

Identification
  • If your class declaration implements an interface and has an error that mentions "unimplemented methods" it means that something in that interface has changed.

Fix
  • You can right-click on the error in Eclipse and choose to automatically generate the unimplemented methods. These will appear at the bottom of your class file. You should then go down to those methods and decide whether you actually want to implement any code in them or leave them as default. 
  • You might want to visit the interface's class file to see if there is some explanation (in comments) for how the new methods are supposed to be used, and you can also look for other classes that use the interface and check what they do with the new methods.

5. Major Changes In Functionality


These can be a real pain but it is very important to understand these. For example, at one point modding started using the JSON and block state format for managing the block models and that required a full rewrite of much of the code related to blocks.

Identification:
  • If your code is showing errors and you can't find a simple replacement that is fixed by one of the other methods above, then there was probably a major functionality change.
  • This is one case where I think it is always okay to ask on a forum for help. Before you start on an upgrade search the forums and if you don't see any good discussion on major changes simply ask "I'm about to update my mod and wonder what the big changes between these versions are."
  • Look for tutorials on the particular upgrade. Some authors (including me) will post some notes related to things we've found during upgrades. For example, I've posted the following upgrade tutorials:
Fix
  • There's no escaping it -- you'll have to rewrite major portions of your mod if you're affected. For example, if you have a mod that has hundreds of custom blocks and then the block modeling system changes, it is going to be a lot of work.
  • Depending on the complexity of your mod, you may actually want to start again from scratch on the new version. While I tend to try to upgrade my existing code, sometimes it makes sense to start fresh.

No comments:

Post a Comment