Как проверка возврата функции java может изменить действие, которое выполняет функция?

Почему функция 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();
}

В текущей версии (0.1) ToolBelt это работает довольно хорошо и не удаляет блокировку, если у игрока нет разрешения для этого региона сервера (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?");

Когда я запускаю вызов 2.1, если я нахожусь в регионе, где у меня есть разрешение на разбиение блоков, я получаю сообщение «Ошибка все еще присутствует?» сообщение, но когда я в зоне ограниченного доступа, я не делаю. Это означает, что safeBreak() возвращает правильное значение, если я должен был разбить блок или нет, но каким-то образом все еще разбивает блок. Кроме того, поскольку у него нет .sendBlockChange() вызова 2, я снова получаю проблему с случаем 3.

Позвоните 3

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

Теперь код не выполняет никаких действий, кроме проверки возвращаемого значения safeBreak(), и тем не менее он по-прежнему разбивает блоки в ограниченной области. Я переключился на Звонок 1, и он не сломал блоки в запретной зоне.

Это возвращает меня к первоначальному вопросу. Я могу перекомпилировать ToolBelt.jar с той лишь разницей, что вызов 1 и вызов 2 и надежно воспроизвести проблему. Если вызов 1 на месте, поддержка региона соблюдается, но случай 3 не обновляет клиент. Если действует вызов 2, клиент всегда получает обновление блока, но может снимать блоки в любом регионе.

Я могу обойти проблему, не разрешая случай 3 (без удаления физики на расстоянии) и используя вызов 1, однако я не хотел бы ставить под угрозу возможности инструментов. Кто-нибудь знает, почему Call 2 и Call 2.1 не работают с регионами, когда Call 1 работает без проблем?

Интересующие ссылки на документацию:

  • Block : представляет блок. Это живой объект, и для любого заданного места в мире может существовать только один Блок.

  • Player: представляет игрока, подключенного или нет.

  • .isCanceled() : Получает состояние отмены этого события. Отмененное событие не будет выполнено на сервере, но все равно будет передано другим плагинам.

  • .sendBlockChange() : отправить изменение блока. Это подделывает пакет изменения блока для пользователя в определенном месте. На самом деле это никак не изменит мир.


person Peter Olson    schedule 31.03.2012    source источник
comment
Можете ли вы немного сузить его? sscce.org   -  person Caffeinated    schedule 01.04.2012
comment
Если я вызову safeBreak, не глядя на возвращаемое значение (safebreak();), то он не разобьет блок (setType = 0), когда плагин региона остановит событие (.isCanceled() == true). Если я вызову setBreak в операторе if(), то даже если плагин региона остановит событие, блок все равно будет сломан.   -  person Peter Olson    schedule 01.04.2012


Ответы (2)


Проблема была на уровне выше, чем я написал. Извините за недостаточное количество данных. Первоначальный вызов Case 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);
}
person Peter Olson    schedule 01.04.2012

Проверка l-value не влияет на код, сгенерировавший l-value . Это означает, что код в разделе «тогда» вашего оператора if, вероятно, является виновником.

В зависимости от того, как спроектирован сторонний код, с которым вы интегрируетесь, такие вещи, как исключения, могут вызывать поведение, подобное откату.

попробуйте использовать простой оператор в своем блоке if (а не «безвредное сообщение печати»), и вы сможете проверить это самостоятельно:

if(safeBreak(target,event.getPlayer(),physics))
{
}
person codelark    schedule 31.03.2012
comment
Я проверил это сейчас с новым вызовом 3 [if(safeBreak(target,event.getPlayer(),physics)) {}] Как и в случае с вызовом 2/2.1, он сломал блокировку в запретной зоне. Я переключил код обратно на Call 1, и он не сломал блоки в зоне ограниченного доступа. - person Peter Olson; 01.04.2012
comment
Слишком много движущихся частей, чтобы мы могли диагностировать вашу конкретную проблему. Я просто заявлял, что нет никакой разницы между foo() и if(foo()) {} (при условии, что foo возвращает логическое значение) - person codelark; 01.04.2012
comment
Я согласен, что слишком много движущихся частей. Однако safeBreak(); и if(safeBreak()) {} ведут себя по-разному, и в этом суть моей проблемы. У меня нет изменений в моем коде, кроме Call 1 и Call 3 - person Peter Olson; 01.04.2012