Forge 1.20.1中如何将手持物品NBT数据转移至放置的方块
解决手持物品NBT转移到放置方块的空指针问题及实现方案
问题分析
你的代码存在几个导致空指针(NullPointerException)的核心问题:
- 强制类型转换风险:直接将
event.getEntity()转为ServerPlayer,若放置方块的实体不是玩家(如发射器),会触发类型转换异常。 - NBT键空值未处理:
itemNBT.get("tag")在物品无自定义NBT时会返回null,直接用Objects.requireNonNull包裹会触发空指针。 - 方块实体初始化时机:
BlockEvent.EntityPlaceEvent触发时,部分方块的实体可能未完全初始化,直接调用getBlockEntity()可能返回null。 - NBT结构不匹配:方块实体与物品的NBT结构不同,直接将物品的
tag键放入方块实体NBT,不符合多数方块实体的预期格式。
修改后的代码
@SubscribeEvent public static void onBlockPlacedBy(BlockEvent.EntityPlaceEvent event) { // 仅处理玩家放置的情况 if (!(event.getEntity() instanceof ServerPlayer player)) { return; } Block placedBlock = event.getPlacedBlock().getBlock(); if (!blockList.contains(placedBlock)) { return; } ItemStack heldStack = player.getMainHandItem(); // 检查手持物品是否存在自定义NBT if (!heldStack.hasTag()) { return; } CompoundTag itemCustomTags = heldStack.getTag(); BlockPos blockPos = event.getPos(); Level level = event.getLevel(); // 安全获取已初始化的方块实体 level.getBlockEntity(blockPos, BlockEntityTypes.ANY).ifPresent(blockEntity -> { CompoundTag blockNBT = blockEntity.serializeNBT(); // 将物品自定义NBT合并到方块实体NBT中 blockNBT.merge(itemCustomTags); blockEntity.deserializeNBT(blockNBT); // 标记方块实体变更并同步到客户端 blockEntity.setChanged(); level.sendBlockUpdated(blockPos, blockEntity.getBlockState(), blockEntity.getBlockState(), Block.UPDATE_ALL); }); }
关键修改说明
- 安全的实体类型校验:用
instanceof模式匹配获取ServerPlayer,避免类型转换错误。 - 前置NBT检查:通过
heldStack.hasTag()判断物品是否有自定义NBT,跳过无数据的情况。 - 非空安全的方块实体获取:用
getBlockEntity(...)的Optional返回值,仅在实体存在时执行操作,避免空指针。 - 正确的NBT合并方式:使用
merge()方法将物品的自定义NBT直接合并到方块实体的NBT中,对齐原版方块(如告示牌、箱子)的NBT继承逻辑。 - 客户端同步:修改后调用
setChanged()和sendBlockUpdated(),确保客户端能同步到最新的方块实体NBT数据。
额外指引
- 查看Minecraft原版代码中**告示牌(SignBlock)或箱子(ChestBlock)**的放置逻辑,学习原版如何处理物品NBT到方块实体的转移。
- 参考Forge社区的开源示例Mod,专注于NBT处理的小型Mod能提供成熟的实现思路。
- 调试时可使用
Level#sendSystemMessage()输出NBT内容,确认数据是否正确传递。
内容的提问来源于stack exchange,提问作者ZeNyfh
相关产品推荐
相关产品推荐

