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
Summon a sheep with a certain loot table seed:
/summon minecraft:sheep ~ ~ ~ {DeathLootTable:"minecraft:entities/sheep",DeathLootTableSeed:123}
Kill it:
/kill @e[sort=nearest,type=sheep,limit=1]
→ You will get exactly 2 piece of mutton.
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
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 for Minecraft 1.16.1