Как проверка возврата функции Java может изменить действие, которое выполняет функция? - PullRequest
0 голосов
/ 01 апреля 2012

Почему функция 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). Я просто устанавливаю цель Блок на event.getClickedBlock(), субъект Игрок на event.getPlayer(), а логическое значение физики на true, если они щелкнули левой кнопкой мыши, и false если они щелкнули правой кнопкой мыши. Вот звонок в версии 0.1:

Звоните 1

safeBreak(target,event.getPlayer(),physics);

В настоящее время я работаю над новой версией, которая поддерживает удаление удаленных блоков. В зависимости от того, находится ли пользователь на корточках и имеет ли он узел разрешения диапазона, целевой блок устанавливается на event.getClickedBlock() или subject.getTargetBlock(null,25). Это прекрасно работает за исключением одного случая.

  • case 1 : Если они фактически щелкнули по блоку (LEFT_CLICK_BLOCK / RIGHT_CLICK_BLOCK), они увидят изменение с любыми настройками физики.
  • case 2 : Если они получили блок на расстоянии и имеют физику true (LEFT_CLICK_AIR), то они видят изменение.
  • case 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() из Call 2, я снова получаю проблему случая 3.

Звоните 3

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

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

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

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

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

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

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

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

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

Ответы [ 2 ]

3 голосов
/ 01 апреля 2012

Проблема была на уровне выше, чем я написал.Извините за недостаточное количество данных.Исходный вызов Case 1 был:

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

Вызов Case 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; в {}, это означало, чтоостальное выровнялось с самым внутренним оператором 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);
}
0 голосов
/ 01 апреля 2012

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

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

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

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