Почему функция 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() : отправить изменение блока. Это подделывает пакет изменения блока для пользователя в определенном месте. На самом деле это никак не изменит мир.