【问题标题】:How can checking the return of a java function change the action the function performs?检查 java 函数的返回如何改变函数执行的操作?
【发布时间】: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,那么即使区域插件停止事件,该块仍然被破坏。

标签: java bukkit


【解决方案1】:

问题的级别比我发布的要高。很抱歉没有包含足够的数据。最初的案例 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; 包装在 {} 中,这意味着 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);
}

【讨论】:

    【解决方案2】:

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

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

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

    if(safeBreak(target,event.getPlayer(),physics))
    {
    }
    

    【讨论】:

    • 我现在已经用新的 Call 3 [if(safeBreak(target,event.getPlayer(),physics)) {}] 进行了测试,与 Call 2/2.1 一样,它打破了管制区。我将代码切换回 Call 1,它并没有破坏禁区内的方块。
    • 移动部件太多,我们无法诊断您的具体问题。我只是说foo()if(foo()) {} 之间没有区别(假设 foo 返回一个布尔值)
    • 我同意活动部件太多。但是,safeBreak();if(safeBreak()) {} 的行为不同,这是我的问题的症结所在。除了 Call 1 和 Call 3 之外,我的代码没有任何更改
    猜你喜欢
    • 2013-12-23
    • 1970-01-01
    • 2014-03-23
    • 2020-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-26
    相关资源
    最近更新 更多