CompatLayer-1.11

From McJty Modding
Jump to: navigation, search

When porting a mod from 1.10.2 to 1.11 you can either do a straight port like usual or else you can try to use CompatLayer which helps in making a mod compatible with 1.10.2 and 1.11 at the same time. However, there are some things you need to do to make this work. It is not automatic. This tutorial goes over the things you have to do and the things you have to watch out for.

The wiki for CompatLayer can be found here: CompatLayer Wiki




Pros and cons

So there are some advantages and disadvantages about writing your mod with CompatLayer. Before you proceed you should read this and decide if this is for you:

Pros

  • Using CompatLayer you have a single branch/source tree for your 1.10.2 and 1.11 versions of the mod. No need to switch
  • Using CompatLayer you have a single jar that runs on both 1.10.2 and 1.11

Cons

  • Your main source tree will be in 1.11 or 1.10.2. You will not be sure your changes work without switching version from time to time. Also it is easy to accidently change code so that it no longer works on the other version without you realizing it.
  • In several cases you have to use ugly 'CompatXxx' classes in order to achieve this compatibility
  • It adds another dependency to your mod.

So consider this well. CompatLayer is not a magic solution. You still have a lot of work before you. But once your mod works with CompatLayer and you did everything right then you should have a lot easier job maintaining your code on both versions.




Preparing your 1.10 branch

I'm assuming that you are using git for versioning. If not then replace this with whatever you are using.

To start I recommend you modify your 1.10 branch to prepare for the move to 1.11 as much as possible. There are lots of things you can already do on the 1.10 code base that are compatible with both 1.10 and 1.11. If you do this it will make the 1.11 branch not that much different from the 1.10 branch. These are the modifications you should already do on your 1.10.2 branch:

  • Your modid should be lowercase. If it is not then you might have a compatibility issue that is hard to resolve.
  • Rename *all* resource files to lowercase. That means texture names, json files and so on. Also make sure that all code references to those resource files are lowercased as well. This is a change that you can do without impact (except for resource pack makers but they will have to adapt to this for 1.11 anyway).
  • All registry names for blocks, items, entities, ... should also be lowercase. If they already are then that's perfect. If not then you will have to add a FMLMissingMappingsEvent handler to your mod to fix this (more on this later).
  • In 1.11 TileEntity.worldObj has been renamed to TileEntity.world. Luckily TileEntity.getWorld() works on both versions so I recommend you change your code to use that
  • In 1.11 Entity.worldObj has been renamed to Entity.world. Use Entity.getEntityWorld() instead

So how do you fix your registry renames in case you were using uppercase names? This code snippet can help. Note that for blocks you also need to fix the ItemBlock. That's why blocks are mentioned in the block *and* item section. This remapping will happen the first time your mod loads on an old world. From that point on blocks in the world will use the new id (for chunks that are loaded that is).

@Mod(modid = ModTut.MODID, name = ModTut.MODNAME, version = ModTut.MODVERSION, dependencies = "required-after:Forge@[11.16.0.1865,)", useMetadata = true)
public class ModTut {
 
 
    @Mod.EventHandler
    public void onMissingMapping(FMLMissingMappingsEvent event) {
        logger.info("Repairing missing mappings");
        for (FMLMissingMappingsEvent.MissingMapping mapping : event.get()) {
            String resourcePath = mapping.resourceLocation.getResourcePath().toLowerCase();
            if (mapping.type == GameRegistry.Type.BLOCK) {
                if ("newblockname".equals(resourcePath)) {
                    mapping.remap(ModBlocks.myNewBlock);
                } else if (...) {
                    ...
                }
            } else if (mapping.type == GameRegistry.Type.ITEM) {
                if ("newitemname".equals(resourcePath)) {
                    mapping.remap(ModItems.myNewItem);
                } else if ("newblockname".equals(resourcePath)) {
                    mapping.remap(Item.getItemFromBlock(ModBlocks.myNewBlock));
                } else if (...) {
                    ...
                }
            }
        }
 
    }




Switching to 1.11

Now make a new 1.11 branch in git based on your current 1.10.2 branch then switch your IDE to that. Update your build.gradle to the latest forge. At the time of writing this is:

minecraft {
    version = "1.11-13.19.0.2180"
    runDir = "run"
    mappings = "snapshot_20161205"
    useDepAts = true
}


Then add the right dependency to compatlayer (which is at the time of writing). I also included dependencies to the 1.11 version of JEI, TOP and the maintained version of WAILA:

repositories {
    maven { // The repo from which to get waila
        name "Mobius Repo"
        url "http://tehnut.info/maven"
    }
    maven { // JEI, McJtyLib and TOP
        url "http://modmaven.k-4u.nl/"
    }
}
 
dependencies {
    deobfCompile "mcp.mobius.waila:Hwyla:1.8.3-B15_1.11"
    deobfCompile "mezz.jei:jei_1.11:4.0.2.194"
    deobfCompile "com.github.mcjty:compatlayer:1.11-0.1.0"
    deobfCompile "mcjty.theoneprobe:TheOneProbe:1.11-1.3.3-46"
}


Now setup your work environment for the new forge and you should be able to go.




All the Errors!

The first thing you should try is to see what compiles and what doesn't. There are a few kinds of errors that can come up:

  • Functions that you are overriding from a vanilla/forge class but that no longer exist. In many cases these functions either have a different name or else have different parameters. If you are lucky chances are that compatlayer will have a solution in the form of a CompatXxx class or interface that you have to extend instead of the vanilla/forge class. Basically you use the CompatXxx class to make abstraction of a change between 1.10.2 and 1.11. These CompatXxx classes typically have their own methods you need to override which is the same on 1.10.2 and 1.11. Examples of this are: CompatBlock for onBlockActivated, CompatCommand for getCommandName/getName and so on.
  • Code that works with entities. Working with entities on 1.11 has changed a bit as they now have a proper ResourceLocation ID. CompatLayer has various helpers in EntityTools to help work around those. Including ways to translate your old-style NBT (that used the 1.10 entity name) to new style NBT that uses the new ResourceLocation.
  • FluidContainerRegistry changes. This class no longer exists in 1.11. CompatLayer gives you FluidTools with which you can get around that.
  • ItemStack related errors. This is a big one and I'll cover a full section on this below.
  • Various other minor changes. Check what CompatLayer has to offer to find a possible solution.
  • If you find something that doesn't seem to be covered and you cannot find a way to do this in a compatible manner in 1.10 and 1.11 then please contact me (McJty) so that I can add something to CompatLayer for this.




What happened to ItemStack's?

There was a very big fundamental change in 1.11 that relates to working with ItemStacks. Basically a 'null' ItemStack is now most likely a mistake. Instead you are supposed to use the new ItemStack.EMPTY which represents an empty stack. So a chest that contains no items is in 1.11 actually filled with EMPTY itemstacks. The consequences of this are potentially big. Especially if your mod works with items a lot. Everywhere in your code where you do things like 'stack == null'. Or 'setStack(null)' you have a bug. Finding these is a challenge.

CompatLayer has an ItemStackTools class that helps you write code that works on 1.10 and 1.11 at the same time. For example, the test 'stack == null' should be replaced with 'ItemStackTools.isEmpty(stack)' which will work on 1.10 and 1.11. Also don't use 'null' for an empty itemstack. Instead use 'ItemStackTools.getEmptyStack()'.

Some recommendations:

  • Manually go over *all* places in your where you use ItemStacks and carefully inspect that code. Every case where an ItemStack is assumed to be null is a bug. Replace that code with something from ItemStackTools.
  • Closely watch the inspections on @Nonnull for itemstacks. CompatLayer for 1.11 has all its methods properly annotated so that this helps with finding those bugs more quickly. CompatLayer for 1.10 obviously doesn't have these.
  • If you have lists of itemstacks in your code it is recommended to use ItemStackList. The 1.11 version of this uses a version which is also used by vanilla and which validates if the stacks are really not null. In many cases it is probably also a good idea to replace arrays of itemstacks with this new list.




Testing

If you use this technique you will most likely be developing in 1.11 (or 1.10.2). Make sure to regularly test your mod in the other version as it is very easy for bugs to creep up.




The Future

What if 1.10.2 is no longer supported? Do I have to keep on using this?

Well that's up to you. If 1.10.2 is no longer supported or you decide you no longer want to support it then I would recommend removing all uses of the CompatXxx classes as those are a bit ugly. In my case I am going to keep on using the ItemStackTools class as I find it easy to find usages of this in my mods. Also who knows what Minecraft 1.12 will bring?