为什么函数 safeBreak() 会在 Call 1 中按预期执行,但在 Call 2 或 Call 2.1 中却没有?
我正在开发我的 Minecraft Bukkit 插件ToolBelt。
我的插件中的一个工具(PickHax)允许玩家在持有定义的材质(默认 DIAMOND_PICKAXE)的同时删除一个块。为了为提供区域保护(如 WorldGuard)和日志记录(如 HawkEye)的其他插件提供尽可能广泛的支持,我创建并调用了 BlockBreakEvent。下面是执行此操作的代码:
protected boolean safeBreak(Block target, Player subject, boolean applyPhysics) {
BlockBreakEvent canBreak = new BlockBreakEvent(target,subject);
server.getPluginManager().callEvent(canBreak);
if(!canBreak.isCancelled())
target.setTypeId(0, applyPhysics);
return !canBreak.isCancelled();
}
在 ToolBelt 的当前版本 (0.1) 中,这非常有效,并且如果玩家没有服务器区域 (WorldGuard) 的权限,则不会删除块。我只是将目标Block设置为event.getClickedBlock()
,将主体Player设置为event.getPlayer()
,并将物理布尔值设置为,true
如果他们左键单击,并且false
如果他们右键单击。这是 0.1 版中的调用:
呼叫 1
safeBreak(target,event.getPlayer(),physics);
我目前正在开发一个支持远程块删除的新版本。根据用户是否蹲下并具有范围权限节点,目标块由event.getClickedBlock()
或设置subject.getTargetBlock(null,25)
。除了在一种情况下,这很好用。
- 案例 1:如果他们实际点击了块(LEFT_CLICK_BLOCK / RIGHT_CLICK_BLOCK),他们会看到变化,无论是物理设置。
- 案例 2:如果他们在一定范围内获得了方块,并且有物理到
true
(LEFT_CLICK_AIR),那么他们会看到变化。 - 案例 3:但是,如果他们在一定范围内获取了块,并且物理到
false
(RIGHT_CLICK_AIR),那么他们在客户端上看不到块更新。如果他们注销并重新登录,则更改是可见的,因此更改确实发生在服务器端。
为了缓解这个问题,我尝试使用subject.sendBlockChange()
,它应该向用户发送虚假的块更改,而不是以任何方式修改服务器。有了这个,用户总能看到他们做了什么。因此新代码:
呼叫 2
if(safeBreak(target,event.getPlayer(),physics))
subject.sendBlockChange(target.getLocation(), 0, (byte)0);
但是,由于某种原因,只是运行safeBreak()
和运行之间的变化if(safeBreak()) sendBlockChange()
使得区域保护(WorldGuard)不受尊重。用户从 WorldGuard 获得打印输出,仍然警告他们没有构建权限,但模块仍然被破坏。
以防万一描述.sendBlockChange()
不准确,它实际上并没有以任何方式改变世界,我还尝试了“无害”打印信息
调用 2.1
if(safeBreak(target,event.getPlayer(),physics))
subject.sendMessage("Error still present?");
当我运行 Call 2.1 时,如果我在一个我有权破坏块的区域中,我会收到“错误仍然存在?” 消息,但是当我在禁区内时,我不会。这意味着safeBreak()
如果我是否应该破坏一个块,它会返回正确的值,但不知何故仍在破坏块。另外,因为它没有.sendBlockChange()
呼叫 2,所以我再次遇到案例 3 问题。
呼叫 3
if(safeBreak(target,event.getPlayer(),physics)) {}
现在代码除了检查 的返回值之外没有任何动作safeBreak()
,但它仍然会破坏限制区域中的块。我切换到Call 1,它并没有打破禁区内的障碍。
这让我回到了最初的问题。我可以重新编译 ToolBelt.jar,唯一的区别是 Call 1 与 Call 2 并可靠地重现问题。如果调用 1 到位,则尊重区域支持,但案例 3 不会更新客户端。如果调用 2 到位,客户端始终会获得块更新,但他们可以删除任何区域中的块。
我可以通过不允许案例 3(范围内没有物理移除)并使用调用 1 来解决这个问题,但是我不想损害工具的能力。有谁知道为什么 Call 2 和 Call 2.1 在 Call 1 没有问题的情况下不适用于区域?
感兴趣的文档链接:
块:代表一个块。这是一个活的物体,对于世界上的任何给定位置,可能只存在一个方块。
Player : 代表一个玩家,连接与否
.isCanceled():获取此事件的取消状态。取消的事件不会在服务器中执行,但仍会传递给其他插件
.sendBlockChange():发送块更改。这会为某个位置的用户伪造一个块更改数据包。这实际上不会以任何方式改变世界。