GitHub Discord
⚡ SmartMenus v2.4

Welcome to Smart Menus

Smart Menus is an advanced, fully configuration-driven inventory GUI plugin for Minecraft (Paper/Spigot). Build complex shop menus, navigation hubs, interactive GUIs, and live data displays — all from YAML files, zero coding required.

What is Smart Menus?

Smart Menus lets server administrators define custom inventory GUIs entirely through YAML configuration files. Each GUI lives in its own subfolder under plugins/SmartMenus/guis/, making it easy to organise, share, and reload individual menus without restarting the server.

Whether you need a simple item shop, a multi-page quest board, a dynamic player list, or a complex condition-gated upgrade menu — Smart Menus handles it.

Key Features

🎛️

15+ Button Types

NONE, INVENTORY, BACK, NEXT, PREVIOUS, JUMP, HOME, MAIN_MENU, PAGINATION, DYNAMIC_PAGINATION, SWITCH, INPUT and more.

⚡

Full Action System

Messages, titles, action bars, sounds, commands, teleport, BungeeCord server connect, and JavaScript script actions.

✅

Advanced Conditions

Economy, XP, permissions, placeholders, math expressions, script conditions, world/region checks, and item requirements.

🔄

Live Dynamic Data

Paginate over online players, loaded worlds, your inventory, ender chest, or LuckPerms permission groups — live, every tick.

🔣

Expression Engine

Write math conditions like %vault_eco_balance% >= 1000 or %player_level% * 2 > 30 directly in YAML.

🧩

Patterns

Define reusable item templates in patterns/ and apply them across multiple GUIs with variable substitution.

📦

Any Inventory Type

Not just chests — open GUIs as HOPPER, DISPENSER, DROPPER, FURNACE, BLAST_FURNACE, SMOKER and more.

🔌

Rich Integrations

Vault, PlaceholderAPI, LuckPerms, WorldGuard, ItemsAdder, Nexo, OreoEssentials, ModeledNPCs.

How It Works

Each GUI is a .yml file inside a subfolder under plugins/SmartMenus/guis/. Smart Menus scans this folder recursively on startup and reload. A minimal GUI looks like this:

YAML
my_shop: title: "&6My Shop" rows: 3 commands: [ "myshop", "shop" ] items: diamond-item: slot: 13 material: DIAMOND name: "&b&lDiamond" lore: - "&7Click to buy for &a$100" conditions: - type: VAULT_MONEY amount: 100 take: true commands: - "give {player} diamond 1"

Supported Versions

MinecraftJavaStatus
1.21.x21+✅ Supported
1.20.x17+✅ Supported
1.19.x17+✅ Supported
1.18.x17⚠️ Untested
≤ 1.17—❌ Not supported

Next Steps

Installation

Smart Menus is a Paper/Spigot plugin. Installation takes under two minutes.

Requirements

RequirementVersionNotes
Server softwarePaper / SpigotPaper strongly recommended
Minecraft1.19 – 1.21
Java17+Java 21 recommended
PlaceholderAPIAny recentOptional but strongly recommended
VaultAnyRequired for economy conditions

Steps

  1. Download SmartMenus.jar from the releases page.
  2. Place it in your server's plugins/ folder.
  3. Start or restart the server. Smart Menus will generate its default config files.
  4. All default GUIs are extracted to plugins/SmartMenus/guis/ automatically.
  5. Use /smartmenus reload after editing any GUI file.
TipYou do not need to restart the server when editing GUIs. Run /smartmenus reload to hot-reload all files.

Folder Structure

Shell
plugins/SmartMenus/ ├── config.yml # Plugin settings ├── lang.yml # All user-facing messages └── guis/ ├── my_shop/ │ └── my_shop.yml ├── rank_upgrade/ │ └── rank_upgrade.yml └── feature_showcase/ ├── showcase_hub.yml └── showcase_pagination.yml
NoteMultiple GUIs can share a single .yml file — just use different top-level keys. Smart Menus loads all .yml files in guis/ recursively.

Quick Start

Create a working GUI in under 5 minutes.

Step 1 — Create the file

Create a new folder and file: plugins/SmartMenus/guis/welcome/welcome.yml

Step 2 — Write the GUI

welcome.yml
welcome_menu: title: "&8&l✦ Welcome &7| &f{player}" rows: 3 commands: [ "welcome", "hub" ] items: # Background filler border: slots: [ 0-8, 18-26 ] material: BLACK_STAINED_GLASS_PANE name: "&8" # Clickable button that runs a command play-btn: slot: 13 material: EMERALD name: "&a&lPlay Now" lore: - "&7Teleport to spawn." - "" - "&eClick!" commands: - "spawn" close-btn: slot: 22 material: BARRIER name: "&c&lClose" close: true

Step 3 — Reload & test

In-game
/smartmenus reload /welcome
That's it!The GUI opens with /welcome. Every player with the smartmenus.command.welcome_menu permission can use it.

Add conditions

Gate the button behind a Vault balance check:

YAML
buy-btn: slot: 13 material: DIAMOND name: "&b&lBuy Diamond — &a$50" conditions: - type: VAULT_MONEY amount: 50 take: true # deducts the money on click commands: - "give {player} diamond 1" else_item: material: COAL name: "&8Not enough money" lore: - "&7Need &a$50&7. You have &c%vault_eco_balance%"

GUI Structure

Every GUI is a top-level YAML key. Under it, you define metadata and an items: map.

Top-level Properties

KeyTypeDefaultDescription
titleStringrequiredInventory title. Supports color codes, {player}, and PlaceholderAPI.
rows1 – 66Number of rows (chest only). Ignored for other inventory_type values.
commandsList[]Commands that open this GUI. First entry is primary; rest are aliases.
inventory_typeStringCHESTContainer type: CHEST, HOPPER, DISPENSER, FURNACE, etc.
main_menuGUI ID—Target GUI for MAIN_MENU buttons.
npc_idInteger—ModeledNPCs NPC ID that opens this GUI on right-click.
fill:Item block—Item used to fill any empty slot after all other items are rendered.
items:MaprequiredNamed item definitions (see below).
use_bottom_inventoryBooleanfalseEnable 90-slot full-screen mode — uses the player's inventory area as interactive GUI slots.
bottom_inventory_modeStringDEFAULTDEFAULT — saves and restores the real inventory on open/close. PACKET_EVENT — sends GUI items as packets only; real inventory is never modified (requires PacketEvents).
bottom_items:Map—Items for the player inventory area (slots 0–35). Only used when use_bottom_inventory: true.
args:List—Typed command arguments passed when opening the GUI. Each arg becomes %arg_name%.
patterns:List—Reusable pattern IDs applied to all items before individual item config.
auto-convertBooleanfalseWhen true, Bedrock players automatically see a native Paper Dialog built from the GUI's items instead of the chest inventory. Java players are unaffected. See Auto-Convert.
bedrock:Block—Explicit Bedrock form definition. Overrides auto-convert when present. Supports SIMPLE_FORM, MODAL_FORM, CUSTOM_FORM, and DIALOGUE types. See Form Types.

Item Properties

KeyDescription
slot / slotsSingle slot number, list, or range like 0-8.
typeButton type (default NONE). See Button Types.
materialBukkit material name, PLAYER_HEAD, or AUTO.
nameDisplay name. Color codes and placeholders supported.
loreList of lore lines.
amountStack size (1–64).
custom_model_dataCustom model data integer.
glowtrue to add enchantment glow.
item_typeItem source: vanilla (default), itemsadder, nexo, headdatabase
item_idCustom item namespace ID — used when item_type is not vanilla (e.g. iasurvival:ruby)
item_flagsList of flags to hide: HIDE_ENCHANTS, HIDE_ATTRIBUTES, HIDE_POTION_EFFECTS, HIDE_UNBREAKABLE
enchantmentsMap of enchantment name → level (e.g. SHARPNESS: 5)
updatetrue to auto-refresh item name/lore with live placeholders every tick interval
update-intervalTicks between placeholder refreshes (default: 10). Requires update: true
give-itemFor DYNAMIC_PAGINATION: true to give the source item to the player's inventory on click
take-itemFor DYNAMIC_PAGINATION: true to remove the source item from the player's inventory/enderchest on click
closetrue to close the GUI on click.
commandsList of console commands run on click. Use {player}.
actionsStructured action list (see Actions).
conditionsList of condition blocks. All must pass.
else_itemItem shown when conditions fail (replaces this item).
view_requirementsConditions to show the item at all.
click_typeLimit to a specific click: LEFT, RIGHT, SHIFT_LEFT, MIDDLE, etc.

Slot Notation

YAML
slot: 13 # single slot slots: [ 0, 1, 2 ] # explicit list slots: [ 0-8 ] # range (inclusive) slots: [ 0-8, 18-26 ] # multiple ranges

Color Codes

YAML
name: "&a&lGreen Bold" name: "&#ff6600Orange Hex" name: "&lBold {player}"

Button Types

Every item in a GUI has a type: field. The default is NONE — a plain clickable item.

TypeDescription
NONEStandard clickable item. Runs commands: / actions:.
INVENTORYOpens another GUI and pushes current to history stack.
BACKPops history stack — returns to previous GUI.
HOMEJumps to the oldest GUI in the history stack.
MAIN_MENUJumps to the GUI set in main_menu: on the current GUI.
NEXTAdvance to the next page of a PAGINATION/DYNAMIC_PAGINATION GUI.
PREVIOUSGo back one page.
JUMPJump to a specific page number (to-page: 2).
PAGINATIONRenders a static paginated element list.
DYNAMIC_PAGINATIONRenders a live data source across slots.
SWITCHMulti-state toggle driven by a key or placeholder.
INPUTAccepts item placement from the player.

Navigation Buttons

Smart Menus maintains a per-player navigation history stack. These buttons let players move through it.

INVENTORY

Opens another GUI and pushes the current one onto the history stack.

YAML
open-shop: type: INVENTORY slot: 10 inventory: my_shop # ID of the target GUI material: CHEST name: "&e&lOpen Shop"

BACK

Pops the history stack and returns to the previous GUI. Hidden if there is no history.

YAML
back-btn: type: BACK slot: 45 material: RED_STAINED_GLASS_PANE name: "&c⬅ Back"

HOME

Jumps directly to the first (oldest) GUI in the navigation stack.

YAML
home-btn: type: HOME slot: 49 material: NETHER_STAR name: "&e⌂ Home"

Opens the GUI specified by main_menu: at the top level of the current GUI definition.

YAML
# At the GUI level: my_sub_gui: main_menu: main_hub ... items: menu-btn: type: MAIN_MENU slot: 50 material: COMPASS name: "&6Main Menu"

JUMP

Jumps to a specific page index (0-based) in a pagination GUI.

YAML
jump-page2: type: JUMP slot: 51 to-page: 1 # 0-indexed material: PAPER name: "&fPage 2"

Pagination

Display a fixed list of elements split across multiple pages.

Setup

YAML
my_menu: title: "&eItems — Page %page%/%max_page%" rows: 6 items: content: type: PAGINATION slots: [ 10-16, 19-25, 28-34 ] # content area material: PAPER name: "&f%title%" lore: - "&7%description%" - "&8Item %index% of %total%" elements: - title: "First Item" description: "This is the first element." total: "10" - title: "Second Item" description: "Another one." total: "10" # ... up to as many as you need next: type: NEXT slot: 53 is-permanent: false # hides on last page material: ARROW name: "&aNext →" prev: type: PREVIOUS slot: 45 is-permanent: false material: ARROW name: "&c← Prev"

Available Placeholders

PlaceholderValue
%page%Current page number (1-based)
%max_page%Total number of pages
%index%Element index (1-based, global)
%total%Total element count
Any element keyValue from the element map (e.g. %title%)

Dynamic Pagination

Render live server data — players, worlds, inventories, and more — across paginated slots.

Setup

YAML
online_players: title: "&bPlayers — %page%/%max_page%" rows: 6 items: players: type: DYNAMIC_PAGINATION source: ONLINE_PLAYERS slots: [ 9-44 ] material: PLAYER_HEAD name: "&e%player_name%" lore: - "&7World: &f%player_world%" - "&7Ping: &f%player_ping%ms" commands: - "tp {player} %player_name%"

Available Sources

SourcePlaceholders Available
ONLINE_PLAYERS%player_name%, %player_world%, %player_gamemode%, %player_health%, %player_level%, %player_ping%
WORLDS%world_name%, %world_type%, %world_players%, %world_time%, %world_difficulty%
PLAYER_INVENTORY%item_name%, %item_amount%, %item_slot%
PLAYER_ENDERCHEST%item_name%, %item_amount%, %item_slot%
PERMISSION_GROUPS%group_name%, %group_weight% (requires LuckPerms)

Taking Items

For inventory/ender chest sources, set take-item: true to remove the item from the player's inventory or ender chest when clicked.

YAML
source: PLAYER_INVENTORY take-item: true

Giving Items

Set give-item: true to add a copy of the source item to the player's main inventory on click. If the inventory is full, the item drops at the player's feet.

YAML
source: PLAYER_ENDERCHEST give-item: true # gives item to main inventory take-item: true # removes item from ender chest (combine both to move)

Inventory Viewer Example

Move items from ender chest to main inventory with a single click:

YAML
enderchest_viewer: title: "&5&l📦 Your Ender Chest" rows: 6 commands: [ "ecview", "enderchestview" ] items: border: slots: [ 0-8, 45-53 ] material: PURPLE_STAINED_GLASS_PANE name: "&8" ec-items: type: DYNAMIC_PAGINATION source: PLAYER_ENDERCHEST slots: [ 9-44 ] material: STONE name: "&f%item_name%" lore: - "&7Amount: &e%item_amount%" - "" - "&aClick &7to move to inventory" give-item: true # adds to main inventory take-item: true # removes from ender chest prev: type: PREVIOUS slot: 46 material: ARROW name: "&c← Previous" is-permanent: false next: type: NEXT slot: 52 material: ARROW name: "&aNext →" is-permanent: false close: slot: 49 material: BARRIER name: "&c&lClose" close: true

Switch Button

A multi-state toggle that evaluates a key or placeholder every tick and displays the matching state.

Setup

YAML
difficulty-switch: type: SWITCH slot: 13 key: "%server_difficulty%" # placeholder or static value material: YELLOW_WOOL name: "&7Unknown" buttons: "easy": material: LIME_WOOL name: "&a&lEASY" commands: [ "difficulty normal" ] "normal": material: YELLOW_WOOL name: "&e&lNORMAL" commands: [ "difficulty hard" ] "hard": material: RED_WOOL name: "&c&lHARD" commands: [ "difficulty easy" ]
NoteThe key: is resolved via PlaceholderAPI every 10 ticks. The matching buttons: entry is displayed. If no key matches, the fallback material/name is shown.

Input Slot

Allow players to place items into a GUI slot, triggering commands when an item is detected.

Setup

YAML
sacrifice-slot: type: INPUT slot: 13 material: MAGMA_BLOCK name: "&c[ Sacrifice Slot ]" lore: - "&7Drop an item here to sacrifice it." on-place: - "broadcast &6{player} &7sacrificed &e%input_item_name%!" - "give {player} diamond 1" remove-on-place: true # false = item stays in slot

Placeholders in on-place

PlaceholderValue
%input_item_material%Material name of the placed item
%input_item_name%Display name (or material if none)
%input_item_amount%Stack size of the placed item

Conditions

Conditions gate whether a button is clickable. All conditions in a list must pass. A failing condition can show an else_item instead.

Structure

YAML
my-item: slot: 13 material: DIAMOND name: "&bDiamond" conditions: - type: VAULT_MONEY amount: 100 take: true - type: PERMISSION permission: "shop.use" else_item: material: BARRIER name: "&cYou can't afford this"

All Condition Types

TypeDescription
VAULT_MONEYRequires a Vault balance. Optionally deducts.
XP_LEVELRequires XP levels.
XP_POINTSRequires raw XP points.
PERMISSIONRequires a Bukkit permission node.
LUCKPERMS_GROUPRequires LuckPerms group membership.
PLACEHOLDERCompares a PAPI placeholder with operator.
PLACEHOLDER_EQUALSString equality check.
PLACEHOLDER_CONTAINSString contains check.
PLACEHOLDER_GREATER_THANNumeric greater-than check.
PLACEHOLDER_LESS_THANNumeric less-than check.
EXPRESSIONFull math/logic expression with placeholders.
SCRIPTJavaScript expression returning boolean.
ITEMRequires a specific item in inventory.
WORLDGUARD_REGIONRequires player to be inside a WG region.
WEATHERChecks current world weather.
WORLDRestricts to specific world(s).
MODELED_NPCRequire player to be within radius of a ModeledNPC.
OREO_CURRENCYOreoEssentials custom currency balance.
OREO_WARPSCheck if an OreoEssentials warp exists and player has permission.
OREO_WARPS_LOCATIONRequire player to be within N blocks of an OreoEssentials warp.
ITEM_CUSTOM_MODELRequire player to have an item with a specific custom model data value.

Economy & XP Conditions

VAULT_MONEY

YAML
- type: VAULT_MONEY amount: 500 take: true # deduct on success (default false)

XP_LEVEL

YAML
- type: XP_LEVEL amount: 10 take: true

XP_POINTS

YAML
- type: XP_POINTS amount: 500 take: true

OREO_CURRENCY

YAML
- type: OREO_CURRENCY currency: "gems" amount: 100 take: true

Permission Conditions

PERMISSION

YAML
- type: PERMISSION permission: "vip.access" negate: false # true = must NOT have the permission

LUCKPERMS_GROUP

YAML
- type: LUCKPERMS_GROUP group: "vip" include_inherited: true # include parent groups negate: false

Placeholder Conditions

PLACEHOLDER

Compare a PAPI placeholder with an operator.

YAML
- type: PLACEHOLDER placeholder: "%player_level%" operator: ">=" value: "10"

Supported operators: == != > >= < <=

PLACEHOLDER_EQUALS

YAML
- type: PLACEHOLDER_EQUALS placeholder: "%player_world%" value: "world" ignore_case: true

PLACEHOLDER_CONTAINS

YAML
- type: PLACEHOLDER_CONTAINS placeholder: "%player_name%" value: "admin" ignore_case: true

PLACEHOLDER_GREATER_THAN

YAML
- type: PLACEHOLDER_GREATER_THAN placeholder: "%vault_eco_balance%" value: "1000"

PLACEHOLDER_LESS_THAN

YAML
- type: PLACEHOLDER_LESS_THAN placeholder: "%player_health%" value: "10"

Expression Engine

Write math and logic expressions directly in YAML. Placeholders are resolved before evaluation — no external library required.

Usage

YAML
- type: EXPRESSION expression: "%vault_eco_balance% >= 1000" - type: EXPRESSION expression: "%player_level% * 2 > 30" - type: EXPRESSION expression: "(%player_health% + %player_food_level%) >= 30"

Supported Operators

CategoryOperators
Arithmetic+ - * /
Grouping( )
Comparison>= <= > < == !=
WarningIf a placeholder resolves to a non-numeric string, the expression returns false. Make sure your placeholders return numbers for arithmetic expressions.

Script Conditions

Evaluate JavaScript expressions as conditions. The player binding gives direct access to the Bukkit Player object.

YAML
- type: SCRIPT script: "player.getLevel() >= 10 && player.getHealth() > 5.0"

Available Bindings

VariableTypeDescription
playerBukkit PlayerThe clicking player
Java VersionScript conditions use Nashorn (Java 8–14) or GraalVM JavaScript when available. On Java 17+ servers without GraalVM, SmartMenus automatically falls back to a built-in expression evaluator that supports ==, !=, >=, <=, >, < — after resolving any PlaceholderAPI tokens. Full JavaScript (loops, functions, Bukkit API calls) still requires GraalVM.

World & Region Conditions

WORLD

YAML
- type: WORLD worlds: [ "world", "world_nether" ] blacklist: false # true = must NOT be in these worlds

WEATHER

YAML
- type: WEATHER weather: "rain" # clear, rain, thunder

WORLDGUARD_REGION

YAML
- type: WORLDGUARD_REGION region: "spawn" require_member: false # true = must be a WG member

OREO_WARPS

Requires an OreoEssentials warp to exist and the player to have permission to use it. Requires OreoEssentials.

YAML
- type: OREO_WARPS warp: "vip_arena"

OREO_WARPS_LOCATION

Requires the player to be within a certain radius (in blocks) of an OreoEssentials warp location. Requires OreoEssentials.

YAML
- type: OREO_WARPS_LOCATION warp: "spawn" radius: 15.0 # max distance in blocks

MODELED_NPC

Requires the player to be within a certain radius of a ModeledNPCs NPC. Requires ModeledNPCs.

YAML
- type: MODELED_NPC npc: 42 # NPC ID radius: 5.0 # max distance in blocks

Item Conditions

ITEM

YAML
- type: ITEM material: DIAMOND amount: 3 take: true # removes items from inventory on success

ITEM with Custom Model Data

YAML
- type: ITEM material: PAPER custom_model_data: 1001 amount: 1 take: true

ITEMSADDER

YAML
- type: ITEMSADDER item_id: "iasurvival:ruby" amount: 5 take: true

NEXO

YAML
- type: NEXO item_id: "my_custom_item" amount: 1 take: true

ITEM_CUSTOM_MODEL

Requires the player to have an item with a specific custom_model_data value in their inventory.

YAML
- type: ITEM_CUSTOM_MODEL material: PAPER # material must also match custom_model_data: 1001 amount: 1 take: true

Actions

Actions are structured operations that fire when a button is clicked. They replace or supplement the simple commands: list.

Structure

YAML
my-button: slot: 13 material: EMERALD name: "&aBuy" actions: - type: SOUND sound: "ENTITY_PLAYER_LEVELUP" volume: 1.0 pitch: 1.2 - type: TITLE title: "&a&lPurchase Complete!" subtitle: "&7You bought a diamond." fade-in: 10 stay: 40 fade-out: 10 - type: CONSOLE_COMMAND command: "give {player} diamond 1"

All Action Types

TypeDescription
PLAYER_MESSAGESend a chat message to the player.
BROADCASTBroadcast a message to all players.
ACTION_BARShow a message on the action bar.
TITLEShow a title + subtitle.
SOUNDPlay a sound to the player.
BROADCAST_SOUNDPlay a sound to all players.
CONSOLE_COMMANDRun a command as console.
PLAYER_COMMANDRun a command as the player.
RANDOM_CONSOLE_COMMANDPick a random command from a list (equal chance).
RANDOM_PLAYER_COMMANDPick a random command as player (equal chance).
CHANCE_REWARDRoll weighted chance and run the matching reward's commands and/or messages.
TELEPORTTeleport the player to coordinates.
OPEN_INVENTORYOpen another GUI by ID.
CLOSE_INVENTORYClose the current GUI.
BACKNavigate back in history.
SERVER_CONNECTSend player to another BungeeCord server.
SCRIPTExecute JavaScript code.

Message & UI Actions

PLAYER_MESSAGE

YAML
- type: PLAYER_MESSAGE message: "&aYou clicked the button, {player}!"

BROADCAST

YAML
- type: BROADCAST message: "&6{player} &7just bought a rank!"

ACTION_BAR

YAML
- type: ACTION_BAR message: "&e+100 Coins"

TITLE

YAML
- type: TITLE title: "&a&lSuccess!" subtitle: "&7Transaction complete." fade-in: 10 stay: 60 fade-out: 20

SOUND

YAML
- type: SOUND sound: "ENTITY_PLAYER_LEVELUP" volume: 1.0 pitch: 1.0

BROADCAST_SOUND

Plays a sound for all online players — useful for server-wide announcements.

YAML
- type: BROADCAST_SOUND sound: "ENTITY_ENDER_DRAGON_DEATH" volume: 1.0 pitch: 1.0

Command Actions

CONSOLE_COMMAND

YAML
- type: CONSOLE_COMMAND command: "give {player} diamond 1"

PLAYER_COMMAND

YAML
- type: PLAYER_COMMAND command: "spawn"

RANDOM_CONSOLE_COMMAND

YAML
- type: RANDOM_CONSOLE_COMMAND commands: - "give {player} diamond 1" - "give {player} emerald 1" - "give {player} gold_ingot 3"

RANDOM_PLAYER_COMMAND

Picks one command at random from the list and runs it as the player.

YAML
- type: RANDOM_PLAYER_COMMAND commands: - "spawn" - "warp hub" - "home"

CHANCE_REWARD

Rolls a number 0–100 and gives the player the first matching reward tier. Each tier has a chance (percentage), an optional commands list (run as console), and an optional messages list (sent directly to the player with full color support). Chances do not need to add up to 100 — any remainder is a silent miss (no reward).

YAML
- type: CHANCE_REWARD rewards: - chance: 5 # 5 % → Legendary commands: - "give {player} netherite_ingot 1" - "broadcast &6{player} &7hit the jackpot!" messages: - "<gradient:#FFD700:#FF8C00><bold>✦ LEGENDARY!</bold></gradient> <white>Netherite Ingot!" - chance: 25 # 25 % → Rare commands: - "give {player} diamond 3" messages: - "<aqua><bold>✦ RARE!</bold></aqua> <gray>You found 3 Diamonds!" - chance: 70 # 70 % → Common commands: - "give {player} iron_ingot 8" messages: - "&fCommon. &7You found 8 Iron Ingots." # remaining 0 % → silent miss

messages: — Color support

Messages under messages: are sent directly to the player and support all color formats:

FormatExample
& codes&a&lGreen Bold
Hex colors&#FF5500text
MiniMessage colors<red>text</red>, <#FF5500>text
MiniMessage gradient<gradient:#FF0000:#0000FF>text</gradient>
MiniMessage rainbow<rainbow>text</rainbow>
MiniMessage formatting<bold>, <italic>, <underlined>
Variable SubstitutionUse {player} in any command or message — it is replaced with the clicking player's name. PlaceholderAPI placeholders also work.

Teleport Action

YAML
- type: TELEPORT world: "world" x: 0.5 y: 64.0 z: 0.5 yaw: 0.0 pitch: 0.0

Inventory Actions

OPEN_INVENTORY

YAML
- type: OPEN_INVENTORY inventory: "my_shop"

CLOSE_INVENTORY

YAML
- type: CLOSE_INVENTORY

BACK

YAML
- type: BACK

OPEN_BEDROCK_FORM

Opens the bedrock: form of the target GUI for Bedrock players. Java players get the normal chest inventory.

YAML
- type: OPEN_BEDROCK_FORM inventory: "my_gui"

OPEN_BEDROCK_DIALOGUE

Opens the target GUI's Bedrock DIALOGUE form (NPC speech bubble). Falls back to SIMPLE_FORM if the Dialogue API is unavailable. Falls back to Java inventory for Java players.

YAML
- type: OPEN_BEDROCK_DIALOGUE inventory: "my_gui"
Auto-RoutingOPEN_INVENTORY also auto-routes to the Bedrock form when auto-convert: true or a bedrock: block is defined on the target GUI. You only need OPEN_BEDROCK_FORM / OPEN_BEDROCK_DIALOGUE when you want to force a specific Bedrock form even if the caller would normally open a different one.

Script Actions

Execute arbitrary JavaScript with the player and plugin bindings.

YAML
- type: SCRIPT script: "player.sendMessage('\u00a7aHello ' + player.getName() + '!');"

Available Bindings

VariableType
playerBukkit Player
pluginSmart MenusPlugin instance

BungeeCord Action

YAML
- type: SERVER_CONNECT server: "lobby"
RequirementYour server must be running behind BungeeCord or Velocity (with BungeeCord forwarding). The plugin automatically registers the BungeeCord plugin messaging channel on startup.

Dynamic Sources

Dynamic Sources power DYNAMIC_PAGINATION buttons with live server data.

ONLINE_PLAYERS

One entry per online player. Material PLAYER_HEAD automatically uses that player's skin.

YAML
type: DYNAMIC_PAGINATION source: ONLINE_PLAYERS material: PLAYER_HEAD name: "&e%player_name%"

WORLDS

One entry per loaded world. Material AUTO picks an icon by environment (GRASS_BLOCK for normal, NETHERRACK for nether, END_STONE for the end).

YAML
source: WORLDS material: AUTO name: "&a%world_name%"

PLAYER_INVENTORY / PLAYER_ENDERCHEST

Renders the player's actual inventory or ender chest items. Non-air slots only.

YAML
source: PLAYER_INVENTORY take-item: true # removes item on click

PERMISSION_GROUPS

All LuckPerms groups, sorted by weight.

YAML
source: PERMISSION_GROUPS name: "&6%group_name%" lore: - "&7Weight: %group_weight%"

Patterns

Patterns are reusable item templates. Define once in plugins/SmartMenus/patterns/, apply across any number of GUIs with variable substitution.

Creating a Pattern

plugins/SmartMenus/patterns/shop_button.yml
shop_button: material: "{material}" name: "&e&l{item_name}" lore: - "&7Price: &a{price}" - "" - "&eClick to buy" conditions: - type: VAULT_MONEY amount: "{price}" take: true commands: - "give {player} {material} 1"

Applying a Pattern

YAML
my_shop: items: diamond-btn: slot: 10 patterns: [ "shop_button" ] vars: material: "DIAMOND" item_name: "Diamond" price: "100" emerald-btn: slot: 11 patterns: [ "shop_button" ] vars: material: "EMERALD" item_name: "Emerald" price: "50"

Commands & Permissions

Plugin Commands

All commands accept the following aliases: /sm, /smenu, /smenus, /smartmenu, /ogui, /gui, /omenu.

CommandDescriptionPermission
/sm reloadHot-reload all GUI files and lang.ymlsmartmenus.reload
/sm open <id> [player]Open a GUI for yourself or another playersmartmenus.open
/sm captureitem <gui> <slot> [key]Capture the item in your hand into a GUI configsmartmenus.captureitem

captureitem — In-Game Item Capture

Hold any item in your main hand, run the command, and SmartMenus writes it directly into the GUI's YAML file. No manual YAML editing — the file is saved and the GUI reloads instantly.

Usage
/smartmenus captureitem <gui_id> <slot> [item_key]
ArgumentRequiredDescription
gui_id✔ID of the GUI to add the item to
slot✔Slot number (0–53) where the item will appear
item_key—YAML key name for the item. Defaults to captured_slot_<slot>

What gets captured automatically:

FieldDetails
nameDisplay name — § color codes converted to & for readability
loreEvery lore line, preserved exactly
materialItem type (e.g. ENCHANTED_BOOK)
enchantmentsRegular enchants and stored enchants (enchanted books)
custom_model_dataCaptured automatically if present
item_flagsHIDE_ENCHANTS, HIDE_ATTRIBUTES, etc.
Example
# Hold a Sharpness V custom enchant book, then run: /smartmenus captureitem my_shop 13 # With a custom key name: /smartmenus captureitem my_shop 13 sharpness_book

This produces the following in my_shop.yml automatically:

Generated YAML
my_shop: items: sharpness_book: slot: 13 material: ENCHANTED_BOOK name: "&6&lSharpness V" lore: - "&7Applies Sharpness V to any sword." - "&ePrice: &f500 tokens" enchantments: sharpness: 5 item_flags: - HIDE_ENCHANTS
TipAfter capturing, open the GUI with /smartmenus open <id> to verify it looks right. You can always hand-edit the generated YAML to add conditions:, actions:, or a price.

GUI Command Permissions

Every GUI command auto-generates a permission: smartmenus.command.<gui_id>

Example
# GUI with id "my_shop" and commands: [ "shop" ] # Auto-generated permission: smartmenus.command.my_shop

All Permissions

PermissionDefaultDescription
smartmenus.opentrueOpen GUIs for other players
smartmenus.reloadopUse /smartmenus reload
smartmenus.captureitemopCapture held item into a GUI config
smartmenus.command.*trueOpen all GUI commands
smartmenus.command.<id>trueOpen a specific GUI command
smartmenus.*opAll permissions

Placeholders

Smart Menus supports PlaceholderAPI in all item names, lore lines, titles, and condition values.

Built-in Placeholders

PlaceholderWhereValue
{player}Commands, actionsClicking player's name
%page%Pagination titles/loreCurrent page (1-based)
%max_page%Pagination titles/loreTotal pages
%player_name%ONLINE_PLAYERS sourcePlayer name
%world_name%WORLDS sourceWorld name
%item_name%Inventory sourcesItem display name
%input_item_material%INPUT on-placeMaterial of placed item
%input_item_name%INPUT on-placeDisplay name of placed item
%input_item_amount%INPUT on-placeAmount of placed item
PlaceholderAPIAny %papi_placeholder% works in names, lore, titles, and condition values when PlaceholderAPI is installed.

Integrations

All integrations are optional. Smart Menus detects each plugin at startup and gracefully degrades if it's not present.

PluginFeatures Unlocked
PlaceholderAPIPlaceholders in names, lore, titles, conditions
VaultVAULT_MONEY condition
LuckPermsLUCKPERMS_GROUP condition, PERMISSION_GROUPS dynamic source
WorldGuardWORLDGUARD_REGION condition
ItemsAdderITEMSADDER condition; item_type: itemsadder items
NexoNEXO condition; item_type: nexo items
OreoEssentialsOREO_CURRENCY, OREO_WARPS conditions; custom currency items
ModeledNPCsnpc_id: binding to open GUI on NPC right-click; MODELED_NPC condition
BungeeCord / VelocitySERVER_CONNECT action

Developer API

SmartMenus ships a clean, versioned public API at com.ogui.api.SmartMenusAPI. Other plugins can open GUIs, register custom action/condition types, and evaluate scripts — all without touching internal classes.

Setup

Add SmartMenus as a dependency in your plugin.yml:

plugin.yml
name: MyPlugin depend: [SmartMenus]

Then get the API instance anywhere in your plugin:

Java
SmartMenusAPI api = SmartMenusAPI.get(); if (api != null) { // SmartMenus is loaded and ready }

Or throw if SmartMenus is not present (useful if it's a hard dependency):

Java
SmartMenusAPI api = SmartMenusAPI.require(); // throws IllegalStateException if not loaded

Opening a GUI

Java
// Open by GUI ID boolean opened = api.openGui(player, "my_shop"); // Open with custom args (%arg_name% placeholders in the GUI) Map<String, String> args = new HashMap<>(); args.put("category", "weapons"); api.openGui(player, "my_shop", args);

Querying GUIs

Java
// Check if a GUI exists boolean exists = api.isGuiLoaded("my_shop"); // Get the definition GuiDefinition def = api.getGui("my_shop"); // List all loaded GUI IDs Set<String> ids = api.getGuiIds();

Reloading

Java
api.reloadGuis(); // reloads all GUI config files from disk

Registering Custom Action Types

Add a new action type that server owners can use in any GUI's actions: list:

Java
api.registerAction("CUSTOM_FIREWORK", map -> { String color = (String) map.getOrDefault("color", "RED"); return (plugin, player, vars) -> { // your logic here — launch a firework, etc. launchFirework(player, color); }; });

Server owners can then use it in YAML:

YAML
actions: - type: CUSTOM_FIREWORK color: "BLUE"

To remove it when your plugin disables:

Java
api.unregisterAction("CUSTOM_FIREWORK");

Registering Custom Condition Types

Add a new condition type that works in conditions: and view-requirements::

Java
api.registerCondition("PLAYTIME", (smartMenusPlugin, section) -> { int hours = section.getInt("hours", 0); return new Condition() { public boolean check(Player player) { return getPlaytimeHours(player) >= hours; } public boolean take(Player player) { return false; } // consume-on-click? public String getErrorMessage(Player player) { return "&cYou need " + hours + " hours of playtime."; } public ConditionType getType() { return null; } }; });

In YAML:

YAML
conditions: - type: PLAYTIME hours: 10

Script Engine

Java
// Evaluate a JavaScript condition (returns boolean) boolean result = api.evalConditionScript("player.getLevel() >= 10 && player.getHealth() > 5.0", player); // Execute a JavaScript action api.evalActionScript("player.sendMessage('\\u00a7aHello from API!');", player);
JavaScript bindingsScripts have access to player (the Bukkit Player object) and plugin (the SmartMenus plugin instance). Requires Nashorn (Java 11–14) or GraalVM JS.

Full Integration Example

MyPlugin.java
public class MyPlugin extends JavaPlugin { @Override public void onEnable() { SmartMenusAPI api = SmartMenusAPI.get(); if (api == null) { getLogger().warning("SmartMenus not found — GUI integration disabled."); return; } // Register a custom "REWARD_CRATE" action api.registerAction("REWARD_CRATE", map -> { String crateType = (String) map.getOrDefault("crate", "common"); return (plugin, player, vars) -> MyCratePlugin.openCrate(player, crateType); }); // Register a custom "FACTION_RANK" condition api.registerCondition("FACTION_RANK", (smPlugin, section) -> { String rank = section.getString("rank", "member"); return new Condition() { public boolean check(Player p) { return MyFactionPlugin.hasRank(p, rank); } public boolean take(Player p) { return false; } public String getErrorMessage(Player p) { return "&cYou need faction rank: &f" + rank; } public ConditionType getType() { return null; } }; }); getLogger().info("SmartMenus integration loaded."); } @Override public void onDisable() { SmartMenusAPI api = SmartMenusAPI.get(); if (api != null) { api.unregisterAction("REWARD_CRATE"); api.unregisterCondition("FACTION_RANK"); } } }

Examples

Copy-paste ready examples covering the most common server setups. Each one is complete and works out of the box.

GeneratorWant to build GUIs visually instead of writing YAML by hand? Use the ⚙ GUI Config Generator — it produces valid YAML automatically.

1. Item Shop (Vault Economy)

A 3-row shop with a border, two buyable items, and a close button. Items show a grey "can't afford" version when the player has insufficient funds.

plugins/SmartMenus/guis/my_shop/my_shop.yml
my_shop: title: "&8&l🛒 Item Shop" rows: 3 commands: [ "shop", "itemshop" ] items: # ── Background ────────────────────────────────────────── border: slots: [ 0-8, 18-26 ] material: BLACK_STAINED_GLASS_PANE name: "&8" # ── Buy Diamond — $100 ───────────────────────────────── buy-diamond: slot: 11 material: DIAMOND name: "&b&lDiamond &8— &a$100" lore: - "&7A shiny diamond." - "" - "&7Your balance: &a$%vault_eco_balance%" - "&eClick to buy" glow: true conditions: - type: VAULT_MONEY amount: 100 take: true # deducts $100 on successful click actions: - type: SOUND sound: "ENTITY_EXPERIENCE_ORB_PICKUP" pitch: 1.5 - type: PLAYER_MESSAGE message: "&a✔ You bought a &bDiamond &afor &2$100&a!" - type: CONSOLE_COMMAND command: "give {player} diamond 1" else_item: material: GRAY_DYE name: "&7Diamond &8— &cNeed $100" lore: - "&cYou don't have enough money." - "&7Balance: &c$%vault_eco_balance%" # ── Buy Emerald — $50 ────────────────────────────────── buy-emerald: slot: 15 material: EMERALD name: "&a&lEmerald &8— &a$50" lore: - "&7A bright green emerald." - "" - "&7Your balance: &a$%vault_eco_balance%" - "&eClick to buy" conditions: - type: VAULT_MONEY amount: 50 take: true actions: - type: SOUND sound: "ENTITY_EXPERIENCE_ORB_PICKUP" - type: PLAYER_MESSAGE message: "&a✔ You bought an &aEmerald &afor &2$50&a!" - type: CONSOLE_COMMAND command: "give {player} emerald 1" else_item: material: GRAY_DYE name: "&7Emerald &8— &cNeed $50" lore: - "&cYou don't have enough money." # ── Close ────────────────────────────────────────────── close: slot: 22 material: BARRIER name: "&c&lClose" close: true

2. Rank Upgrade Menu

Players can upgrade their rank by paying and clicking. Uses multiple conditions: money, existing rank, and "must NOT already have VIP". Shows the button greyed out if already VIP.

plugins/SmartMenus/guis/rank_upgrade/rank_upgrade.yml
rank_upgrade: title: "&6&l⭐ Rank Upgrades" rows: 4 commands: [ "rankup", "upgrade" ] items: border: slots: [ 0-8, 27-35 ] material: YELLOW_STAINED_GLASS_PANE name: "&8" # ── VIP Upgrade button ───────────────────────────────── vip-upgrade: slot: 13 material: GOLD_INGOT name: "&6&lUpgrade to &e&lVIP" lore: - "&8&m────────────────────" - " &7Cost: &a$5,000" - " &7Requires: &fDefault rank" - "&8&m────────────────────" - "" - "&eClick to upgrade" glow: true conditions: - type: VAULT_MONEY amount: 5000 take: true - type: PERMISSION permission: "group.default" # must have Default rank - type: PERMISSION permission: "group.vip" negate: true # must NOT already be VIP actions: - type: CONSOLE_COMMAND command: "lp user {player} parent set vip" - type: TITLE title: "&6&l⭐ VIP Unlocked!" subtitle: "&7Welcome to VIP, &e{player}&7!" stay: 60 - type: SOUND sound: "UI_TOAST_CHALLENGE_COMPLETE" volume: 1.0 - type: BROADCAST message: "&6⭐ &e{player} &7has upgraded to &6&lVIP&7!" - type: CLOSE_INVENTORY else_item: material: IRON_INGOT name: "&7Already VIP &8or &cInsufficient Funds" lore: - "&7You either already have VIP," - "&7or you need &a$5,000 &7(have: &c$%vault_eco_balance%&7)." close: slot: 31 material: BARRIER name: "&c&lClose" close: true

3. Navigation Hub (Multi-level menus)

A main hub that opens sub-menus. BACK/HOME buttons let players navigate the history stack.

plugins/SmartMenus/guis/hub/hub.yml
# ── Main Hub ────────────────────────────────────────────────── main_hub: title: "&8&l⚙ Server Hub" rows: 3 commands: [ "hub", "menu" ] main_menu: main_hub # MAIN_MENU buttons return here items: border: slots: [ 0-8, 18-26 ] material: BLACK_STAINED_GLASS_PANE name: "&8" btn-shop: type: INVENTORY # opens shop_menu and pushes hub to history slot: 11 inventory: shop_menu material: CHEST name: "&e&lShop" lore: - "&7Buy items and upgrades." - "&eClick →" btn-quests: type: INVENTORY slot: 13 inventory: quest_menu material: BOOK name: "&d&lQuests" lore: - "&7View available quests." - "&eClick →" btn-stats: type: INVENTORY slot: 15 inventory: stats_menu material: EXPERIENCE_BOTTLE name: "&a&lStats" lore: - "&7View your statistics." - "&eClick →" close: slot: 22 material: BARRIER name: "&c&lClose" close: true # ── Shop Sub-menu ────────────────────────────────────────────── shop_menu: title: "&e&lShop" rows: 3 main_menu: main_hub items: border: slots: [ 0-8, 18-26 ] material: YELLOW_STAINED_GLASS_PANE name: "&8" buy-xp: slot: 13 material: EXPERIENCE_BOTTLE name: "&a&lBuy 10 XP Levels — &2$200" conditions: - type: VAULT_MONEY amount: 200 take: true commands: - "xp {player} 10l" else_item: material: GRAY_DYE name: "&7Need &a$200" # BACK returns to main_hub back: type: BACK slot: 20 material: RED_STAINED_GLASS_PANE name: "&c⬅ Back" # HOME also returns to main_hub (they're the same here) home: type: HOME slot: 22 material: NETHER_STAR name: "&e⌂ Home" close: slot: 26 material: BARRIER name: "&c&lClose" close: true

4. Paginated Crate Rewards Preview

A scrollable list of rewards using PAGINATION. Each element is defined in the YAML — no limit on how many.

plugins/SmartMenus/guis/crate/crate_preview.yml
crate_preview: title: "&5&l✦ Crate Rewards &8| &7%page%&8/&7%max_page%" rows: 6 commands: [ "cratepreview" ] items: border-top: slots: [ 0-8 ] material: PURPLE_STAINED_GLASS_PANE name: "&8" border-bot: slots: [ 45-53 ] material: PURPLE_STAINED_GLASS_PANE name: "&8" # Content area: rows 2-5 (slots 9-44), edges filler filler-left: slots: [ 9, 18, 27, 36 ] material: GRAY_STAINED_GLASS_PANE name: "&8" filler-right: slots: [ 17, 26, 35, 44 ] material: GRAY_STAINED_GLASS_PANE name: "&8" rewards: type: PAGINATION slots: [ 10-16, 19-25, 28-34, 37-43 ] material: PAPER name: "&f&l%reward_name%" lore: - "&7Chance: &e%chance%%" - "&7Amount: &f%amount%" - "" - "&8Item %index% of %total%" elements: - reward_name: "Diamond Sword" chance: "5" amount: "1" total: "12" - reward_name: "64x Gold Ingots" chance: "15" amount: "64" total: "12" - reward_name: "$1,000 Cash" chance: "20" amount: "$1,000" total: "12" - reward_name: "Speed Potion" chance: "25" amount: "3" total: "12" - reward_name: "XP Bottle x10" chance: "20" amount: "10" total: "12" - reward_name: "Enchanted Book" chance: "10" amount: "1" total: "12" - reward_name: "Netherite Ingot" chance: "3" amount: "1" total: "12" - reward_name: "VIP 7 Days" chance: "2" amount: "7 days" total: "12" # Add more items here — Smart Menus handles pagination automatically prev: type: PREVIOUS slot: 46 is-permanent: false material: ARROW name: "&c← Previous" page-info: slot: 49 material: PAPER name: "&7Page &f%page% &8/ &f%max_page%" next: type: NEXT slot: 52 is-permanent: false material: ARROW name: "&aNext →" close: slot: 53 material: BARRIER name: "&c&lClose" close: true

5. Online Players List (Dynamic)

Live list of all online players with auto-skinned heads. Clicking teleports you to that player. Updates every tick.

plugins/SmartMenus/guis/players/players.yml
online_players: title: "&b&l👥 Online Players &8(&7%page%&8/&7%max_page%&8)" rows: 6 commands: [ "players", "online" ] items: border: slots: [ 0-8, 45-53 ] material: CYAN_STAINED_GLASS_PANE name: "&8" player-list: type: DYNAMIC_PAGINATION source: ONLINE_PLAYERS slots: [ 9-44 ] material: PLAYER_HEAD # auto-applies that player's skin name: "&e&l%player_name%" lore: - "&8&m──────────────────" - " &7World: &f%player_world%" - " &7Mode: &f%player_gamemode%" - " &7Health: &a%player_health%&7/&a%player_max_health%" - " &7Level: &e%player_level%" - " &7Ping: &f%player_ping%ms" - "&8&m──────────────────" - " &eClick &7to teleport" commands: - "tp {player} %player_name%" prev: type: PREVIOUS slot: 46 is-permanent: false material: ARROW name: "&c← Previous" page-info: slot: 49 material: PAPER name: "&7Page &f%page% &8/ &f%max_page%" next: type: NEXT slot: 52 is-permanent: false material: ARROW name: "&aNext →" close: slot: 53 material: BARRIER name: "&c&lClose" close: true

6. Warp Menu (Expression Conditions)

Warp buttons that use expression conditions — e.g. the nether warp requires level 10+, the end warp requires level 30+.

plugins/SmartMenus/guis/warps/warps.yml
warp_menu: title: "&8&l🗺 Warps" rows: 3 commands: [ "warps", "warp" ] items: border: slots: [ 0-8, 18-26 ] material: GRAY_STAINED_GLASS_PANE name: "&8" # Spawn — always accessible warp-spawn: slot: 10 material: GRASS_BLOCK name: "&a&lSpawn" lore: - "&7Teleport to spawn." - "&eClick!" actions: - type: CLOSE_INVENTORY - type: TELEPORT world: "world" x: 0.5 y: 64.0 z: 0.5 # Nether — requires level 10 warp-nether: slot: 13 material: NETHERRACK name: "&c&lNether &8— &7Lv10+" lore: - "&7Teleport to the nether." - "&7Requires: &eLv10" - "&7Your level: &e%player_level%" conditions: - type: EXPRESSION expression: "%player_level% >= 10" actions: - type: CLOSE_INVENTORY - type: TELEPORT world: "world_nether" x: 0.5 y: 64.0 z: 0.5 else_item: material: SOUL_SAND name: "&7Nether &8— &cLocked" lore: - "&cRequires Level &e10&c." - "&7Your level: &e%player_level%" # End — requires level 30 warp-end: slot: 16 material: END_STONE name: "&5&lThe End &8— &7Lv30+" lore: - "&7Teleport to the end." - "&7Requires: &eLv30" - "&7Your level: &e%player_level%" conditions: - type: EXPRESSION expression: "%player_level% >= 30" actions: - type: CLOSE_INVENTORY - type: TELEPORT world: "world_the_end" x: 0.5 y: 64.0 z: 0.5 else_item: material: END_STONE_BRICKS name: "&7The End &8— &cLocked" lore: - "&cRequires Level &e30&c." - "&7Your level: &e%player_level%" close: slot: 22 material: BARRIER name: "&c&lClose" close: true

7. NPC Shop (ModeledNPCs)

Opens automatically when a player right-clicks NPC #42. Also accessible via /npcshop.

plugins/SmartMenus/guis/npc_shop/npc_shop.yml
npc_shop: title: "&6&l🧙 Wizard's Shop" rows: 4 npc_id: 42 # right-click ModeledNPC ID 42 to open commands: [ "npcshop" ] items: border: slots: [ 0-8, 27-35 ] material: ORANGE_STAINED_GLASS_PANE name: "&8" buy-spell: slot: 13 material: BLAZE_POWDER name: "&6&lFire Spell Book" lore: - "&7Cast fire magic." - "&7Cost: &a10 XP levels" - "" - "&7Your levels: &e%player_level%" - "&eClick to buy" conditions: - type: XP_LEVEL amount: 10 take: true commands: - "give {player} blaze_powder 1" else_item: material: GUNPOWDER name: "&7Fire Spell Book &8— &cNeed 10 Levels" close: slot: 31 material: BARRIER name: "&c&lClose" close: true

Plugin Config

Located at plugins/SmartMenus/config.yml. Auto-generated on first start. Edit then run /smartmenus reload — no restart needed.

Full config.yml

plugins/SmartMenus/config.yml
# ───────────────────────────────────────────── # Smart Menus — Main Configuration # ───────────────────────────────────────────── # Controls whether the default example GUI folders are regenerated on startup. # # true → Missing default files are recreated every server start. # Great while learning — the examples always come back. # # false → Default GUIs are never written after first generation. # Delete example folders and they will stay gone. # Use this once you have your own GUIs set up. regenerate-default-guis: true

Option Reference

KeyDefaultDescription
regenerate-default-guis true When true, every missing default GUI file (all the examples that ship with Smart Menus) is re-extracted from the jar on every server start.

Set to false once you've cleaned up the example GUIs and only want your own configs on disk.
Recommended workflow
  1. Start with regenerate-default-guis: true — study the examples.
  2. Copy and customise what you need into your own folders.
  3. Delete the example folders.
  4. Set regenerate-default-guis: false — they won't come back.

Language File

Located at plugins/SmartMenus/lang.yml. Every message players and the console see comes from here. Supports legacy codes, hex colors, gradients, and rainbow effects.

Color Format Support

FormatExampleResult
Legacy codes&c&lBold RedBold red text
Hex color&#FFD700GoldGold / #FFD700
Gradient<gradient:#FF0000:#FFD700>Fire</gradient>Red→gold gradient
Rainbow<rainbow>Colorful</rainbow>Cycling rainbow
PlaceholderAPI%vault_eco_balance%Live placeholder value

Sections

general

Core plugin messages shown to players.

lang.yml
general: prefix: "<gradient:#FFD700:#FF8C00>&l[SmartMenus]</gradient> &r" no_permission: "<gradient:#FF0000:#FF6B6B>✖ Access Denied</gradient> &7→ &fNo permission." player_only: "&#FF4444&l⚠ &cOnly players can use this command." gui_not_found: "<gradient:#FF0000:#FF6B6B>✖ GUI Not Found</gradient> &7→ &f{gui}" player_not_found: "&#FF6B6B✖ Player not found: &f{player}" gui_opened: "<gradient:#00FF00:#00FFFF>✔ Opened</gradient> &7GUI &f{gui} &7for &f{player}" reload: success: "<gradient:#00FF00:#32CD32>✔ Reloaded!</gradient> &7Smart Menus reloaded." gui_count: " &#00FFFF→ &7Loaded &f{count} &7GUI(s)"

commands

Messages for the /smartmenus command output.

lang.yml
commands: usage: main: "<gradient:#FFD700:#FFA500>&l⚡ Smart Menus</gradient>" line1: "&7Usage: &f/smartmenus &7<&bopen&7|&breload&7> &8[&fid&8] &8[&fplayer&8]" open: usage: "&#FF6B6B&l⚠ Usage: &c/smartmenus open &f<id> &8[player]" available: "<gradient:#00FFFF:#0099FF>Available GUIs:</gradient> &f{guis}" reload: no_permission: "<gradient:#FF0000:#FF6B6B>✖ Permission Denied</gradient>"

conditions

Error messages shown when a condition fails. Each condition type has its own sub-key.

lang.yml
conditions: vault_money: insufficient: | <gradient:#FF0000:#FFD700>💰 Insufficient Funds!</gradient> &#FFD700&l→ Required: &f${amount} &#00FF00&l→ Balance: &f${balance} economy_unavailable: "⚠ Vault not found!" xp_level: insufficient: | <gradient:#00FFFF:#0099FF>⭐ Not Enough Levels!</gradient> &#00FFFF&l→ Required: &f{amount} &7levels &#FFD700&l→ Current: &f{current} &7levels permission: missing: | <gradient:#FF0000:#FFA500>🔒 ACCESS DENIED</gradient> &#FF6B6B&l→ Required: &#FFD700{permission} worldguard_region: not_in_region: | <gradient:#FF6B6B:#FFA500>📍 Wrong Location!</gradient> &#FFA500&l→ Required region: &#FFD700{region}

Condition Message Placeholders

PlaceholderAvailable inValue
{amount}All cost/requirement messagesRequired amount
{balance}vault_money, oreo_currencyPlayer's current balance
{current}xp_level, xp_points, itemPlayer's current amount
{currency}oreo_currencyCurrency name
{permission}permissionRequired permission node
{region}worldguard_regionRegion name
{placeholder}placeholderPAPI placeholder key
{operator}placeholderComparison operator
{value}placeholderComparison value
{required}weatherRequired weather
{worlds}worldList of allowed worlds
{npc}modeled_npcNPC name
{distance}modeled_npcCurrent distance in blocks
{radius}modeled_npcRequired max distance
{warp}oreo_warpsWarp name
{item}item, itemsadder, nexoItem name
{count}reload messagesNumber of GUIs loaded
{gui}general, npc messagesGUI ID
{player}VariousPlayer name
{error}Error messagesError details

Ready-to-use Gradient Examples

Copy & paste these into any message
# 🔥 Fire <gradient:#FF0000:#FF6600:#FFFF00>🔥 Fire text 🔥</gradient> # 🌊 Ocean <gradient:#0000FF:#00FFFF:#1E90FF>🌊 Ocean waves 🌊</gradient> # ✨ Gold Shine <gradient:#FFD700:#FFED4E:#FFD700>✨ Shiny gold ✨</gradient> # ⚡ Neon <gradient:#FF00FF:#00FFFF>⚡ Neon effect ⚡</gradient> # 🌅 Sunset <gradient:#FF6B6B:#FFD93D:#6BCB77>🌅 Sunset glow 🌅</gradient> # Multi-line condition message with gradient header: insufficient: | <gradient:#FF0000:#FFD700>💰 Not Enough Money!</gradient> &#FFD700&l→ Need: &f${amount} &#00FF00&l→ Balance: &f${balance}
TipRun /smartmenus reload after editing lang.yml — changes apply instantly without a server restart.
Multi-line valuesUse the YAML block scalar | for multi-line messages (as shown above). Each new line becomes a new chat line.

CHAT_INPUT Button

Prompts the player to close the inventory and type a response in chat. The typed text is captured as {chat_input} and passed to all actions.

How It Works

  1. Player clicks the CHAT_INPUT button
  2. The GUI closes automatically
  3. The chat-prompt message is sent to the player
  4. The next message the player types is captured
  5. All actions: fire with {chat_input} replaced by what they typed

Setup

YAML
ask-nickname: type: CHAT_INPUT slot: 13 material: NAME_TAG name: "&eSet Your Nickname" lore: - "&7Click and type a new nickname." - "" - "&eClick →" chat-prompt: "&eType your new nickname in chat:" actions: - type: CONSOLE_COMMAND command: "nick {player} {chat_input}" - type: PLAYER_MESSAGE message: "&aYour nickname is now: &f{chat_input}"

Properties

KeyRequiredDescription
chat-promptNoMessage sent to the player asking for input. Supports color codes and placeholders.
actions:NoActions fired after the player types. {chat_input} is replaced with what they typed.

Notes

  • {chat_input} works in all action fields: commands, messages, titles, etc.
  • Conditions are checked on the initial click — before the chat prompt is sent
  • If the player quits before typing, no action fires
  • The player's typed message is not broadcast to other players

Real-World Example: Warp Search

YAML
warp-search-btn: type: CHAT_INPUT slot: 4 material: COMPASS name: "&bSearch Warps" lore: - "&7Type a warp name to teleport." - "&eClick →" chat-prompt: "&bType a warp name:" actions: - type: PLAYER_COMMAND command: "warp {chat_input}" - type: PLAYER_MESSAGE message: "&7Warping to: &b{chat_input}"

Full-Screen GUI — 90 Interactive Slots

SmartMenus is the only GUI plugin that can use the player's own inventory as part of the GUI — giving you up to 90 interactive slots in a single menu.

Overview

When use_bottom_inventory: true is set, SmartMenus takes over the player's 36 inventory slots (main + hotbar) as interactive GUI slots, giving you 90 total. Two rendering modes are available:

ModeHow it worksRequirements
DEFAULTReal inventory is saved to memory on open, slots are filled with GUI items, and the snapshot is restored on close. Zero item loss under normal conditions.Nothing extra
PACKET_EVENTGUI items are sent as packets only. The real inventory is never touched or modified at any point — crash-safe, no snapshot needed, no flicker.PacketEvents plugin installed
RecommendedUse PACKET_EVENT on production servers. Because the real inventory is never modified, a crash cannot cause item loss. Install PacketEvents and set bottom_inventory_mode: PACKET_EVENT.
Auto-detectionSmartMenus prints a banner on startup showing whether PacketEvents is detected. If a GUI requests PACKET_EVENT but PacketEvents is absent, SmartMenus automatically falls back to DEFAULT and logs a warning.

Enabling It

YAML
my_fullscreen_gui: title: "&5&lFull Screen Hub" rows: 6 use_bottom_inventory: true bottom_inventory_mode: PACKET_EVENT # DEFAULT or PACKET_EVENT items: # Slots 0–53 (the top chest section) center: slot: 22 material: NETHER_STAR name: "&dMain Hub" bottom_items: # Slots 0–35 (the player's inventory area shown below) info: slot: 4 material: PAPER name: "&fServer Hub"

Slot Mapping

The bottom_items slot numbers (0–35) map to the player's inventory as displayed on screen:

bottom_items slotsPlayer inventory area (screen order)Player inv slots (internal)
0–8Main row 1 (top)9–17
9–17Main row 218–26
18–26Main row 327–35
27–35Hotbar (bottom row)0–8

So slot: 4 in bottom_items = center of the first visible row of the player's inventory area. slot: 31 = center of the hotbar row.

Safety Guarantees

DEFAULT mode:

  • Real inventory saved on open — stored per player UUID in memory
  • Restored on GUI close — 1-tick delayed to let SmartInvs finish cleanup
  • Restored on player quit — fires before the server saves player data to disk
  • Restored on server reload / plugin disable — loops all online players
  • Restored on kick — handled by the quit event

PACKET_EVENT mode:

  • Real inventory is never modified — no snapshot required
  • On close, the real items are re-sent via packets so the client sees them correctly
  • Crash-safe — a server crash cannot corrupt or lose inventory because nothing was written
  • Navigating between full-screen GUIs is seamless — no flicker, no intermediate restore step
Do not nest full-screen GUIs (DEFAULT mode only)In DEFAULT mode, avoid opening a second use_bottom_inventory GUI from inside one that is already active — the snapshot of the first GUI would be overwritten. In PACKET_EVENT mode this is handled automatically with no risk of item loss.

Both Sections Support Everything

Items in bottom_items: support the full feature set — the same as items::

  • All button types: NONE, SWITCH, INPUT, DYNAMIC_PAGINATION, PAGINATION, etc.
  • All 23 condition types and view-requirements
  • All 16 action types and per-click-type click_actions:
  • close: true, glow: true, enchantments, item_flags
  • Auto-refresh with update: true + update-interval:

Full Example

plugins/SmartMenus/guis/dynamic_examples/full_screen_demo.yml
server_hub: title: "&5&l✦ Server Hub" rows: 6 commands: [ "hub", "fullscreen" ] use_bottom_inventory: true bottom_inventory_mode: PACKET_EVENT # DEFAULT or PACKET_EVENT (PacketEvents required for PACKET_EVENT) items: border-top: slots: [ 0-8 ] material: PURPLE_STAINED_GLASS_PANE name: "&8" border-bot: slots: [ 45-53 ] material: PURPLE_STAINED_GLASS_PANE name: "&8" side-left: slots: [ 9, 18, 27, 36 ] material: PURPLE_STAINED_GLASS_PANE name: "&8" side-right: slots: [ 17, 26, 35, 44 ] material: PURPLE_STAINED_GLASS_PANE name: "&8" center-info: slot: 22 material: NETHER_STAR name: "&d&lFull-Screen GUI" lore: - "&7The 6 rows above &fand&7 your inventory" - "&7below are all part of this GUI." - "&7Your real items are never modified." glow: true btn-hello: slot: 25 material: LIME_DYE name: "&a&lSay Hello" actions: - type: PLAYER_MESSAGE message: "&d&lFull-Screen GUI &7says: &fHello, &e{player}&f!" - type: SOUND sound: UI_BUTTON_CLICK close: slot: 49 material: BARRIER name: "&c&lClose" close: true bottom_items: bottom-border-left: slots: [ 0, 9, 18, 27 ] material: GRAY_STAINED_GLASS_PANE name: "&8" bottom-border-right: slots: [ 8, 17, 26, 35 ] material: GRAY_STAINED_GLASS_PANE name: "&8" bottom-btn-one: slot: 4 material: DIAMOND name: "&b&lBottom Button 1" lore: - "&7This button lives in your inventory area." - "&eClick →" actions: - type: PLAYER_MESSAGE message: "&bYou clicked bottom button 1!" - type: SOUND sound: ENTITY_EXPERIENCE_ORB_PICKUP hotbar-center: slot: 31 material: NETHER_STAR name: "&d&lHotbar Center" lore: - "&7This is in the hotbar row." - "&eClick →" actions: - type: TITLE title: "&d✦" subtitle: "&7You clicked the hotbar slot!" stay: 40

Per-Click-Type Actions

Assign completely different actions to different mouse buttons. Left-click can buy, right-click can preview, shift-click can bulk-buy — all on the same item.

Overview

Instead of a single actions: list (which fires for any click), use click_actions: with click-type keys. Each key holds its own independent action list.

Supported Click Types

KeyFires when
ANYAny click (same as using actions:)
LEFTLeft mouse button
RIGHTRight mouse button
SHIFT_LEFTShift + left click
SHIFT_RIGHTShift + right click
MIDDLEMiddle mouse button (scroll wheel click)
DOUBLE_CLICKDouble left-click

Structure

YAML
my-button: slot: 13 material: DIAMOND name: "&bDiamond" lore: - "&eLeft-click &7to buy ×1" - "&eRight-click &7to preview" - "&eShift+left &7to buy ×64" click_actions: LEFT: - type: CONSOLE_COMMAND command: "give {player} diamond 1" - type: SOUND sound: ENTITY_EXPERIENCE_ORB_PICKUP RIGHT: - type: PLAYER_MESSAGE message: "&7Diamond: used for tools, armor, and enchanting tables." SHIFT_LEFT: - type: CONSOLE_COMMAND command: "give {player} diamond 64" - type: SOUND sound: ENTITY_PLAYER_LEVELUP - type: TITLE title: "&b✦ Bulk Buy" subtitle: "&7×64 diamonds added!" stay: 30 MIDDLE: - type: ACTION_BAR message: "&7Middle clicked!"

click_actions vs actions

Featureactions:click_actions:
Single list for all clicks✔—
Different behavior per click type—✔
Works with conditions✔✔
Works on DYNAMIC_PAGINATION✔✔
Works in bottom_items✔✔
Mixing bothYou can use both actions: and click_actions:. The actions: list fires for ANY click. click_actions: fires for the matching type. If LEFT is defined in both, both fire on left-click.

Real-World Example: Admin Player Browser

YAML
player-list: type: DYNAMIC_PAGINATION source: ONLINE_PLAYERS slots: [ 9-44 ] material: PLAYER_HEAD name: "&e%player_name%" lore: - "&7Left: &fTeleport" - "&7Right: &fKick" - "&7Shift+L: &fBan" - "&7Middle: &fMessage" click_actions: LEFT: - type: CONSOLE_COMMAND command: "tp {player} %player_name%" - type: CLOSE_INVENTORY RIGHT: - type: CONSOLE_COMMAND command: "kick %player_name% Kicked by admin" SHIFT_LEFT: - type: CONSOLE_COMMAND command: "ban %player_name% Banned by admin" MIDDLE: - type: CHAT_INPUT chat-prompt: "&7Enter message to send to &e%player_name%&7:" - type: CONSOLE_COMMAND command: "msg %player_name% {chat_input}"

View Requirements

Hide items entirely when conditions fail — the slot appears completely empty. This is different from conditions:, which keeps the item visible but blocks the click.

View Requirements vs Conditions

Featureconditions:view-requirements:
Item visible when condition fails✔ (shows else_item)✘ (slot is empty)
Can show else_item✔✔
Checked on click✔—
Checked on every render tick—✔
Use case"Can't afford this" feedback itemHidden VIP/admin-only buttons

Structure

YAML
vip-only-item: slot: 13 material: DIAMOND_SWORD name: "&d&lVIP Sword" lore: - "&7VIP members only." view-requirements: - type: PERMISSION permission: "group.vip" # This slot is completely empty for non-VIP players

With else-item

Show a different item (e.g. a locked indicator) instead of leaving the slot empty:

YAML
secret-feature: slot: 22 material: EMERALD name: "&aSecret Feature" lore: - "&7Exclusive admin tool." view-requirements: - type: PERMISSION permission: "smartmenus.admin" else-item: material: GRAY_STAINED_GLASS_PANE name: "&8???" lore: - "&7Unlock with Admin rank."

Priority Chain — N Levels

Each else-item can have its own view-requirements and its own else-item, creating an unlimited priority chain on a single slot. SmartMenus walks the chain at render time and displays the first item whose conditions pass.

YAML
rank-display: slot: 13 material: NETHER_STAR name: "&4Admin Item" view-requirements: - type: PERMISSION permission: "group.admin" else-item: material: DIAMOND name: "&bVIP Item" view-requirements: - type: PERMISSION permission: "group.vip" else-item: material: STONE name: "&7Default Item" # No view-requirements = always shown if the above fail
Chain stepBehaviour
Top item passesTop item is displayed
Top fails → else-item passeselse-item is displayed
Both fail → else-item's else-itemWalks deeper, no limit
Chain exhaustedSlot is empty

This is equivalent to DeluxeMenus' priority system, and is what the DeluxeMenus Converter produces automatically.

Dynamic Behavior

View requirements are re-evaluated every render tick (every ~10 ticks). This means items can appear and disappear in real time as the player's state changes — no need to reopen the GUI.

Example: a button that appears only while the player is in a specific region, or only when they have enough balance:

YAML
spawn-only-btn: slot: 15 material: BEACON name: "&eSpawn Exclusive" lore: - "&7Only available at spawn." view-requirements: - type: WORLDGUARD_REGION region: "spawn"

All Condition Types Work

All 23 condition types work inside view-requirements: just like they do in conditions:. You can stack multiple — all must pass for the item to be visible.

YAML
elite-button: slot: 13 material: NETHERITE_INGOT name: "&4&lElite Reward" view-requirements: - type: PERMISSION permission: "rank.elite" - type: EXPRESSION expression: "%player_level% >= 50" - type: WORLD worlds: [ "survival" ]

In-Game GUI Editor

SmartMenus ships a fully featured in-game visual editor that lets you create and modify GUIs without ever touching a YAML file. Open it with a command, click items, type values in chat, and every change is saved to disk instantly.

Opening the Editor

Command
/smartmenus editor # opens the GUI list /ogui editor # alias

Requires the permission smartmenus.editor (default: op).

Screen Flow

The editor is a multi-screen state machine. Every action you take inside it moves you between screens:

ScreenWhat you seePurpose
GUI ListAll loaded GUIsSelect a GUI to edit, or create a new one
Layout Editor54-slot replica of the GUIClick any slot to edit that slot's item
Slot EditorProperty picker for one slotChange material, name, lore, button type, actions, conditions, etc.
Action ListAll actions on a slotView, add, reorder, or remove actions
Action EditorA single action's propertiesSet action type and parameters
Condition ListAll conditions on a slotView, add, or remove conditions
Condition EditorA single condition's propertiesSet condition type and parameters
Switch StatesAll states of a SWITCH buttonAdd/remove individual switch states
Pagination ElementsAll elements of a PAGINATION buttonAdd/remove static element definitions

Universal Controls

ActionEffect
Left-click a slot in Layout EditorOpen Slot Editor for that slot
Right-click a slot in Layout EditorClear / delete that slot entirely
Shift-click an action or conditionRemove it from the list
Click slot 53 (Back button)Return to the previous screen
Type a value when promptedSend it in chat — the editor captures it automatically
Type cancel in chatAbort any ongoing chat-input prompt
Auto-SaveEvery change in the editor is immediately written to the GUI's YAML file on disk. There is no separate "save" step.

Creating a New GUI

  1. Run /smartmenus editor.
  2. Click the lime green "New GUI" item (bottom of the list).
  3. Type the GUI ID in chat (e.g. my_gui) and press Enter.
  4. A new file is created at guis/<id>/<id>.yml and the Layout Editor opens immediately.

Permissions

PermissionDefaultDescription
smartmenus.editoropAccess to /smartmenus editor
ogui.editoropAlias permission for /ogui editor

Editor Screens Reference

Every screen in the in-game editor is documented here — what each slot does, what chat prompts appear, and what gets saved.

GUI List Screen

Opened by /smartmenus editor. Displays all currently loaded GUIs as clickable items.

  • Left-click any GUI item → open its Layout Editor.
  • Slot 53 holds a New GUI button (lime glass). Clicking it prompts you to type a new GUI ID in chat.

Layout Editor Screen

A 54-slot replica of the selected GUI. Each slot mirrors the corresponding button in the actual GUI config.

  • Left-click any slot → open the Slot Editor for that slot.
  • Right-click any slot → delete that button from the config entirely.
  • Slot 53 = Back (returns to GUI List).

Slot Editor Screen

Shown after clicking a slot. A fixed 3-row inventory displaying all editable properties of a single button:

SlotPropertyInput type
0MaterialChat: type an exact Bukkit material name (e.g. DIAMOND)
1Display NameChat: any text with & color codes
2LoreChat: lines separated by |
3Button TypeCycles through types: NONE → BACK → NEXT → PREVIOUS → JUMP → HOME → MAIN_MENU → INVENTORY → PAGINATION → SWITCH → INPUT → CHAT_INPUT → (back to NONE)
4Glow (enchant effect)Toggle: left-click to enable/disable
5Close on clickToggle: left-click to enable/disable
6ActionsClick to open the Action List screen
7ConditionsClick to open the Condition List screen
8Update (tick refresh)Toggle: left-click to enable/disable
13Type-specific param 1Context-sensitive — see table below
14Type-specific param 2Context-sensitive — see table below
15Type-specific param 3Context-sensitive — see table below
16Type-specific param 4Context-sensitive — see table below
53BackReturn to Layout Editor

Type-Specific Parameters (slots 13–16)

Button TypeSlot 13Slot 14Slot 15Slot 16
JUMPto-page (chat: page number)———
SWITCHSwitch key (chat: string)Edit States → Switch States screen——
PAGINATIONSlots string (chat: e.g. 10-16,19-25)Edit Elements → Pagination Elements screen——
INPUT——On-place actions count → Action ListRemove-on-place toggle

Action List Screen

Lists all actions attached to the current button. Each action appears as an item showing its type and parameters.

  • Shift-click any action → remove it.
  • Slot 45 = Add Action button → prompts for action type in chat, then walks through each required parameter.
  • Slot 53 = Back.

Supported action types you can add: PLAYER_COMMAND, CONSOLE_COMMAND, PLAYER_MESSAGE, BROADCAST, SOUND, OPEN_GUI, TITLE, ACTION_BAR, SERVER_CONNECT, TELEPORT, GIVE_ITEM, TAKE_ITEM, GIVE_MONEY, TAKE_MONEY, GIVE_XP, TAKE_XP, SCRIPT.

Condition List Screen

Lists all conditions attached to the current button.

  • Shift-click any condition → remove it.
  • Slot 45 = Add Condition → prompts for condition type, then walks through each parameter (e.g. amount, permission node, expression string). For parameters that can be skipped, type skip.
  • Slot 53 = Back.

All 23 condition types are available. The PLACEHOLDER_* family (EQUALS, CONTAINS, STARTS_WITH, ENDS_WITH) also accepts an ignore_case parameter — type true, false, or skip.

Switch States Screen

Accessed from the Slot Editor when the button type is SWITCH, via the "Edit States" button (slot 14).

  • Each state is displayed as an item showing the state key, material, display name, and command count.
  • Shift-click a state → remove it.
  • Slot 45 = Add State → multi-step chat prompt: key → material → display name → commands (pipe-separated).
  • Slot 53 = Back.
TipState keys become the YAML path under buttons.<state-key> in the SWITCH button config.

Pagination Elements Screen

Accessed from the Slot Editor when the button type is PAGINATION, via the "Edit Elements" button (slot 14).

  • Each element shows its title and description.
  • Shift-click an element → remove it.
  • Slot 45 = Add Element → two-step chat prompt: title → description.
  • Slot 53 = Back.

Chat Input Reference

Whenever the editor needs text input it closes the inventory and shows a prompt in chat. The input is captured from your next chat message. At any point you can type cancel to abort.

PromptExpected inputExample
MaterialBukkit enum name (case-insensitive)diamond_sword
Display NameText with & codes&b&lMy Item
LoreLines separated by |&7Line one|&7Line two
Action typeAction enum namePLAYER_COMMAND
Action parameterValue for that parametergive {player} diamond 1
Condition typeCondition enum nameVAULT_MONEY
Condition parameterValue or skip100
GUI ID (new GUI)Alphanumeric + underscoremy_cool_gui
JUMP to-pageInteger page number2
SWITCH keyString identifieron
PAGINATION slotsRange/list string10-16,19-25
State keyIdentifier stringenabled
State materialBukkit enum nameGREEN_WOOL
State nameText with color codes&a Enabled
State commandsPipe-separated commandssay on|give {player} cookie 1
Element titleText with color codes&6Quest One
Element descriptionText with color codes&7Complete the tutorial

GUI Converters

SmartMenus can automatically convert GUI configuration files from other popular menu plugins into its own format. Three converters are built in: CommandPanel, DeluxeMenus, and ZMenus.

Workflow

  1. Place the source GUI file(s) into the appropriate input folder inside the plugin data directory.
  2. Run the convert command.
  3. Find the converted .yml file in plugins/SmartMenus/guis/converted/.
  4. Run /smartmenus reload to load it, then test.

Folder Structure

Shell
plugins/SmartMenus/ ├── converter/ │ ├── commandpanel/ ← place CommandPanel .yml files here │ ├── deluxemenus/ ← place DeluxeMenus .yml files here │ └── zmenus/ ← place ZMenus inventory .yml files here └── guis/ └── converted/ ← all converted output goes here

All four directories are created automatically on first plugin start — you do not need to create them manually.

Command

Command
/smartmenus convert <type> <file> /ogui convert <type> <file>
ArgumentValuesDescription
<type>cp / commandpanel
dm / deluxemenus
zm / zmenus
Which converter to use
<file>Filename without .ymlFile inside the converter's input folder

Tab-complete works for both the type and the filename.

Examples

Command
/smartmenus convert cp my_shop /smartmenus convert dm main_menu /smartmenus convert zm rtp

Permissions

PermissionDefault
smartmenus.convertop
ogui.convertop

What Gets Converted

All three converters map the following concepts:

🏷️

Title & Rows

GUI title (with color codes) and inventory row count.

📦

Items

Material, display name, lore, glow effect, custom model data, item flags.

⚡

Actions

Player commands, console commands, messages, sounds, open GUI, server connect.

🧱

Fill Items

Glass pane border/fill items are auto-detected and mapped to fill.material.

📍

Slot Ranges

Range notation like 0-8 is expanded to individual slot numbers.

🎨

Color Codes

All §/&/hex formats are normalised to SmartMenus & codes.

After ConvertingAlways review the output file and test the GUI in-game. Complex features (conditions, per-click actions, placeholders in lore) may require manual adjustments that fall outside the scope of automatic conversion.

CommandPanel Converter

Converts GUI YAML files from the CommandPanel plugin into SmartMenus format.

Input Folder

Shell
plugins/SmartMenus/converter/commandpanel/

Usage

Command
/smartmenus convert cp <filename>

Where <filename> is the name of the file without .yml, placed in the input folder above.

CommandPanel Format

CommandPanel GUIs use a layout: section to map slot numbers to item keys, and a separate items: section for item definitions.

CommandPanel YAML
title: "&6My Shop" rows: 3 command: "myshop" aliases: [ "shop" ] items: diamond-btn: material: diamond name: "&bDiamond" lore: - "&7Click to purchase." enchanted: [ true ] actions: commands: - "[player] /buy diamond" - "[console] eco take {player} 100" layout: 13: [ diamond-btn ] fill: [ border-item ]

Conversion Mapping

CommandPanel fieldSmartMenus output
title<id>.title
rows<id>.rows
command / commands / aliases<id>.commands list
layout.fill<id>.fill.material
layout.<N><id>.items.<N>
material (lowercase)material (uppercase)
namename
lorelore
enchanted: [true]glow: true
item-model: "1234"custom_model_data: 1234
itemType: [noAttributes]item_flags: [HIDE_ATTRIBUTES]

Action Prefix Mapping

CommandPanel prefixSmartMenus action type
[chat] /cmd or [player] /cmdPLAYER_COMMAND
[console] cmdCONSOLE_COMMAND
[msg] textPLAYER_MESSAGE
[broadcast] textBROADCAST
[title] t;sTITLE (title + subtitle split on ;)
[sound] SOUNDSOUND
[open] gui_idOPEN_GUI
[close]close: true on the item
[refresh]update: true on the item
[delay] NSkipped — no equivalent

Requirement Prefix Mapping

CommandPanel requirementSmartMenus condition type
[permission] nodePERMISSION
[money] N / [vault] NVAULT_MONEY
[exp] N / [xp] NXP_LEVEL
[item] material=X amount=NITEM
[item] material=X custom=NITEM_CUSTOM_MODEL
[luckperms] group / [group] groupLUCKPERMS_GROUP

DeluxeMenus Converter

Converts GUI YAML files from the DeluxeMenus plugin into SmartMenus format.

Input Folder

Shell
plugins/SmartMenus/converter/deluxemenus/

Usage

Command
/smartmenus convert dm <filename>

DeluxeMenus Format

DeluxeMenus YAML
menu_title: "&6My Shop" open_command: myshop size: 27 items: diamond-item: slot: 13 material: DIAMOND display_name: "&bDiamond" lore: - "&7Buy for $100" glow: true hide_attributes: true left_click_commands: - "[player] buy diamond" - "[close]" right_click_commands: - "[message] &cRight-click not supported"

Conversion Mapping

DeluxeMenus fieldSmartMenus output
menu_title<id>.title
size ÷ 9<id>.rows
open_command (string or list)<id>.commands
slot / slots: [0-8, 36]<id>.items.<N> per slot
display_namename
lorelore
glow: trueglow: true
update: trueupdate: true
hide_attributes: trueitem_flags: [HIDE_ATTRIBUTES]
hide_enchants: trueitem_flags: [HIDE_ENCHANTS]
hide_potion_effects: trueitem_flags: [HIDE_ADDITIONAL_TOOLTIP]
hide_unbreakable: trueitem_flags: [HIDE_UNBREAKABLE]

Click Type Merging

DeluxeMenus distinguishes left-click, right-click, and shift variants. SmartMenus has a single actions list. The converter merges all click command lists, deduplicating by command string. Priority order: click_commands → left_click_commands → right_click_commands → shift_left_click_commands → shift_right_click_commands.

Action Prefix Mapping

DeluxeMenus prefixSmartMenus action type
[player] cmdPLAYER_COMMAND
[console] cmdCONSOLE_COMMAND
[message] textPLAYER_MESSAGE
[broadcast] textBROADCAST
[sound] SOUNDSOUND
[menu] id / [open] id / [openguimenu] idOPEN_GUI
[connect] serverSERVER_CONNECT
[title] t;sTITLE (split on ;)
[close]close: true on the item
[refresh]update: true on the item
[delay] NSkipped

Slot Priority

DeluxeMenus allows multiple items on the same slot with a priority: number — lower number = higher priority. The converter automatically handles this: items sharing a slot are sorted by priority and chained as view-requirements + else-item.

DeluxeMenus YAML (before)
items: vip-item: slot: 13 priority: 1 material: DIAMOND display_name: "&bVIP Item" view_requirement: requirements: perm: type: has permission permission: group.vip default-item: slot: 13 priority: 2 material: STONE display_name: "&7Default Item"
SmartMenus YAML (after conversion)
items: 13: material: DIAMOND name: "&bVIP Item" view-requirements: - type: PERMISSION permission: group.vip else-item: material: STONE name: "&7Default Item"

view_requirement Conversion

The view_requirement block on any item is converted to SmartMenus view-requirements. Supported requirement types:

DeluxeMenus typeSmartMenus typeFields
has permissionPERMISSIONpermission
has moneyVAULT_MONEYamount
has exp lvl / has exp levelXP_LEVELamount
has itemITEMmaterial, amount
>= / <= / == / > / < / !=PLACEHOLDERplaceholder, operator, value
string equalsPLACEHOLDER_EQUALSplaceholder, value
string containsPLACEHOLDER_CONTAINSplaceholder, value
javascript / regex matchesSkipped — not supported

Note: deny_commands inside view_requirement are intentionally ignored. SmartMenus handles failed view requirements by simply not rendering the item.

Fill Item Auto-Detection

Items that satisfy all of the following are automatically promoted to fill.material instead of being written as individual slot items:

  • Material contains GLASS_PANE
  • Assigned to more than 3 slots
  • Has no click commands of any type
  • Has no display name (or a blank one)

GUI ID Normalization

SmartMenus registers GUIs using the filename with hyphens replaced by underscores (e.g. jobs-farmer.yml → ID jobs_farmer). DM [openguimenu] commands keep the original hyphenated name. SmartMenus automatically normalizes the target ID at runtime so both forms resolve correctly — no manual editing needed.

Command Conflict Protection

If a converted menu registers a command that is already owned by another plugin (for example a DM config that listed /jobs as its open command, which conflicts with JobsReborn), SmartMenus skips that specific alias instead of overriding it. A warning is logged listing which alias was skipped. Other aliases for the same GUI are still registered normally.

ImportantIf all aliases for a GUI conflict with existing plugins, the GUI will have no registered command. Open it with /smartmenus open <gui_id> or add a non-conflicting command manually in the output YAML.

Post-Click Refresh Delay

When a button fires a PLAYER_COMMAND or CONSOLE_COMMAND and the item has update: true, SmartMenus schedules a full GUI re-render 5 ticks (250 ms) after the click. This gives the server time to process the command before PlaceholderAPI values are re-read, so the button state visually updates correctly after actions like /jobs join or /jobs leave.

ZMenus Converter

Converts inventory YAML files from the ZMenus plugin into SmartMenus format.

Input Folder

Shell
plugins/SmartMenus/converter/zmenus/

Usage

Command
/smartmenus convert zm <filename>
commands.yml is not a GUIZMenus ships a commands.yml that maps command names to inventory IDs. This file is not a GUI definition and will be rejected with an error. Convert the actual inventory YAML files (e.g. rtp.yml, shop.yml).

ZMenus Format

ZMenus YAML
name: "&6RTP Menu" size: 27 items: rtp-btn: slot: 13 item: material: COMPASS name: "&bRandom Teleport" lore: - "&7Click to teleport!" glow: true flags: [ HIDE_ATTRIBUTES ] actions: - type: 'player command' commands: [ 'rtp' ] - type: 'close' - type: 'sound' sound: ENTITY_ENDERMAN_TELEPORT

Conversion Mapping

ZMenus fieldSmartMenus output
name<id>.title
size ÷ 9<id>.rows
slot / slots: [...]<id>.items.<N> per slot
item.materialmaterial
item.namename
item.lorelore
item.glow: trueglow: true
item.flags: [...]item_flags: [...]
item.amountamount

Action Type Mapping

ZMenus action typeSmartMenus action type
player command / player_commandPLAYER_COMMAND
console command / console_commandCONSOLE_COMMAND
closeclose: true on the item
open inventory / open_inventoryOPEN_GUI
soundSOUND
messagePLAYER_MESSAGE
broadcastBROADCAST
connect / server_connectSERVER_CONNECT

Item Flag Mapping

ZMenus flagSmartMenus flag
HIDE_ATTRIBUTESHIDE_ATTRIBUTES
HIDE_ENCHANTSHIDE_ENCHANTS
HIDE_UNBREAKABLEHIDE_UNBREAKABLE
HIDE_ADDITIONAL_TOOLTIP / HIDE_POTION_EFFECTSHIDE_ADDITIONAL_TOOLTIP

Fill Item Auto-Detection

Same logic as the DeluxeMenus converter: glass pane items with more than 3 slots, no display name, and no actions are promoted to fill.material.

Head Materials

ZMenus supports custom head prefixes. The converter normalises all of them to PLAYER_HEAD:

  • head-<texture>
  • BaseHead-<texture>
  • hdb-<id> (HeadDatabase)

Bedrock / Geyser Support

SmartMenus 2.1 adds full native Bedrock support using Paper's Dialog API. Geyser automatically translates Paper Dialog packets into Bedrock-native forms — no Geyser-specific code, no Cumulus, no extra setup beyond installing Geyser.

How It Works

When a Bedrock player opens a GUI, SmartMenus detects them and opens a Paper Dialog instead of a chest inventory:

  1. Paper sends a Dialog packet to the client
  2. Geyser intercepts it and converts it to a Bedrock-native form packet automatically
  3. The Bedrock player sees a native mobile-friendly form — no chest inventory
  4. Java players see the normal chest inventory — nothing changes for them
Zero extra codeYou do not need to write Bedrock-specific logic. Add auto-convert: true to a GUI and Bedrock players automatically get a native dialog. Java players are completely unaffected.

Requirements

RequirementVersionNotes
Paper1.21.5+Dialog API is Paper-only
Geyser-SpigotAny recentHandles Java→Bedrock packet translation
Floodgate2.2+Optional but recommended — improves Bedrock detection reliability
Runs without Bedrock supportSmartMenus works on any Paper/Spigot 1.16+ server. If Geyser and Floodgate are absent, all Bedrock features are simply skipped — nothing breaks.

Bedrock Player Detection

SmartMenus detects Bedrock players in two ways, in priority order:

  1. Floodgate API — FloodgateApi.getInstance().isFloodgatePlayer(uuid) when Floodgate is installed (most reliable)
  2. UUID prefix fallback — UUIDs starting with 00000000-0000-0000- are Bedrock players even without Floodgate

Two Modes

⚡

Auto-Convert

Add auto-convert: true to any existing GUI. SmartMenus builds a Bedrock dialog automatically from your items. Zero extra YAML.

🎛️

Explicit Bedrock Block

Add a bedrock: section for full control — custom buttons with images, inputs, dropdowns, sliders, NPC dialogues.

Java Compatibility

Both modes are fully transparent to Java players. The auto-convert flag and the bedrock: block are only used when the opening player is detected as a Bedrock player. Java players always see the normal chest inventory.

Next Steps

  • Auto-Convert — the fastest way to add Bedrock support to existing GUIs
  • Form Types — SIMPLE_FORM, MODAL_FORM, CUSTOM_FORM, DIALOGUE reference

Auto-Convert

Add one line to any GUI and Bedrock players automatically get a native dialog built from your items. No additional YAML required.

Enabling It

YAML
my_shop: title: "&6&l💰 Shop" rows: 3 commands: [ "shop" ] auto-convert: true # ← that's all items: sword: slot: 10 material: DIAMOND_SWORD name: "&b&lDiamond Sword" lore: - "&7A sharp blade." - "" - "&ePrice: &a$500" - "&7Click to purchase!" conditions: - type: VAULT_MONEY amount: 500 commands: - "give {player} diamond_sword 1" close: true pickaxe: slot: 12 material: DIAMOND_PICKAXE name: "&b&lDiamond Pickaxe" lore: - "&7Mine faster." - "" - "&ePrice: &a$750" conditions: - type: VAULT_MONEY amount: 750 commands: - "give {player} diamond_pickaxe 1" close: true close-btn: slot: 22 material: BARRIER name: "&c&lClose" close: true

Java players see the normal chest inventory. Bedrock players see a SIMPLE_FORM dialog with buttons automatically generated from the items above.

What Gets Converted

Auto-convert scans all items sorted by slot number and builds a button list:

Item propertyBecomes
nameButton label (first line)
First 3 lore linesAdditional label lines below the name
conditions:Checked before the button's actions fire
actions: / commands:Run when the button is tapped
close: trueCloses the form after actions fire
view-requirements:Button is hidden entirely if conditions fail

What Gets Skipped

Items with no actions and no commands (decorative fillers, glass pane borders) are automatically excluded — they would produce useless buttons with nothing to do.

Dialog Title

The dialog uses the GUI's title: value. Color codes are converted automatically.

Fallback

If auto-convert: true is set but the player is not on Bedrock (or Geyser/Floodgate are absent), the normal Java inventory opens — no change in behavior.

Overriding with an Explicit Bedrock Block

If you want more control (images on buttons, a content message, custom button order), add a bedrock: block. When both auto-convert: true and bedrock: are present, the explicit bedrock: block takes priority.

YAML
my_shop: title: "&6Shop" rows: 3 auto-convert: true # used as fallback if bedrock: is removed bedrock: # ← this takes priority type: SIMPLE_FORM title: "&6&l💰 Shop" content: "&7Choose an item:" buttons: - text: "&b&lDiamond Sword\n&7Price: $500" image-type: path image-data: textures/items/diamond_sword conditions: - type: VAULT_MONEY amount: 500 actions: - type: CONSOLE_COMMAND command: "give {player} diamond_sword 1" - type: CLOSE_INVENTORY items: # ... your normal Java inventory items ...

All Default GUIs

Every GUI that ships with SmartMenus (34 total) has auto-convert: true set out of the box. Bedrock players can use every default GUI on a fresh install with zero configuration.

Bedrock Form Types

When you add an explicit bedrock: block to a GUI, you choose one of four form types. Each maps to a different Bedrock UI.

Form Type Overview

TypeBedrock UIBest For
SIMPLE_FORMScrollable button list with optional imagesShops, menus, navigation
MODAL_FORMTwo-button confirmation dialogConfirm/cancel prompts
CUSTOM_FORMInput form with text fields, dropdowns, sliders, togglesSettings, preferences, name entry
DIALOGUENPC speech bubble with button listNPC conversations, quest dialogs

SIMPLE_FORM

A scrollable list of buttons. Each button can have an image (path or URL) and its own conditions and actions.

YAML
bedrock: type: SIMPLE_FORM title: "&6&l💰 Money Shop" content: "&7Choose an item to purchase:" buttons: - text: "&b&lDiamond Sword\n&7Price: &a$500" image-type: path image-data: textures/items/diamond_sword conditions: - type: VAULT_MONEY amount: 500 actions: - type: CONSOLE_COMMAND command: "give {player} diamond_sword 1" - type: CLOSE_INVENTORY - text: "&b&lDiamond Pickaxe\n&7Price: &a$750" image-type: path image-data: textures/items/diamond_pickaxe conditions: - type: VAULT_MONEY amount: 750 actions: - type: CONSOLE_COMMAND command: "give {player} diamond_pickaxe 1" - type: CLOSE_INVENTORY - text: "&c&lClose" actions: - type: CLOSE_INVENTORY

SIMPLE_FORM Properties

KeyDescription
titleForm title. Supports & color codes.
contentText displayed above the button list.
buttonsList of button definitions (see below).

Button Properties

KeyDescription
textButton label. Use \n for line breaks within the label.
image-typepath (resource pack path) or url (image URL).
image-dataThe path or URL value. Example: textures/items/diamond_sword
conditionsStandard SmartMenus condition list. All must pass for actions to fire.
actionsStandard SmartMenus action list. Run when the button is tapped and conditions pass.

MODAL_FORM

A two-button dialog: one confirm button and one deny button. Ideal for purchase confirmations and yes/no prompts.

YAML
bedrock: type: MODAL_FORM title: "&c&lConfirm Purchase" content: "&7Are you sure you want to buy a Diamond Sword for &a$500&7?" confirm-button: "&a✔ Yes, buy it" deny-button: "&c✘ No, cancel" confirm-actions: - type: CONSOLE_COMMAND command: "give {player} diamond_sword 1" - type: PLAYER_MESSAGE message: "&aPurchase complete! You got a Diamond Sword." - type: CLOSE_INVENTORY deny-actions: - type: PLAYER_MESSAGE message: "&7Purchase cancelled." - type: CLOSE_INVENTORY

MODAL_FORM Properties

KeyDescription
titleDialog title.
contentMain message body.
confirm-buttonLabel for the confirm/yes button.
deny-buttonLabel for the deny/no button.
confirm-actionsActions fired when the player taps the confirm button.
deny-actionsActions fired when the player taps the deny button.

CUSTOM_FORM

A form with free-form inputs: text fields, dropdowns, sliders, step sliders, toggles, and labels. Values are captured as variables accessible in submit actions.

YAML
bedrock: type: CUSTOM_FORM title: "⚙ Player Preferences" inputs: - key: display_name type: INPUT label: "Display Name" placeholder: "Enter a name..." max-length: 24 - key: gamemode type: DROPDOWN label: "Preferred Gamemode" options: [ "Survival", "Creative", "Adventure" ] default: 0 - key: walk_speed type: SLIDER label: "Walk Speed" min: 1 max: 10 step: 1 default: 5 - key: pvp_enabled type: TOGGLE label: "Enable PvP" default: false - key: notice type: LABEL label: "&7Changes apply immediately." submit-actions: - type: CONSOLE_COMMAND command: "nick {player} %bedrock_input_display_name%" - type: PLAYER_MESSAGE message: "&aPreferences saved! Name: &f%bedrock_input_display_name%"
Input VariablesEach input's value is stored as %bedrock_input_<key>% and available in all submit actions. For example, an input with key: display_name becomes %bedrock_input_display_name%.

CUSTOM_FORM Properties

KeyDescription
titleForm title.
inputsList of input definitions (see below).
submit-actionsActions fired when the player submits the form.

Input Types

TypeDescriptionSpecific fields
INPUTSingle-line text fieldplaceholder, default (text), max-length
DROPDOWNDrop-down option listoptions (list of strings), default (index, 0-based)
SLIDERNumeric slidermin, max, step, default (float)
STEP_SLIDERSlider that snaps to labelled stepsoptions (list of step labels), default (index)
TOGGLEOn/off toggle switchdefault (true/false)
LABELNon-interactive text labelnone — uses label as display text

Common Input Fields

KeyDescription
keyIdentifier used in %bedrock_input_<key>%. Defaults to the lowercased label if omitted.
typeInput type (see table above).
labelText shown next to the input field.

DIALOGUE

An NPC speech bubble with a button list. The npc-name appears as the speaker's name above the dialog.

YAML
bedrock: type: DIALOGUE title: "&6Old Merchant" content: "&7Ahh, a traveler! What brings you to my humble shop today?" npc-name: "Old Merchant" buttons: - text: "Browse Wares" actions: - type: OPEN_INVENTORY inventory: shop_main - text: "I need repairs" actions: - type: OPEN_INVENTORY inventory: repair_shop - text: "Never mind" actions: - type: CLOSE_INVENTORY

DIALOGUE Properties

KeyDescription
titleDialog title (shown at the top).
contentThe NPC's spoken text.
npc-nameSpeaker name shown above the dialog. Usually the NPC's name.
buttonsList of response buttons. Same format as SIMPLE_FORM buttons.
NPC BindingYou can bind any GUI to a ModeledNPC using npc_id: at the GUI level. When the NPC is right-clicked, the DIALOGUE form opens automatically for Bedrock players.

Placeholder Support

All text fields in Bedrock forms support:

  • & color codes — converted automatically
  • {player} — replaced with the player's name
  • %placeholder% — any PlaceholderAPI placeholder
  • %bedrock_input_<key>% — CUSTOM_FORM input values (in submit actions)

Changelog

Release history for SmartMenus.

v2.4

Bug Fixes

  • Fixed: OPEN_GUI action not working after DM conversion
    Clicking navigation buttons converted from DeluxeMenus [openguimenu] commands did nothing. The OPEN_GUI action type was missing from the internal action parser and was silently discarded. Now correctly opens the target GUI.
  • Fixed: GUI ID hyphen/underscore mismatch
    GUIs are registered with underscores derived from their filename (e.g. jobs-farmer.yml → jobs_farmer), but DM configs reference them with hyphens ([openguimenu] jobs-farmer). SmartMenus now normalizes the target ID at lookup time so both forms resolve correctly.
  • Fixed: SmartMenus overriding other plugins' commands
    When a converted GUI registered a command alias already owned by another plugin (e.g. /jobs owned by JobsReborn), SmartMenus forcibly stole it — causing the original plugin's command to stop working entirely. Player command actions like jobs join miner would then re-open the GUI instead of running the intended command. SmartMenus now skips conflicting aliases and logs a warning instead of overriding.
  • Fixed: Script conditions always returning false on Java 17+ servers
    Nashorn (the built-in JavaScript engine) was removed in Java 15. Script conditions such as %jobsr_user_joinedjobcount% == %jobsr_maxjobs% always evaluated to false on modern servers without GraalVM. SmartMenus now includes a built-in fallback expression evaluator (after PAPI resolution) supporting ==, !=, >=, <=, >, < for both numeric and string values. Full JavaScript still requires GraalVM.
  • Fixed: GUI not refreshing visually after a click fires a command
    When a button with update: true fired a player command (e.g. /jobs join miner), the GUI re-rendered immediately in the same tick — before the server had processed the command — so PlaceholderAPI values hadn't changed yet and the button appeared to do nothing. The GUI now waits 5 ticks (250 ms) after the click before re-rendering.

v2.3 and earlier

See the plugin's Spigot resource page for older release notes.