====== 将方块实体数据同步至物品堆 ====== ===== 介绍 ===== 创建有方块实体的方块时,可能需要旗下有来自你的 ''BlockItem'' 的 ''ItemStack'' 的预先定义的 NBT 数据,或者在破坏方块后存在 ''BlockEntity'' 数据至 ''ItemStack''。 本教程,我们假定你已经创建了[[blocks|方块]](和方块物品)以及[[blockentity|方块实体]],并且为它[[blockentity_modify_data|存储了数据]]。 ===== 带有数据的方块掉落 ===== 要让方块掉落的物品堆中能够有来自方块实体的 NBT 或数据组件,只需要改变//战利品表//。 ==== 对于 1.20.5 之后的版本 ==== 因为引入了数据组件,你需要在方块实体中,存在方块物品的数据组件。这需要你将 NBT 数据存储为数据组件。更多请参见[[blockentity modify data#使用数据组件]]。 **如果在使用 1.21 之后的版本**,请将路径中的单词 **''loot_table''** 替换为 **''loot_tables''**。 { "pools": [ { "rolls": 1.0, "bonus_rolls": 0.0, "entries": [ { "name": "tutorial:demo_block", "type": "minecraft:item" } ], "conditions": [ { "condition": "minecraft:survives_explosion" } ] } ], "functions": [ { "source": "block_entity", "include": [ "tutorial:number" ], "function": "minecraft:copy_components" } ] } 其中 * ''%%"include"%%'':数据组件类型的 ID。 ==== 对于 1.20.5 之前的版本 ==== 在 1.20.5 之前,数据组件没有被引入,所以只要复制 NBT。 { "type": "minecraft:block", "pools": [ { "rolls": 1.0, "entries": [ { "type": "minecraft:item", "name": "tutorial:demo_block", "functions": [ { "function": "minecraft:copy_nbt", "source": "block_entity", "ops": [ { "source": "number", "target": "BlockEntityTag.number", "op": "replace" } ] } ] } ] } ] } 其中: * ''%%"source"%%'' 是 NBT 的键,我们在 ''DemoBlockEntity'' 类的 ''writeNbt'' 和 ''readNbt'' 方块中有使用过 -- ''%%"number"%%'' * ''%%"target"%%'' 是掉落的''ItemStack'' 的 NBT 的路径(''source'' 前面是 ''%%"BlockEntityTag"%%'' 键,需要这个才能放置有存储的数据的方块) -- ''%%"BlockEntityTag.number"%%'' 要保存更多的字段,只需要往 ''%%ops%%'' 字段添加更多的 replace 操作(有source、target 和 op)。 ===== 在物品提示中读取存储的数据 ===== 对于 1.20.5 以及之后的版本,要获取存储在物品堆中的方块实体数据,可以使用其数据组件。 **对于 1.20.5 及之后的版本:** public class DemoBlock extends BlockWithEntity { [...] @Override public void appendTooltip(ItemStack stack, Item.TooltipContext context, List tooltip, TooltipType options) { final Integer i = stack.get(TutorialDataComponentTypes.NUMBER); if (i == null) return; tooltip.add(Text.literal("数字:" + i)); } } 对于 1.20.5 之前的版本,我们调用 ''getBlockEntityNbt'',这会在内部调用 ''getSubNbt''。 **对于 1.20.5 之前的版本:** public class DemoBlock extends BlockWithEntity { [...] @Override public void appendTooltip(ItemStack stack, BlockView world, List tooltip, TooltipContext context) { NbtCompound nbt = BlockItem.getBlockEntityNbt(stack); if (nbt == null) return; tooltip.add(Text.literal("数字: " + nbt.getInt("number")) } } 类似地,可以使用 ''getName'' 方法,这样数据可以直接显示在名称中,在这个例子中,你需要修改方块物品注册的方块以创建 ''DemoBlockItem'' 而不是 ''BlockItem''。 public class DemoBlockItem extends BlockItem { public DemoBlockItem(Block block, Settings settings) { super(block, settings); } @Override public Text getName(ItemStack stack) { final MutableText name = Text.translatable(stack.getTranslationKey()); if (stack.contains(TutorialDataComponentTypes.NUMBER)) { name.append(" - number=" + stack.get(TutorialDataComponentTypes.NUMBER)); } return name; } } ===== 拾取带有数据的物品 ===== 在创造模式下,当你按下 ''Ctrl'' 的同时拾取物品(按下鼠标中键),会得到带有所有方块实体数据的物品堆。但是,如果不按下 ''Ctrl'',只会得到没有数据的,除非在 ''DemoBlock'' 类中修改 ''getPickStack'' 方法: @Override public ItemStack getPickStack(WorldView world, BlockPos pos, BlockState state) { final ItemStack pickStack = super.getPickStack(world, pos, state); final BlockEntity blockEntity = world.getBlockEntity(pos); if (blockEntity instanceof DemoBlockEntity demoBlockEntity) { pickStack.applyComponentsFrom(demoBlockEntity.createComponentMap()); } return pickStack; } > **注意:**没有按下 ''Ctrl'' 时,拾取方块是仅发生在客户端的,所以你应该修改 ''toUpdatePacket'' 和 ''toInitialChunkDataNbt'' 并在修改数据时调用 ''updateListeners''(参见[[blockentity modify data#将服务器数据同步至客户端]])。 ===== 有用的资料 ===== 原版全码可以看到更多例子,例如 ''ShulkerBoxBlock'' 和 ''ShulkerBoxBlockEntity'',实现了 ''Nameable'' 接口,并有通过 ''ItemStack'' 和 ''BlockEntity'' **存储自定义名字**的代码。也有一些其他有用的信息: * [[https://zh.minecraft.wiki/w/战利品表|Minecraft Wiki 的战利品表页面]] * [[datagen_loot|战利品表的数据生成]]