0

为什么函数 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():发送块更改。这会为某个位置的用户伪造一个块更改数据包。这实际上不会以任何方式改变世界。

4

2 回答 2

3

这个问题比我发布的要高。很抱歉没有包含足够的数据。最初的案例 1 调用是:

if(isUseEvent())
    safeBreak(target,event.getPlayer(),physics);
else {
    target.setTypeId(0,physics);
    subject.sendBlockChange(target.getLocation(), 0, (byte)0);
}

案例 2 电话是

if(isUseEvent())
    if(safeBreak(target,event.getPlayer(),physics))
        subject.sendBlockChange(target.getLocation(), 0, (byte)0);
else {
    target.setTypeId(0,physics);
    subject.sendBlockChange(target.getLocation(), 0, (byte)0);
}

但是,因为我没有将if(safeBreak()) doStuff;in {} 包装起来,这意味着 else 与最里面的if()语句对齐。因此,正确的格式将显示:

if(isUseEvent())
    if(safeBreak(target,event.getPlayer(),physics))
        subject.sendBlockChange(target.getLocation(), 0, (byte)0);
    else {
        target.setTypeId(0,physics);
        subject.sendBlockChange(target.getLocation(), 0, (byte)0);
    }

这可以通过不采取愚蠢的捷径并正确包装我的if() {doStuff;}

if(isUseEvent()) {
    if(safeBreak(target,event.getPlayer(),physics)) {
        subject.sendBlockChange(target.getLocation(), 0, (byte)0);
    }
}else {
    target.setTypeId(0,physics);
    subject.sendBlockChange(target.getLocation(), 0, (byte)0);
}
于 2012-04-01T00:13:19.140 回答
0

检查左对生成左值的代码没有影响。这意味着 if 语句的“then”部分中的代码可能是罪魁祸首。

根据您集成的第三方代码的设计方式,异常之类的事情可能会导致类似回滚的行为。

尝试在您的 if 块中使用一个简单的语句(而不是“无害的打印消息”),您应该能够自己验证这一点:

if(safeBreak(target,event.getPlayer(),physics))
{
}
于 2012-03-31T23:35:21.660 回答