mojira.dev
MC-174435

Death loot table seed won't work with "/loot" command on mobs

The bug

The NBT tag of mobs DeathLootTableSeed can only affect the loot when the mob actually dies. However when you use "/loot kill" command on the mob, the loot is still random instead of fixed.

How to reproduce

  1. Summon a sheep with a certain loot table seed:

    /summon minecraft:sheep ~ ~ ~ {DeathLootTable:"minecraft:entities/sheep",DeathLootTableSeed:123}
  2. Kill it:

    /kill @e[sort=nearest,type=sheep,limit=1]

    → You will get exactly 2 piece of mutton.

  3. Summon the sheep again and use /loot command on it:

    /loot give @s kill @e[sort=nearest,type=sheep,limit=1]

    → You know how many mutton you get

Code Analysis

Code analysis by @unknown can be found in this comment

Comments 8

Can confirm for Minecraft 1.16.1

Tried to do a code analysis, if I messed up please tell me 🙂

 

The following was decompiled using https://github.com/hube12/DecompilerMC and CFR.

If we look at net.minecraft.server.commands.LootCommand, the method for /loot kill is declared on line 252.

private static int dropKillLoot(CommandContext<CommandSourceStack> commandContext, Entity entity, DropConsumer dropConsumer) throws CommandSyntaxException {
  if (!(entity instanceof LivingEntity)) {
    throw ERROR_NO_LOOT_TABLE.create((Object)entity.getDisplayName());

     }
  ResourceLocation resourceLocation = ((LivingEntity)entity).getLootTable();
  CommandSourceStack commandSourceStack = (CommandSourceStack)commandContext.getSource();
  LootContext.Builder builder = new LootContext.Builder(commandSourceStack.getLevel());
  Entity entity2 = commandSourceStack.getEntity();
  if (entity2 instanceof Player) {
    builder.withParameter(LootContextParams.LAST_DAMAGE_PLAYER, (Player)entity2);
    }
  *builder.withParameter(LootContextParams.DAMAGE_SOURCE, DamageSource.MAGIC);*
  builder.withOptionalParameter(LootContextParams.DIRECT_KILLER_ENTITY, entity2);
  builder.withOptionalParameter(LootContextParams.KILLER_ENTITY, entity2);
  builder.withParameter(LootContextParams.THIS_ENTITY, entity);
  builder.withParameter(LootContextParams.ORIGIN, commandSourceStack.getPosition());
  LootTable lootTable = commandSourceStack.getServer().getLootTables().get(resourceLocation);
  List<ItemStack> list2 = lootTable.getRandomItems(builder.create(LootContextParamSets.ENTITY));
  return dropConsumer.accept(commandContext, list2, list -> LootCommand.callback(commandSourceStack, list, resourceLocation));
    }

When it adds parameters to the LootContext.Builder (the bolded part), it does not set the random seed (this is done by calling LootContext.Builder.withOptionalRandomSeed). Therefore, the seed is variable.

However, in net.minecraft.world.entity.Mob (this may not be the correct class, but even if this is in a different file the problem and solution are still accurate) on line 463, there's this method:

@Override
protected LootContext.Builder createLootContext(boolean bl, DamageSource damageSource) {
    return super.createLootContext(bl, damageSource).withOptionalRandomSeed(this.lootTableSeed, this.random);
    }

As you can see, it creates a LootContext.Builder with an optional random seed of this.lootTableSeed (which is the NBT tag).

The solution would be to add an additional builder.withOptionalRandomSeed(this.lootTableSeed) in dropKillLoot()

Can confirm in 20w49a.

Can confirm in 21w03a.

Can confirm in 1.19.

Can confirm in 1.19.2.

Can confirm in 1.20.2.

Kong Bai

(Unassigned)

Confirmed

Commands, Loot tables

/loot

1.15.2, 20w10a, 20w14a, 20w16a, 1.16.1, ..., 21w37a, 1.19, 1.19.2, 1.20.2, 1.21.1

Retrieved