====== 将方块实体数据同步至物品堆 ======
===== 介绍 =====
创建有方块实体的方块时,可能需要旗下有来自你的 ''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|战利品表的数据生成]]