Minestom开发Minecraft服务器时玩家下落放置水桶偶发失效问题
问题根因
- Minestom中
Point类的withX()/withY()/withZ()方法是不可变操作,只会返回新的Point实例,不会修改原对象。你现有代码中直接调用这几个方法但没有赋值回placedPos,导致实际设置方块的位置仍是原始点击的铁块位置,而非铁块顶面的目标放置位。下落时客户端和服务端的位置同步存在毫秒级误差,会触发这个逻辑漏洞导致放置失败。 - 你没有取消客户端的方块放置预测逻辑:Minestom默认允许客户端先渲染放置结果,即便服务端实际未生成方块,客户端也会短暂显示放置成功,就会出现你遇到的“客户端显示放了但服务端没生成”的表现。
- 现有逻辑没有对事件有效性做前置校验,也没有处理放置成功后水桶转空桶的逻辑,会进一步加重客户端和服务端的状态不一致问题。
修复后代码
globalEventHandler.addListener(PlayerUseItemOnBlockEvent.class, event -> { final Player player = event.getPlayer(); // 先取消客户端默认预测,所有放置逻辑由服务端判定后同步 event.setCancelled(true); final ItemStack usedItem = event.getItemStack(); if (usedItem.getMaterial() != Material.WATER_BUCKET) { player.getInventory().update(); return; } // 校验点击的是铁块顶面 final Block clickedBlock = player.getInstance().getBlock(event.getPosition()); if (clickedBlock != Block.IRON_BLOCK || event.getBlockFace().normalY() != 1) { player.getInventory().update(); return; } // 正确计算放置位置:接收with方法返回的新Point实例 Point placedPos = event.getPosition() .withY(event.getPosition().y() + 1); // 额外校验放置位置是否可替换,避免其他逻辑占用位置导致放置失效 if (player.getInstance().getBlock(placedPos).isReplaceable()) { player.getInstance().setBlock(placedPos, Block.WATER); // 非创造模式下将用水桶换成空桶 if (player.getGameMode() != GameMode.CREATIVE) { usedItem.setAmount(usedItem.getAmount() - 1); player.getInventory().addItemStack(ItemStack.of(Material.BUCKET)); } player.sendMessage("placed water"); } player.getInventory().update(); });
额外优化建议
- 可增加放置位置与玩家的距离校验,避免客户端恶意发包作弊
- 高版本服务端可额外校验玩家视线是否对准目标放置位,进一步减少位置同步误差导致的偶发失效问题
内容的提问来源于stack exchange,提问作者JustDoom
相关产品推荐
相关产品推荐

