Перейти к содержимому

Логические структуры

Автор меню

Все логические структуры в AbstractMenus устроены как if -> then -> else и могут вкладываться друг в друга.

Блок действий - это сложный объект, в котором лежат правила и другие действия. Реальная структура:

ПараметрТипНазначение
rulesБлок правилОбычные правила
actionsБлок действийВыполняются, если игрок прошёл все правила
denyActionsБлок действийВыполняются, если игрок не прошёл хотя бы одно правило

Блок легко представить как бесконечное дерево, где каждая ветка - правило или блок действий.

Структура блока действий

Пример блока действий посложнее:

title: "Пример"
size: 6
openActions {
message: "Открываем меню..."
rules {
permission: "super.admin"
}
actions {
message: "У тебя есть право super.admin!"
rules {
money: 1000
}
actions {
takeMoney: 1000
}
denyActions {
giveMoney: 1000
}
}
}

Что произойдёт при открытии меню, по порядку:

  1. В чат прилетает “Открываем меню…”.
  2. Проверяется право “super.admin”.
  3. Если право есть - выполняется вложенный блок и отправляется “У тебя есть право super.admin!”.
  4. Проверяется, есть ли на балансе 1000 валюты.
  5. Если есть - списывается.
  6. Если нет - выдаётся 1000.

Внутри блока правил можно прописать локальные действия. Удобно, когда снаружи actions и denyActions использовать нельзя. Пример:

items: [
{
slot: 0
material: STONE
rules {
permission: "am.admin"
actions {
message: "Да!"
}
denyActions {
message: "Нет"
}
}
}
]

Логика стандартная: actions срабатывают, когда игрок прошёл все правила блока rules. denyActions - когда не прошёл хотя бы одно правило текущего блока.

В примере выше “Нет” прилетит игроку без права am.admin. Блок actions, если бы был, отработал бы наоборот - когда игрок правилам соответствует.

На самом деле любой rules - это список объектов, где каждый объект - блок правил. Раньше мы просто открывали rules и писали внутри:

rules {
permission: "super.admin"
group: "vip"
}

Раз блок правил - это список других блоков, можно прописать несколько похожих правил и в каждый добавить свои локальные действия.

click {
message: "Ты кликнул по камешку"
rules: [
{
permission: "my.perm"
actions {
message: "У тебя есть право"
}
},
{
money: 500
actions {
message: "У тебя достаточно денег"
}
denyActions {
message: "У тебя недостаточно денег. Но возьми."
giveMoney: 500
}
}
]
actions {
message: "У тебя достаточно денег и нужное право!"
}
}

Здесь блок правил используется как список. По шагам, что происходит при клике:

  1. Сообщение “Ты кликнул по камешку”.
  2. Проверяется право “my.perm”.
  3. Если право есть - “У тебя есть право”.
  4. Проверяется баланс на 500 валюты.
  5. Если деньги есть - “У тебя достаточно денег”.
  6. Если нет - “У тебя недостаточно денег”, плюс игроку выдаётся 500 монет.
  7. Если обе проверки прошли - “У тебя достаточно денег и нужное право!”.

Любое правило можно инвертировать. Поставь перед его именем - - это “NOT”. Если правило возвращало true, теперь вернёт false, и наоборот.

Пример:

rules {
-permission: "group.admin"
}
actions {
message: "Ты не Админ :("
}
denyActions {
message: "Ты Админ!"
}

Здесь actions сработает, если у игрока нет права group.admin - результат правила permission инвертирован.

Эту запись можно навешивать на любое правило, в том числе на логические обёртки. О них дальше.

По умолчанию блок rules склеивает правила через “AND” - даже когда задан списком. Логические обёртки нужны для условий посложнее: можно комбинировать and, or и оператор “NOT”. Под капотом обёртка - это просто правило, внутри которого живут другие правила. Поэтому пишутся обёртки внутри rules.

rules и так работает по “AND”, поэтому отдельная обёртка and нужна в основном внутри or. Тем не менее, пример с её прямым использованием:

rules {
and {
permission: "group.vip"
gamemode: CREATIVE
}
}

Правило and вернёт true, если у игрока есть право group.vip И режим игры CREATIVE.

Ещё пример - обёртка как список групп правил. Формат похож на описанный выше.

rules {
and: [
{
permission: "group.vip"
gamemode: CREATIVE
},
{
permission: "group.helper"
}
]
}

Здесь and вернёт true, если у игрока есть group.vip И режим CREATIVE И право group.helper.

Возвращает true, если хотя бы одно из вложенных правил вернуло true.

rules {
or {
permission: "group.vip"
gamemode: CREATIVE
}
}

Здесь or вернёт true, если у игрока есть group.vip ИЛИ режим CREATIVE.

С or в виде списка групп правил поведение такое же. Пример:

rules {
or: [
{
permission: "group.vip"
gamemode: CREATIVE
},
{
permission: "group.helper"
}
]
}

Здесь or вернёт true, если у игрока есть group.vip ИЛИ режим CREATIVE ИЛИ право group.helper.

oneof пригодится, когда правила задаются списком и у каждого свои локальные действия. По логике это and, но с одной важной разницей. Если взять обычный and как список:

rules {
and: [
{
permission: "perm1"
actions {
message: "У тебя есть perm1"
}
},
{
permission: "perm2"
actions {
message: "У тебя есть perm2"
}
}
]
}

то даже если у игрока есть право из первого блока (perm1), его локальные действия выполнятся, но дальше всё равно прогоняются остальные правила, и игрок получит лишние сообщения. И в итоге сам and всё равно вернёт false, если хоть одно правило не прошло.

Иногда это не то, что нужно, - тут и спасает oneof. То же самое через oneof:

rules {
oneof: [
{
permission: "perm1"
actions {
message: "У тебя есть perm1"
}
},
{
permission: "perm2"
actions {
message: "У тебя есть perm2"
}
}
]
}

то если у игрока есть perm1, локальные действия тоже выполнятся, но oneof сразу остановится и вернёт true. Если ни одно из правил не подошло - вернёт false.

С oneof можно спокойно навешивать локальные действия и быть уверенным, что выполнятся ровно те, что относятся к сработавшему блоку правил. Пример:

rules {
oneof: [
{
permission: "perm1"
actions {
message: "У тебя есть perm1"
}
},
{
permission: "perm2"
actions {
message: "У тебя есть perm2"
}
}
]
denyActions { // Сработает, только если ни одно правило в списке не прошло
message: "Ни одно из требуемых правил не выполнено"
}
}

Прогоняет вложенные правила в контексте другого игрока. Удобно, когда у тебя есть плейсхолдер, который разворачивается в имя другого игрока (например, цель, выбранная через активатор command), и проверять правила нужно по нему, а не по зрителю меню.

rules {
playerScope {
name: "%activator_cmd_arg_target%"
rules {
permission: "myserver.vip"
gamemode: SURVIVAL
}
}
}
  • name - игрок, в чей контекст переключаемся. Плейсхолдеры раскрываются.
  • rules - блок правил, который проверяется против этого игрока. Структура - как у обычного rules.

Если такой игрок не онлайн, обёртка просто вернёт false, без исключения.

Обёртки можно вкладывать друг в друга и собирать любые условия. Пример:

rules {
or: [
{
and {
permission: "vip"
gamemode: CREATIVE
}
},
{
and {
permission: "premium"
gamemode: SURVIVAL
}
}
]
}

Здесь or вернёт true, если:

у игрока есть vip AND режим CREATIVE

ИЛИ

у игрока есть premium AND режим SURVIVAL.

Использование одинаковых действий и правил в одном блоке

Заголовок раздела «Использование одинаковых действий и правил в одном блоке»

Как и многие другие форматы, HOCON не разрешает повторять один и тот же ключ в одном блоке. Например:

click {
message: "Привет" // Ок
message: "Привет снова" // Ошибка парсинга
}

упадёт парсер - в одном блоке не может быть нескольких параметров с одним именем. Иногда это мешает: например, когда нужно несколько одинаковых действий подряд. В AbstractMenus есть обёртка bulk, но читается она так себе. Есть способ проще.

Чтобы засунуть несколько одинаковых действий или правил в один блок, добавь к имени параметра префикс _. Например:

click {
message: "Привет"
_message: "Привет снова"
__message: "Привет снова и снова"
___message: "Привет снова и снова и снова"
}

Парсер на это не ругается - имена у параметров разные. После парсинга, но до десериализации действий и правил, плагин обрезает префиксные _, и имена правил и действий снова становятся корректными.

Через тот же приём можно класть несколько правил в один логический блок. Пример:

rules {
or {
gamemode: SURVIVAL
_gamemode: ADVENTURE
__gamemode: SPECTATOR
}
}

Обёртка or вернёт true, если хотя бы одно из правил подходит игроку.

Эта запись с префиксами эквивалентна такой:

rules {
or: [
{ gamemode: SURVIVAL },
{ gamemode: ADVENTURE },
{ gamemode: SPECTATOR }
]
}

То есть, используя префикс _ для правил, мы избегаем шаблонного кода.