Mojira Archive
MC-91727

loot table "spreading large stacks" will overwrite existing items

The bug

Item stacks with a count > 1 generated by loot tables are spread into multiple smaller item stacks even if there is not enough place in the container resulting in a loss of items and the following warning message:

[Server thread/WARN]: Tried to over-fill a container

How to reproduce

  1. Create a loot table which generates 27 item stacks, with some of them having a count > 1
    {
        "pools": [
            {
                "rolls": 24,
                "entries": [
                    {
                        "type": "item",
                        "name": "minecraft:iron_sword",
                        "weight": 1
                    }
    	    ]
    	},
            {
                "rolls": 3,
                "entries": [
                    {
                        "type": "item",
                        "name": "minecraft:torch",
                        "functions": [
                            {
                                "function": "set_count",
                                "count": 16
                            }
                        ],
                        "weight": 1
                    }
                ]
            }
        ]
    }
    
  2. Save it for example in a data pack under \data\loot_tables\custom\test_loot_table.json
  3. Load the world and place a chest with the loot table
    /setblock ~ ~ ~ chest{LootTable:"custom:test_loot_table"}
    
  4. Open the chest
    → You will notice that it contains less than 24 swords, but the torch item stacks are split into more than 3. Additionally a warning should appear in the log: "[Server thread/WARN]: Tried to over-fill a container"

Code analysis and suggested fix

Based on 1.11.2 decompiled using MCP 9.35 rc1

The problem is that the method net.minecraft.world.storage.loot.LootTable.shuffleItems(List<ItemStack>, int, Random) only calculates the amount of free slots once and not in every iteration and additionally the amount of stacks which should be split is not subtracted.

Suggested fix
/**
 * shuffles items by changing their order and splitting stacks
 *
 * @param p_186463_2_ Free slots in container
 */
private void shuffleItems(List<ItemStack> stacks, int p_186463_2_, Random rand)
{
    // Comment: "list" contains the item stacks to split
    List<ItemStack> list = Lists.<ItemStack>newArrayList();
    Iterator<ItemStack> iterator = stacks.iterator();

    while (iterator.hasNext())
    {
        ItemStack itemstack = (ItemStack)iterator.next();

        if (itemstack.isEmpty())
        {
            iterator.remove();
        }
        else if (itemstack.getCount() > 1)
        {
            list.add(itemstack);
            iterator.remove();
        }
    }

    // Comment: Removed this and moved it in the loop condition
    // p_186463_2_ = p_186463_2_ - stacks.size();

    // while (p_186463_2_ > 0 && ((List)list).size() > 0)
    while ((p_186463_2_ - stacks.size() - list.size()) > 0 && ((List)list).size() > 0)
    {
        ItemStack itemstack2 = (ItemStack)list.remove(MathHelper.getInt(rand, 0, list.size() - 1));
        int i = MathHelper.getInt(rand, 1, itemstack2.getCount() / 2);
        ItemStack itemstack1 = itemstack2.splitStack(i);

        if (itemstack2.getCount() > 1 && rand.nextBoolean())
        {
            list.add(itemstack2);
        }
        else
        {
            stacks.add(itemstack2);
        }

        if (itemstack1.getCount() > 1 && rand.nextBoolean())
        {
            list.add(itemstack1);
        }
        else
        {
            stacks.add(itemstack1);
        }
    }

    stacks.addAll(list);
    Collections.shuffle(stacks, rand);
}