【发布时间】:2015-08-17 19:46:53
【问题描述】:
为什么函数 safeBreak() 在调用 1 中按预期执行,但在调用 2 或调用 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() 正在返回正确的值,以判断我是否应该破坏块,但不知何故仍在破坏块。另外,因为它没有呼叫 2 的 .sendBlockChange(),所以我再次遇到案例 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 没有问题的情况下不适用于区域?
感兴趣的文档链接:
Block :表示一个块。这是一个活的物体,对于世界上的任何给定位置,只能存在一个方块。
Player : 代表玩家,连接与否
.isCanceled() :获取此事件的取消状态。取消的事件不会在服务器中执行,但仍会传递给其他插件
.sendBlockChange() :发送块更改。这会为某个位置的用户伪造一个块更改数据包。这实际上不会以任何方式改变世界。
【问题讨论】:
-
你能把范围缩小一点吗? sscce.org
-
如果我在不查看返回值的情况下调用 safeBreak (safebreak();),那么当区域插件停止事件 (.isCanceled() == 真)。如果我在 if() 语句中调用 setBreak,那么即使区域插件停止事件,该块仍然被破坏。