如何检查java函数的返回值是否会改变函数执行的操作?

时间:2012-03-31 22:15:14

标签: java bukkit

为什么函数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,如果它们左键单击,{ {1}}如果他们右键点击。这是0.1版本中的调用:

致电1

false

我目前正在开发支持范围块删除的新版本。根据用户是否正在蹲伏并具有范围权限节点,目标阻止由safeBreak(target,event.getPlayer(),physics); event.getClickedBlock()设置。除了一种情况外,这种方法很好。

  • 案例1 :如果他们实际点击了该块(LEFT_CLICK_BLOCK / RIGHT_CLICK_BLOCK),他们会看到更改,并使用物理设置。
  • 案例2 :如果他们获得了范围内的区块,并且物理到subject.getTargetBlock(null,25)(LEFT_CLICK_AIR),那么他们就会看到更改。
  • 案例3 :但是,如果他们获得了范围内的区块,并且物理到true(RIGHT_CLICK_AIR),那么他们会看到一个区块更新客户端。如果他们注销并重新开启,则更改是可见的,因此更改确实发生在服务器端。

为了缓解这个问题,我尝试使用false,它应该向用户发送虚假的块更改,而不是以任何方式修改服务器。有了这个,用户总能看到他们做了什么。因此新代码:

致电2

subject.sendBlockChange()

但是,出于某种原因,仅运行if(safeBreak(target,event.getPlayer(),physics)) subject.sendBlockChange(target.getLocation(), 0, (byte)0); 和运行safeBreak()之间的更改使得区域保护(WorldGuard)不受尊重。用户从WorldGuard获取打印输出仍然警告他们没有构建权,但是bock仍然被打破。

如果if(safeBreak()) sendBlockChange()的描述并不准确,它实际上并没有以任何方式改变世界,我也尝试过“无害”的打印消息

致电2.1

.sendBlockChange()

当我运行Call 2.1时,如果我在一个我有权打破块的区域,我会得到“错误仍然存​​在?”消息,但是当我在禁区时,我没有。这意味着if(safeBreak(target,event.getPlayer(),physics)) subject.sendMessage("Error still present?"); 返回正确的值,如果我应该打破一个块,但不知何故仍然打破了块。另外,因为它没有调用2的safeBreak(),所以我再次遇到案例3问题。

致电3

.sendBlockChange()

现在代码除了检查if(safeBreak(target,event.getPlayer(),physics)) {} 的返回值之外没有任何操作,但它仍然在限制区域中打破了块。我切换到了Call 1,它没有打破禁区内的区块。

这让我回到原来的问题。我可以重新编译ToolBelt.jar,唯一的区别是Call 1 vs Call 2并可靠地重现问题。如果调用1已就绪,则会遵循区域支持,但案例3不会更新客户端。如果调用2已到位,则客户端始终获取块更新,但是他们可以删除任何区域中的块。

我可以通过不允许案例3(在范围内没有物理移除)和使用调用1来解决问题,但是我不想牺牲工具的能力。当Call 1没有问题时,有没有人知道为什么Call 2和Call 2.1不能与区域一起工作?

感兴趣的文档链接:

  • Block:表示一个块。这是一个活动对象,世界上任何给定位置只能存在一个Block。

  • Player:表示已连接或未连接的播放器

  • .isCanceled():获取此事件的取消状态。取消的事件不会在服务器中执行,但仍会传递给其他插件

  • .sendBlockChange():发送一个块更改。这为某个位置的用户伪造了一个块更改包。这实际上不会以任何方式改变世界。

2 个答案:

答案 0 :(得分: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;包裹在{}中,所以它意味着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);
}

答案 1 :(得分:0)

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

根据您正在集成的第三方代码的设计方式,异常等内容可能会导致类似回滚的行为。

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

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