如何在Minecraft Forge中编辑玩家NBT数据?新手Mod开发遇阻
Minecraft Forge模组开发:无法给玩家添加自定义NBT标签的解决方案
核心问题分析
pPlayer.serializeNBT()返回的是玩家NBT的副本对象,修改这个副本后未写回玩家实体,导致修改不会生效。- 自定义的
NLevelTag未实现Tag接口的必要序列化/反序列化方法,Minecraft无法将其正确转换为可存储的NBT数据。 - 直接操作整个玩家NBT(serialize/deserialize)风险极高,容易覆盖原生或其他模组的NBT数据,不推荐使用。
正确解决方案
方案1:使用玩家持久化数据容器(官方推荐)
Minecraft提供的PersistentDataContainer是专门用于存储自定义数据的安全容器,不会干扰原生NBT结构:
- 定义全局数据键(避免键名冲突):
public static final ResourceLocation NECROMANCY_LEVEL_KEY = new ResourceLocation("necromancy", "level_data");
- 修改
finishUsingItem方法:
@Override public ItemStack finishUsingItem(ItemStack stack, Level level, LivingEntity livingEntity) { if (livingEntity instanceof Player pPlayer) { PersistentDataContainer dataContainer = pPlayer.getPersistentData(); if (!dataContainer.contains(NECROMANCY_LEVEL_KEY, Tag.TAG_COMPOUND)) { // 初始化等级数据 CompoundTag levelTag = new CompoundTag(); levelTag.putInt("level", 1); levelTag.putInt("levelXP", 1); // 对应初始levelUp()后的XP值 dataContainer.put(NECROMANCY_LEVEL_KEY, levelTag); } else { // 更新已有数据 CompoundTag levelTag = dataContainer.getCompound(NECROMANCY_LEVEL_KEY); int currentXP = levelTag.getInt("levelXP"); levelTag.putInt("levelXP", currentXP + 5); // 对应levelUp(5)的效果 } } return super.finishUsingItem(stack, level, livingEntity); }
方案2:修复自定义Tag并正确写回玩家NBT(不推荐)
如果坚持使用自定义Tag,必须让NLevelTag完整实现Tag接口:
- 重构
NLevelTag类:
public class NLevelTag implements Tag { private int level; private int levelXP; private static final Logger LOGGER = LogUtils.getLogger(); public NLevelTag(int _level, int _levelXP) { this.level = _level; this.levelXP = _levelXP; } // 从CompoundTag读取数据 public static NLevelTag fromTag(CompoundTag tag) { return new NLevelTag(tag.getInt("level"), tag.getInt("levelXP")); } // 将数据转为CompoundTag public CompoundTag toTag() { CompoundTag tag = new CompoundTag(); tag.putInt("level", this.level); tag.putInt("levelXP", this.levelXP); return tag; } // 实现Tag接口必要方法 @Override public void write(FriendlyByteBuf buf) { buf.writeInt(level); buf.writeInt(levelXP); } @Override public byte getId() { return Tag.TAG_COMPOUND; } @Override public Tag copy() { return new NLevelTag(this.level, this.levelXP); } // 你的levelUp方法 public void levelUp() { this.levelXP++; LOGGER.debug("XP leveled up to " + this.levelXP); } public void levelUp(int times) { for (int i = 0; i < times; i++) { levelUp(); } } }
- 修改
finishUsingItem方法:
@Override public ItemStack finishUsingItem(ItemStack stack, Level level, LivingEntity livingEntity) { if (livingEntity instanceof Player pPlayer) { // 获取玩家NBT副本 CompoundTag entityNBT = pPlayer.saveWithoutId(new CompoundTag()); if (!entityNBT.contains("necromancy_level", Tag.TAG_COMPOUND)) { NLevelTag levelTag = new NLevelTag(1, 0); levelTag.levelUp(); entityNBT.put("necromancy_level", levelTag.toTag()); pPlayer.load(entityNBT); // 将修改后的NBT写回玩家实体 } else { CompoundTag storedTag = entityNBT.getCompound("necromancy_level"); NLevelTag levelTag = NLevelTag.fromTag(storedTag); levelTag.levelUp(5); entityNBT.put("necromancy_level", levelTag.toTag()); pPlayer.load(entityNBT); // 写回实体 } } return super.finishUsingItem(stack, level, livingEntity); }
关键注意事项
- 优先使用
PersistentDataContainer,这是官方推荐的自定义数据存储方案,避免破坏原生NBT,也降低模组冲突概率。 - 自定义Tag必须完整实现
Tag接口的所有方法,否则无法被Minecraft正确序列化。 - 操作玩家NBT副本后,必须调用
load()方法写回实体,否则修改不会生效。
内容的提问来源于stack exchange,提问作者Jade Prince
相关产品推荐
相关产品推荐

