GUI
Обзор
GUI превращает одну запись TypeWriter, Open GUI, в полноценное меню-инвентарь. Вы выбираете тип контейнера (сундук, наковальня, окно торговли, написанная книга...), пишете заголовок в MiniMessage, а затем описываете содержимое как дерево макетов (layouts), хранящихся в layoutPool записи. Каждый макет размещает предметы: материал, название, описание (lore) и то, что происходит при клике игрока.
Всё редактируется в веб-панели: файлы меню вручную писать не нужно. Меню открывается как любое другое действие: из шага квеста, диалога NPC, события-команды или другого меню. Клики выполняют консольные команды, запускают другие записи и изменяют факты, так что меню — это ещё один способ управлять вашими страницами TypeWriter.
GUI также является основой для других меню-расширений BTC. Shops и Quest Codex не поставляют собственное окно: они размещают маркерные предметы в макете open_gui, а расширение заполняет их товарами или квестами.
Возможности
- Десять макетов —
simple,flex,paginated,scrollable,frame,composite,book,merchant,storageиleaderboard. - 24 типа контейнеров — сундуки от 9 до 54 слотов, а также наковальня, стол зачарований, кузнечный стол, камнерез, точило, ткацкий станок, картографический стол, торговец, книга, воронка, печи и другие.
- MiniMessage везде — цвета, градиенты и плейсхолдеры PlaceholderAPI в заголовках, названиях предметов и описаниях.
- Действия для каждого типа клика — левый, правый, с Shift, средний, двойной клик, цифровые клавиши, выброс и обмен с левой рукой, у каждого свои команды и триггеры.
- Права на слоты — скрывайте слот через
viewPermissionили оставляйте его видимым, но неактивным черезclickPermission. - Повторение слотов — заполняйте ряд или блок с помощью
count,direction,gapиrepeatY, не копируя предметы. - Представления и вкладки — одна оболочка меню, несколько экранов, заголовок с навигационной цепочкой и подсвечиваемые вкладки.
- Постоянное хранилище — слоты, сохраняющие предметы для каждого игрока или группы, с квотами, плейсхолдерами и необязательным выбросом при закрытии.
- Таблицы лидеров — ранжируйте игроков, миры и группы по одному или нескольким фактам, включая игроков не в сети, либо показывайте строки, которые даёт другое расширение через поле
source(например, статистику Dungeon). - Расширенный инвентарь — превратите собственный инвентарь игрока в четыре дополнительных ряда меню для холста из десяти рядов.
Установка
- Установите TypeWriter на сервер Paper или Folia. Должен присутствовать PacketEvents, который TypeWriter и так требует.
- Скачайте JAR-файл из релизов GitHub GUI (кнопка в сводной панели выше).
- Положите его в
plugins/Typewriter/extensions/и перезапустите сервер. - По желанию: установите CraftEngine, если в заголовках используются теги
<image>или<shift>для отрисовки пользовательских фонов.
Устанавливайте GUI раньше расширений, которые на него опираются, таких как Shops и Quest Codex.
Публичный релиз GUI не включает отдельный редактор меню и его веб-сервер: меню редактируются в веб-панели TypeWriter. В релиз входит только проверка при запуске, которая читает опубликованные записи open_gui и сообщает в консоли о неработающих меню (см. раздел «Устранение неполадок»).
Быстрый старт
Соберём небольшое меню из трёх кнопок:
- В веб-панели откройте страницу sequence и добавьте запись Open GUI. Назовите её
main_menu. - Установите
guiTypeвCUSTOM,sizeвSIZE_27, аtitleв<gold>Main menu. - В
layoutPoolдобавьте макет типаsimple, задайте емуidmain, затем впишитеmainвmainLayoutId. - В
itemsмакета добавьте предмет в позицииx4,y1. Выберите вitemDIAMOND, задайтеdisplayNameзначение<aqua>Rewardsи добавьте вloreстроку<gray>Click to claim. - В
interactionListдобавьте взаимодействие сtypeLEFT_CLICK. Добавьте вcommandsконсольную команду, напримерgive %player% diamond 1, или свяжите запись вtriggers. ВключитеcloseMenu, чтобы закрывать меню после клика. - Добавьте второй предмет
simpleсcount9,directionrightи серой цветной стеклянной панелью, чтобы нарисовать ряд рамки. Задайте егоdisplayNameодин пробел. - Свяжите запись Open GUI с тем, что её запускает, например с триггером варианта диалога или событием-командой, затем нажмите Publish.
Добейтесь работы одного макета simple в игре, прежде чем добавлять слои frame, scrollable или paginated. Каждый макет адресуется по своему id, поэтому опечатка в mainLayoutId — первое, что стоит проверить, если меню открывается пустым.
Как это работает
Когда действие срабатывает, расширение строит дерево макетов для этого игрока (условия и права вычисляются именно в этот момент), регистрирует сессию меню и показывает инвентарь. Каждый клик сопоставляется со слотом в этой позиции, после чего выполняется подходящее взаимодействие. Сессия запоминает предыдущие меню, поэтому gui:back возвращает в меню, из которого пришёл игрок.
Меню
Типы контейнеров
guiType выбирает окно. CUSTOM — это обычный инвентарь наподобие сундука, высоту которого вы выбираете через size. size применяется только к CUSTOM и по умолчанию равен шести рядам; остальные типы используют ванильный размер своего окна.
guiType | Окно |
|---|---|
CUSTOM, CHEST, ENDER_CHEST, BARREL, SHULKER_BOX | Сетки в стиле сундука. Для CUSTOM size может быть SIZE_9, SIZE_18, SIZE_27, SIZE_36, SIZE_45 или SIZE_54. |
HOPPER, DISPENSER, DROPPER | Небольшие контейнеры фиксированного размера. |
FURNACE, BLAST_FURNACE, SMOKER, BREWING_STAND, BEACON | Окна механизмов. |
CRAFTING_TABLE, ENCHANTING_TABLE, ANVIL, SMITHING_TABLE, GRINDSTONE, LOOM, CARTOGRAPHY_TABLE, STONECUTTER | Ванильные станции. Без макета они ведут себя как ванильный экран; GUI управляет только открытием и закрытием. |
MERCHANT, VILLAGER_TRADE | Окно торговли с жителем, заполняемое макетом merchant. |
BOOK | Написанная книга, заполняемая макетом book. |
Макеты
Каждый макет — это именованная запись layoutPool. Макет, указанный в mainLayoutId, является корневым; к остальным обращаются по ссылкам (дочерние элементы composite, innerId у scrollable, layoutId у frame).
| Макет | Для чего использовать | Ключевые поля |
|---|---|---|
simple | Фиксированные кнопки и декор в позициях x/y. | items |
flex | Предметы, автоматически раскладываемые по рядам. | justifyContent (START, CENTER, END, SPACE_BETWEEN), alignItems, wrap, virtualHeight |
paginated | Длинный список, разбитый на страницы. | itemsPerPage, slots, items, navigationButtons (NEXT, PREVIOUS, BACK, INDICATOR) |
scrollable | Содержимое выше или шире окна. | innerId, virtualWidth, virtualHeight, buttons (direction, step), showDefaultButtons, defaultStep |
frame | Несколько независимых прямоугольников, например вкладки, содержимое и нижняя часть. | frames (id, x, y, width, height, layoutId) |
composite | Наложение слоёв: фон, содержимое, кнопки. | children (id макетов, более поздние рисуются поверх) |
book | Страницы написанной книги. | pages (MiniMessage) |
merchant | Обмен с жителем с пользовательскими предметами. | trades (result, costOne, costTwo, maxUses, criteria) |
storage | Постоянные слоты на основе артефакта GUI Storage. | entry, groupKey, slots |
leaderboard | Строки рейтинга со страницами, по фактам или из source другого расширения. | leaderboard, x, y, width, height, previousButton, nextButton |
Макеты book и merchant работают только как главный макет, при этом guiType переопределяется на соответствующее окно. Макет scrollable получает стрелки по умолчанию, пока включён showDefaultButtons (по умолчанию): по одной для каждого направления без записи в buttons, см. Макеты, фреймы и слоты.
Подробный разбор каждого макета смотрите в разделе Макеты, фреймы и слоты.
Предметы и слоты
Каждый предмет макета — это описание слота с одинаковым набором полей:
| Поле | Роль |
|---|---|
item | Отображаемый предмет. Если оставить пустым, слот показывает предмет, который игрок держит в основной руке. |
displayName, lore | Текст MiniMessage с поддержкой PlaceholderAPI. По умолчанию названия не курсивные; добавьте <italic>, если нужен курсив. Пустой displayName сохраняет название предмета вместо замены его пустым. |
x, y | Столбец от 0 до 8 и ряд, где 0 — самый верхний. |
rollPerSlot | Для предмета, повторяемого в нескольких слотах, у которого item случайный: выключено (по умолчанию) — выбор делается один раз, и все слоты показывают этот результат; включено — каждый слот выбирает сам при открытии меню. Результат сохраняется, пока меню открыто, если не включён liveItem. |
liveItem | Выключено (по умолчанию): предмет выбирается при открытии меню и сохраняется на всю сессию; при обновлении меняются только название и описание. Включено: предмет вычисляется заново при каждом обновлении, поэтому случайный предмет выбирается каждый раз заново. При включённом rollPerSlot каждый повторяемый слот выбирает сам при каждом обновлении; иначе все повторяемые слоты делят один выбор на обновление. Пустое название по-прежнему игнорируется. |
count, direction, gap, repeatY | Повторение слотов, см. ниже. |
criteria | Слот не отображается для игроков, которые не соответствуют условию. |
viewPermission | Право, необходимое, чтобы видеть слот. |
clickPermission | Право, необходимое для использования слота; без него слот отображается, но неактивен. |
allowPickup, isGhost | Можно ли забрать предмет из меню. Оба выключены по умолчанию; ghost-предметы можно кликать, но нельзя перемещать или копировать. |
cooldownTicks | Число тиков до следующего клика по тому же слоту. |
interactionList, triggers, modifiers | Что делает клик. |
input | Открывает диалог ввода текста при клике. |
storageId | Превращает слот в постоянный слот хранилища из storagePool. |
buttonType, buttonPrefix | Помечает слот как кнопку, предоставляемую другим расширением. |
animation | Перемещает слот в целевую позицию за заданное число миллисекунд при открытии меню. |
Повторение слотов. count повторяет предмет вдоль direction (right, left, down, up). gap — это шаг, а не отступ: 1 означает вплотную, 2 оставляет один пустой слот между копиями, а 0 ведёт себя как 1. repeatY повторяет всю линию по перпендикулярной оси. Без direction поля count, gap и repeatY игнорируются и размещается один слот. Эти поля считают слоты, а не размер стопки предмета.
Перекрытия. Когда два слота занимают одну ячейку, тот, что что-то делает (действие по клику, тег, слот хранилища), побеждает чисто декоративный независимо от порядка. Между двумя слотами одного вида побеждает объявленный последним. Предупреждение с указанием ячейки записывается в лог один раз.
Действия по клику
interactionList сопоставляет тип клика с командами и триггерами. Доступные типы: LEFT_CLICK, RIGHT_CLICK, SHIFT_LEFT_CLICK, SHIFT_RIGHT_CLICK, MIDDLE_CLICK, DOUBLE_CLICK, от NUMBER_KEY_1 до NUMBER_KEY_9, DROP, DROP_ALL и SWAP_OFFHAND.
commandsвыполняются от имени консоли.%player%заменяется именем игрока, а плейсхолдеры PlaceholderAPI разрешаются первыми.triggersзапускают записи TypeWriter для игрока. Собственныеtriggersиmodifiersслота срабатывают при каждом левом или правом клике либо при совпадении конкретного взаимодействия.closeMenuзакрывает окно после действия.executeReturnвозвращает к предыдущему меню.
Команды, начинающиеся с gui:, обрабатывает сам GUI:
| Команда | Эффект |
|---|---|
gui:close | Закрывает меню. |
gui:back | Заново открывает предыдущее меню сессии. |
gui:open <entry_id> или gui:action <entry_id> | Запускает другую запись, обычно другой Open GUI. |
gui:view <view_id> | Переключается на другое представление без закрытия инвентаря. |
gui:page <delta> [layout_id] | Сдвигает макет paginated или leaderboard на указанное число страниц. |
gui:scroll <dx> <dy> [layout_id] | Прокручивает макет scrollable. |
gui:refresh | Немедленно перерисовывает меню. |
gui:sound <sound_key> | Воспроизводит игроку звук. |
Открытие, возврат и закрытие
Open GUI, открытый из другого меню, помещается в историю сессии, поэтому gui:back или executeReturn возвращает к нему. Закрытие клавишей Escape завершает сессию. Открытие макета book сначала завершает текущую сессию.
Задайте audio, чтобы воспроизводить звуки при открытии, закрытии, прокрутке и клике. Задайте autoRefreshTicks, чтобы перерисовывать меню каждые N тиков, пока оно открыто, — именно благодаря этому плейсхолдеры в названиях и описаниях обновляются как счётчик или таймер.
Представления, вкладки и наследование
views превращают один open_gui в несколько экранов с общей оболочкой. Фрейм (frame), у которого layoutId равен @view, заполняется макетом активного представления: сначала явным соответствием frames представления, затем макетом с именем <view id>_<frame id>, затем теми же поисками по parentId представления. Пометьте предмет-вкладку buttonPrefix view: и задайте buttonType равным id представления — по клику он переключит представление и будет подсвечен, пока оно активно. Заголовок представления допускает {breadcrumb}, {view} и {root}.
baseMenuId указывает на другой Open GUI и объединяет его layoutPool, views и storagePool с вашими: макет с тем же id заменяет унаследованный. Подробности — в разделе Представления, вкладки и наследование.
Слоты хранилища
Артефакт GUI Storage хранит предметы для каждого игрока или группы. Объявите повторно используемые конфигурации в storagePool записи, затем задайте предмету storageId или используйте макет storage. В названиях и описаниях предметов слота хранилища допускаются {stored_name}, {stored_amount} и {stored_max}. {score_raw} и {value_<key>} также доступны для строк таблицы лидеров с source.
Привязки кликов по умолчанию, которые можно изменить в GUI Global Settings:
| Действие | Клик по умолчанию |
|---|---|
| Положить один предмет с курсора | Левый клик |
| Положить всю стопку с курсора | Shift + левый клик |
| Взять один предмет | Правый клик |
| Забрать всё | Левый клик |
| Взять стопку | Shift + правый клик |
| Заполнить из инвентаря | Обмен с левой рукой (F) |
| Выбросить всё на землю | Выброс (Q) |
dropOnClose выбрасывает то, что ещё хранится, под ноги игроку, когда меню по-настоящему закрывается: по Escape или при отключении, но не когда игрок переходит в другое меню.
Расширенный инвентарь
extendToPlayerInventory показывает собственные 36 слотов игрока как четыре дополнительных ряда, давая холст из десяти рядов (y от 0 до 9). Это работает только на стороне клиента: настоящий инвентарь никогда не изменяется и восстанавливается при закрытии меню, а предметы нельзя забрать из нижних рядов. Требуется guiType CUSTOM с size SIZE_54. Корневой макет, у которого id заканчивается на _extended, включает эту возможность сам.
Конфигурация
У GUI нет файла конфигурации. Его настраивают две необязательные записи manifest:
- GUI Extension Configuration включает логирование
debugи параметрhideVanillaItemStats(включён по умолчанию), который скрывает строки урона атаки, прочности и зачарований на кнопках меню. - GUI Global Settings задаёт привязки кликов хранилища и предметы-соединители дерева навыков.
Плейсхолдеры
| Плейсхолдер | Возвращает |
|---|---|
%typewriter_gui_storage_amount_<entry_id>_<slot_index>% | Число предметов, хранящихся у игрока в записи GUI Storage, 0, если пусто. |
%typewriter_gui_storage_has_<entry_id>_<slot_index>% | true, если в слоте есть предмет, иначе false. |
<slot_index> — это y * 9 + x слота хранилища. Эти плейсхолдеры всегда читают собственное хранилище игрока, а не общее групповое.
Внутри меню расширение заменяет следующие токены: {stored_name}, {stored_amount}, {stored_max} (слоты хранилища), {page}, {next_page}, {prev_page}, {total_pages} и {page_indicator} (кнопка INDICATOR макета paginated), а также {rank}, {name}, {score}, {group}, {world} и {score_<fact_id>} (строки таблицы лидеров).
Записи
Записей: 6, сгенерированы из исходного кода расширения.
Действия1
- Open GUIOpen a GUI for the player
Артефакты2
- GUI Leaderboard PopulationOffline population data for GUI leaderboards
- GUI StoragePersistent storage artifact for GUI storage slots
Манифесты3
- GUI ConfigGUI Extension Configuration
- GUI LeaderboardGUI leaderboard backed by Typewriter facts
- GUI SettingsGUI Global Settings
Устранение неполадок
Меню открывается пустым. mainLayoutId пуст или не совпадает ни с одним id макета в layoutPool (id чувствительны к регистру). При использовании baseMenuId пустой mainLayoutId берётся у родителя. Проверка при запуске сообщает в консоли о неразрешённом главном макете.
Слот отсутствует, а консоль пишет «falls outside the grid». Предмет оказался при отрицательных x или y либо за столбцом 8. Обычно это следствие слишком большого count, который считает слоты. В логе указаны макет и отклонённые координаты.
Консоль пишет «Slot overlap ... gap». Два слота занимают одну ячейку. gap — это шаг: 1 размещает слоты вплотную, а 2 оставляет один пустой слот между ними, поэтому авторы, которые читают gap: 1 как «один промежуток», получают копии, накладывающиеся на соседей друг друга. Измените gap или переместите один из слотов.
Слот виден администраторам, но не игрокам. В viewPermission указано право, которое есть только у операторов. Пустые значения игнорируются, но опечатка — это реальный узел, доступный только операторам. То же правило действует для viewPermission представления.
Кнопка отображается, но ничего не делает. Проверьте clickPermission, затем cooldownTicks, затем criteria. Если у слота есть buttonType, его buttonPrefix должен совпадать с расширением, которое его предоставляет; незанятый тег сохраняет ваш interactionList, но больше ничего. Команды выполняются от имени консоли, поэтому команда, которой нужен отправитель-игрок, молча завершается ошибкой.
Прокручиваемое содержимое не двигается. Проверьте, что содержимое больше окна (virtualWidth и virtualHeight): стрелка показывается, только пока в её направлении есть что прокручивать. При отключённом showDefaultButtons и без buttons стрелок нет вообще. Стрелка по умолчанию также уступает место, если её ячейка уже занята предметом содержимого, кнопкой или декорацией: в консоль ничего не пишется, поэтому переместите её через scrollArrows в GUI Settings или добавьте собственные buttons с direction и step вне прокручиваемой области.
Таблица лидеров показывает только игроков в сети. Привяжите артефакт GUI Leaderboard Population в поле population таблицы лидеров, чтобы сохранять последние известные значения игроков не в сети.
Расширенное меню не показывает десять рядов. extendToPlayerInventory требует guiType CUSTOM и size SIZE_54.
Связанные расширения
- Shops — меню магазинов на основе макетов GUI и маркера
shop_button:. - Quest Codex — меню журнала квестов, построенное на том же шасси
open_gui.