User Tools

Site Tools


zh_cn:tutorial:blockentity_sync_itemstack

将方块实体数据同步至物品堆

介绍

创建有方块实体的方块时,可能需要旗下有来自你的 BlockItemItemStack 的预先定义的 NBT 数据,或者在破坏方块后存在 BlockEntity 数据至 ItemStack

本教程,我们假定你已经创建了方块(和方块物品)以及方块实体,并且为它存储了数据

带有数据的方块掉落

要让方块掉落的物品堆中能够有来自方块实体的 NBT 或数据组件,只需要改变战利品表

对于 1.20.5 之后的版本

因为引入了数据组件,你需要在方块实体中,存在方块物品的数据组件。这需要你将 NBT 数据存储为数据组件。更多请参见使用数据组件

如果在使用 1.21 之后的版本,请将路径中的单词 loot_table 替换为 loot_tables

src/main/resources/data/tutorial/loot_table/blocks/demo_block.json
{
  "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。

src/main/resources/data/tutorial/loot_tables/blocks/demo_block.json
{
  "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 类的 writeNbtreadNbt 方块中有使用过 – "number"
  • "target" 是掉落的ItemStack 的 NBT 的路径(source 前面是 "BlockEntityTag" 键,需要这个才能放置有存储的数据的方块) – "BlockEntityTag.number"

要保存更多的字段,只需要往 ops 字段添加更多的 replace 操作(有source、target 和 op)。

在物品提示中读取存储的数据

对于 1.20.5 以及之后的版本,要获取存储在物品堆中的方块实体数据,可以使用其数据组件。

对于 1.20.5 及之后的版本:

DemoBlock.class
public class DemoBlock extends BlockWithEntity {
 
    [...]
 
    @Override
    public void appendTooltip(ItemStack stack, Item.TooltipContext context, List<Text> 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 之前的版本:

DemoBlock.class
public class DemoBlock extends BlockWithEntity {
 
    [...]
 
    @Override
    public void appendTooltip(ItemStack stack, BlockView world, List<Text> tooltip, TooltipContext context) {
        NbtCompound nbt = BlockItem.getBlockEntityNbt(stack);
        if (nbt == null) return;
 
        tooltip.add(Text.literal("数字: " + nbt.getInt("number"))
    }
}

类似地,可以使用 getName 方法,这样数据可以直接显示在名称中,在这个例子中,你需要修改方块物品注册的方块以创建 DemoBlockItem 而不是 BlockItem

DemoBlockItem.class
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 时,拾取方块是仅发生在客户端的,所以你应该修改 toUpdatePackettoInitialChunkDataNbt 并在修改数据时调用 updateListeners(参见将服务器数据同步至客户端)。

有用的资料

原版全码可以看到更多例子,例如 ShulkerBoxBlockShulkerBoxBlockEntity,实现了 Nameable 接口,并有通过 ItemStackBlockEntity 存储自定义名字的代码。也有一些其他有用的信息:

zh_cn/tutorial/blockentity_sync_itemstack.txt · Last modified: 2024/08/27 02:48 by solidblock