数据附加
数据附加系统允许 Mod 为方块实体、区块和实体附加并存储额外数据。
如需存储额外的世界数据,可以使用 SavedData。
提示 物品堆的数据附加已由原版数据组件取代。
创建附加类型
使用此系统前,需要注册 AttachmentType,其配置包括:
- 默认值 supplier:首次访问数据时创建实例。
- 可选序列化器:需要持久化附加数据时提供。
- 如果配置了序列化器,可设置
copyOnDeath标记,在死亡时自动复制实体数据(见下文)。
建议 如果不希望数据持久化,就不要提供序列化器。
提供序列化器有几种方式:直接实现 IAttachmentSerializer;实现 INBTSerializable 并使用静态方法 AttachmentType#serializable 创建构建器;或向构建器提供 codec。
无论使用哪种方式,都必须将附加类型注册到 NeoForgeRegistries.ATTACHMENT_TYPES。例如:
// Create the DeferredRegister for attachment types
private static final DeferredRegister<AttachmentType<?>> ATTACHMENT_TYPES = DeferredRegister.create(NeoForgeRegistries.ATTACHMENT_TYPES, MOD_ID);
// Serialization via INBTSerializable
private static final Supplier<AttachmentType<ItemStackHandler>> HANDLER = ATTACHMENT_TYPES.register(
"handler", () -> AttachmentType.serializable(() -> new ItemStackHandler(1)).build()
);
// Serialization via codec
private static final Supplier<AttachmentType<Integer>> MANA = ATTACHMENT_TYPES.register(
"mana", () -> AttachmentType.builder(() -> 0).serialize(Codec.INT).build()
);
// No serialization
private static final Supplier<AttachmentType<SomeCache>> SOME_CACHE = ATTACHMENT_TYPES.register(
"some_cache", () -> AttachmentType.builder(() -> new SomeCache()).build()
);
// In your mod constructor, don't forget to register the DeferredRegister to your mod bus:
ATTACHMENT_TYPES.register(modBus);
使用附加类型
注册后即可在任意持有对象上使用。若数据不存在,调用 getData 会附加一个新的默认实例。
// Get the ItemStackHandler if it already exists, else attach a new one:
ItemStackHandler stackHandler = chunk.getData(HANDLER);
// Get the current player mana if it is available, else attach 0:
int playerMana = player.getData(MANA);
// And so on...
如果不希望自动附加默认实例,可以先用 hasData 检查:
// Check if the chunk has the HANDLER attachment before doing anything.
if (chunk.hasData(HANDLER)) {
ItemStackHandler stackHandler = chunk.getData(HANDLER);
// Do something with chunk.getData(HANDLER).
}
也可以使用 setData 更新数据:
// Increment mana by 10.
player.setData(MANA, player.getData(MANA) + 10);
重要 通常在修改方块实体和区块后,需要分别使用
setChanged与setUnsaved(true)标记其已更改。调用setData时会自动完成:chunk.setData(MANA, chunk.getData(MANA) + 10); // will call setUnsaved automatically但如果修改的是通过
getData取得的数据(包括新创建的默认实例),则必须显式标记方块实体或区块已更改:var mana = chunk.getData(MUTABLE_MANA); mana.set(10); chunk.setUnsaved(true); // must be done manually because we did not use setData
与客户端共享数据
要将方块实体、区块或实体附加数据同步到客户端,需要自行向客户端发送数据包。对于区块,可以通过 ChunkWatchEvent.Sent 判断何时应向玩家发送区块数据。
玩家死亡时复制数据
默认情况下,实体附加数据不会在玩家死亡时复制。若希望自动复制,请在构建器中设置 copyOnDeath。
更复杂的处理可以通过 PlayerEvent.Clone 实现:读取原实体的数据,再赋给新实体。事件的 #isWasDeath 方法可区分死亡重生与从末地返回。这一点很重要,因为从末地返回时数据已经存在,必须避免重复累加。
例如:
NeoForge.EVENT_BUS.register(PlayerEvent.Clone.class, event -> {
if (event.isWasDeath() && event.getOriginal().hasData(MY_DATA)) {
event.getEntity().getData(MY_DATA).fieldToCopy = event.getOriginal().getData(MY_DATA).fieldToCopy;
}
});
来源: NeoForged/Documentation(1.21.1)。中文翻译依据提交
4a680cdd4d15。