Good practices in Minecraft modding
A fork of diesieben07’s Common issues and recommendations thread
- Use the registry events or DeferredRegisters to register your blocks, items and other registry entries. Do not create registry entries (like blocks and items) in a static initializer.
- Do not use methods that are deprecated in Forge or vanilla Minecraft. Usually there will be an explanatory method next to the method explaining what to use instead. There is an exception for methods in the Block class, these may be overridden. Instead of calling them, use the one in AbstractBlockState.
- Do not use ITileEntityProvider or ContainerBlock. These classes are legacy vanilla code. To add a TileEntity to your Block, override
createTileEntity(you need the ones with a BlockState parameter) in your Block class.
- Do not use IInventory. Use the capability API with IItemHandler.
- Do not reach across logical sides. This will cause subtle and not-so-subtle issues that may occur randomly and very rarely. Read and understand the sides documentation to understand why this is a bad idea.
- Do not use the unlocalized names for things other than displaying the name of a block/item. If you want to access the registry name for a registry entry, use IForgeRegistryEntry#getRegistryName.
- Do not use numerical IDs for registry entries. They can and will change. Use the static references in e.g. the Items class or use textual IDs (e.g. minecraft:stone) if you need dynamic references.
- Any IForgeRegistryEntry (commonly items and blocks) is singleton-like. That means that there should only ever be one instance of your registered block. There is not a new Block instance for every position in the world and there is not a new Item instance for every ItemStack. This means that you cannot store position-related things as instance fields in your block class, etc. You must use a TileEntity resp. the NBT data on ItemStack.
- An ItemStack variable should never be null and all of vanilla and Forge code expects this to be the case. Use
ItemStack.EMPTYinstead of null and
ItemStack#isEmptyto check for empty stacks. Stack emptiness cannot be determined by equality to ItemStack.EMPTY as referential equality differs greatly from the logic of
ItemStack#isEmptyand will fail in the majority of contexts due to the nature of object references.
- Registry names and asset file names must be completely lowercase.
- Do not access client-only code from common/server code, use the DistExecutor system. See the sides documentation (in particular the section about DistExecutor) for more information.
- Handle exceptions properly. Do not avoid a game crash at all costs, sometimes it is okay to crash the game. Read this article for more information.
Older versions (1.12)
- Do not use the ItemModelMesher.
Use ModelLoader.setCustomModelResourceLocation in ModelRegistryEvent to register your item models.
- TileEntity implementations must always have a no-args constructor as the game relies on blind reflection to construct new instances at runtime. No-arg constructors are implicit for simple classes; it is safe to declare secondary constructors but in that case you must declare an explicit no-args constructor too.
- Do not implement IMessage and IMessageHandler on the same class. It does not make logical sense and leads to confusion and hard to trace bugs.
- Translation keys, TileEntity registration names and enum entries added using EnumHelper should contain your ModID to avoid collisions between mods.
Note: This also applies to the unlocalized name for entities (set by the entityName parameter of
- Do not use the ItemModelMesher.
- The concept of a "common proxy" does not make sense. The whole point of DistExecutor is to abstract over client specific and server specific behavior, the opposite of "common". Common code should go in your main mod class.
- Use @Override when you intend to override methods. This helps tremendously when updating your code for new versions of Minecraft, but in general is good practice. Read this Stack Overflow answer for an explanation.
- Don’t use IHasModel or a similar interface on your Item and/or Block classes to denote that they are capable of registering a model is an anti-pattern and not necessary. All items require a model to be registered for them and usually no type-specific information from the item is needed, only the registry name, which is accessible for all items by default.
- Do not abuse inheritance for code-reuse, such as ItemBase, BaseItem or ItemExampleMod.
What does your ItemBase do that a utility method that receives an Item cannot? In other words, can you replace
someUtilityMethod(new Item(), someNameOrWhatever)? If so, using a utility method will allow you to use ANY Item class such as the ones from vanilla without breaking your pattern. - JamiesWhiteShirt
- Do not use the "export" or "create jar file" functionalities of your IDE or other means to create your final mod file. You must use the Gradle build task. See the documentation for more info.
- When creating a Git repository for your mod, the repository root should be where your build.gradle file is. The MDK ships with a default .gitignore file so that only the necessary files will be added to version control.
Some methods might sound like they do the same thing, although they don’t.
PlayerEntity#getActiveItemStack: Item in use triggered by right click normally (such as blocking with shield or pulling back bow)
PlayerInventory#getItemStack: The item currently held by the mouse in a container
PlayerInventory#getCurrentItem: The item selected on the hotbar