Minecraft Inventory Mechanic Bugs - CJ-MC-Mods/Overloaded GitHub Wiki

Overview

This is mostly some random thoughts I have had while I have been writing Minecraft mods. Each point on this list has many more examples and in-depth explanations behind them, but this is mostly for me to record / explain why I made certain decisions in my mods / advice.

Vanilla and most mods do not respect the contract of IItemHandler (IInventory for direct vanilla class). This has caused many bugs over the years and will continue to do so. Vanilla refuses to fix bugs if its not reproducible in vanilla alone, and forge will not fix things that change Vanilla behavior so we are stuck at in impasse.

Slots

Inventory interactions are defined by which slot you are interacting with. Due to this all methods require that you pass in the slot. This means that every inventory must have slots (or at least fake it). This makes it very hard to have connected inventories as you need to keep slot count consistent across the entire network or somehow fake it at access points.

From a performance standpoint having slots is a terrible design. The current way to search for an item in inventory is to loop over every slot until you find it. If your inventory has Integer.MAX_VALUE slots though you are adding a lot of wasted cycles. This also means an external party is looking at the internals to another system ie breaking Law of Demeter. The fix for this would be for insertion to be just offer an item stack with extraction being a predicate function. If it was a predicate function it would also make it very easy to extra multiple stacks from a single call. This would require a major redesign of ItemStack though because of how closely it is tied to slots.

Slots for Infinity Barrel

The Infinity Barrel is designed to have huge storage and be fast as possible. To accomplish this it only has a single "slot", internally it reality just doesn't care what slot you pass it. The issue arises when other things assume my internal state by calling getStackInSlot (such as the Vanilla Hopper). To get around that I could make it so that there is an additional slot that is always empty, but this means inserting an invalid item / when it is full is twice the amount of calls. For the time being I will add a config to enable this trade-off. I might end up making this a per block configurable but that would come later.

ItemStack

ItemStack is how everything is passed around between inventories. There have been several changes of the years to fix some issues with it. First it use to commonplace that an empty stack was represented by null, this is a very bad design (see java Optional and other source material, TODO add link to coding paradigms explanation).

Currently ItemStack is a mutable class but the assumption with inventories is NEVER modify one returned from a method (comment on method by forge: "SERIOUSLY: DO NOT MODIFY THE RETURNED ITEMSTACK"). Either ItemStack should be made immutable or there should be a parent type that Inventories pass around that is effectively immutable. Right now you could technically write a mod that passes an ItemStack to a chest and then modify while it is sitting in that chest. The results of this are not defined and could very easily cause corruption / memory leaks.

getInventoryStackLimit

Vanilla exposes this as part of the Inventory interface but it is only ever used externally by redstone logic. This causes issues in the modding world as some creators think you should use this to override an item's stack size limit (Deep Storage Unit, Infinity Barrel) while others see it as another limit to take it with the Math.min of the item stack (Dirt Chest). Either way those are for internal representations and have no use to external parties.