✨ Features • 📦 Download • 📥 Installation • 📋 Commands • 🚀 Quick Start • 📁 Files • 🌍 i18n • ⚙️ GUI • ⚡ Actions • ⌨️ Smart Mechanisms • ⌨️ Custom Commands • 🎣 Catchers • 🧩 API • 📡 PAPI • 🔨 Build
📖 中文文档请见 README_zh.md
|
🧱 Layout System — Define GUI layout with character art, intuitive and flexible |
🔍 Smart Detection — PAPI expansion dependency check, placeholder scanning, config typo hints |
You may obtain the JAR through non-paid methods, but please do not redistribute without permission, and do not obtain this resource through unauthorized channels.
Choose one of the following methods to obtain the plugin JAR:
📦 Expand
| Method | Description |
|---|---|
| 🧧 Paid | Download the pre-built JAR directly |
| 🔨 Free | Clone the source and build yourself |
Spend ¥1 RMB to get the latest pre-built JAR file:
🔗 https://www.ifdian.net/item/c88c27ca43e111f1915752540025c377
Build from source:
💡 Requires Java 8+, Gradle, and an internet connection to download dependencies.
📋 Expand
| Plugin | Required | Description |
|---|---|---|
| TabooLib | ✅ | Core framework, bundled in releases |
| PlaceholderAPI | 💡 Recommended | Placeholder resolution |
| Vault | ⚪ Optional | Economy operations |
| ItemsAdder / Oraxen / CraftEngine | ⚪ Optional | Custom item materials |
🎈 You can download and install this plugin directly without any dependencies.
1. Place XPListGUI.jar in the plugins/ folder
2. Restart the server. If Plugman is installed, try /plugman load XPListGUIHot-reloading has risks. It is recommended to restart the server to load the plugin.
📋 Expand
| Command | Description | Permission |
|---|---|---|
/xplistgui help |
Display help | xplistgui.admin.help |
/xplistgui list |
List all GUIs (with hover details) | xplistgui.admin.list |
/xplistgui create <name> |
Create a new GUI from template | xplistgui.admin.create |
/xplistgui delete <name> |
Delete a GUI (with confirmation) | xplistgui.admin.delete |
/xplistgui open <name> [-s] |
Open a GUI | xplistgui.admin.open |
/xplistgui reload |
Reload all configs and commands | xplistgui.admin.reload |
/xplistgui papiinstall <exp> |
Install a PAPI expansion | xplistgui.admin.papiinstall |
💡 Aliases:
/xplgui/plgui/xplg/xplistg/plistg🔑 Master permission
xplistgui.admingrants all sub-nodes. Each GUI'spermissionfield controls access — leave empty for public.
The open command has two usages:
# When GUI config has errors, debug info will be displayed
/xplistgui open example
# When GUI config has errors, debug info will NOT be displayed
/xplistgui open example -s📦 Expand to view steps
# 1. Create your first GUI
/xplistgui create example
# 2. Open the GUI
/xplistgui open exampleEdit the GUI config file at: plugins/XPListGUI/lists/example.yml
💡 Tips
If the GUI config has errors, using/xplistgui open <GUI name>will display detailed debug info in chat for easy troubleshooting.
Add -s after/xplistgui open <GUI name>to skip smart detection (no debug info displayed).
For details, see Smart Mechanisms and Commands & Permissions.
Once the plugin loads, the plugins/XPListGUI/ directory contains the following files:
📁 Expand file reference
language: "zh_CN" # Plugin display language (10 supported)
debug: false # Console debug log toggle
debug-fake-players: 30 # Fake players for pagination testing (0 to disable)
catcher-timeout: 60000 # Chat catcher timeout in millisecondsChanges take effect after /xplistgui reload — no restart needed.
Stores all plugin messages in 10 languages. Auto-generated on first load. Edit any message freely and /xplistgui reload to apply.
|
|
Falls back to Message.yml when the target language file does not exist.
Each GUI corresponds to a .yml file. Generate from template via /xplistgui create.
See GUI Configuration below.
Map custom commands to GUIs. See Custom Commands.
Set the language in config.yml:
language: "zh_CN" # Choose from 10 supported languages belowLanguage files are located in the plugins/XPListGUI/lang/ directory. All messages are configurable.
🌍 Supported Languages
| Code | Language |
|---|---|
zh_CN |
简体中文 (Simplified Chinese) |
zh_TW |
繁體中文 (Traditional Chinese) |
en_US |
English |
ja_JP |
日本語 (Japanese) |
ko_KR |
한국어 (Korean) |
fr_FR |
Français (French) |
de_DE |
Deutsch (German) |
es_ES |
Español (Spanish) |
pt_BR |
Português (Portuguese) |
ru_RU |
Русский (Russian) |
Each GUI is a .yml file under the lists/ directory.
Create one with /xplistgui create <name> from the template.
📋 Field Reference
| Field | Type | Description |
|---|---|---|
title |
String | GUI title, supports & color codes and PAPI placeholders |
size |
Number | GUI size, must be a multiple of 9 (9/18/27/36/45/54) |
permission |
String | Permission required to open this GUI, empty = public |
open-actions |
List | Actions executed when GUI is opened (sound, message, etc.) |
show-online-players |
Bool | Show online player heads |
show-offline-players |
Bool | Show offline player heads |
show-self |
Bool | Include the viewer in the player list |
head-blacklist |
List | Player names to exclude (case-sensitive) |
player-slot-char |
Char | Player head placeholder, Layout mode only |
page-button-prev |
Char | Previous page button char, Layout mode only |
page-button-next |
Char | Next page button char, Layout mode only |
layout |
List | Character grid layout, Layout mode only |
items |
Map/List | Item definitions: character keys (Layout) or slot list (Static) |
catchers |
Map | Chat catcher definitions, see Catchers |
🧱 Layout Mode (Recommended)
player-slot-char: "P"
page-button-prev: "<"
page-button-next: ">"
layout:
- "#########"
- "#PPPPPPP#"
- "#PPPPPPP#"
- "###<#>###"
items:
'#': # Border
enabled:
material: BLACK_STAINED_GLASS_PANE
name: " "
'P': # Player head
player-head: true
name: '{player}'
lore:
- "&7Click to view"
actions:
- 'tell: &aYou clicked &e{player}'
'<': # Page button
enabled:
material: ARROW
name: "&ePrevious"
disabled:
material: BARRIER
name: "&cFirst page reached"Setting
materialto"HEAD:<player-name>"displays the head of the specified player (supports online search for premium accounts).
{player}is auto-replaced with the head owner's name, only valid whenplayer-head: true.
Page buttons require both
enabled(usable) anddisabled(boundary) materials. Key names must be exact (enabled/disabled, notenable/disable).
📍 Static Slot Mode
Without layout, use slot numbers directly:
items:
- slot: 22
material: DIAMOND
name: "&bWelcome"
amount: 1
lore:
- "&7Click to open shop"
actions:
- 'tell: &aHello!'
- 'open: shop'🎒 Third-party Item Materials
The material field can directly call ItemsAdder / Oraxen / CraftEngine custom items:
material: "itemsadder:namespace:item_id" # ItemsAdder
material: "oraxen:item_id" # Oraxen
material: "craftengine:namespace:item_id" # CraftEngineActions are behaviors triggered when clicking an item, executed top-to-bottom in order. Format: type:content.
📋 Action Table
| Category | Action | Format | Example |
|---|---|---|---|
| Command | console |
console:<cmd> |
console:give {player} diamond 1 |
| Command | player / p |
player:<cmd> |
player:spawn |
| Command | op |
op:<cmd> |
op:gamemode creative {player} |
| Message | tell / msg |
tell:<msg> |
tell:&aHello {viewer} |
| Message | tell-<player> |
tell-<player>:<msg> |
tell-{player}:&eSomeone viewed you |
| Message | title |
title:<main>,<sub>,<fadeIn>,<stay>,<fadeOut> |
title:&6Congrats!,&eReward received,10,60,20 |
| Message | actionbar / ab |
actionbar:<msg> |
actionbar:&aBalance: 1000 coins |
| Sound | sound / play |
sound:<sound> |
sound:ENTITY_PLAYER_LEVELUP |
| Flow | delay / wait |
delay:<ticks> |
delay:60 |
| Flow | return |
return |
Stop all subsequent actions immediately |
| Flow | close |
close |
Close the current GUI |
| Navigation | open |
open:<GUI> |
open:shop |
| Navigation | connect / server |
connect:<server> |
connect:lobby |
| Variable | set |
set:<key>=<value> |
set:target={player} |
| Economy | give-money |
give-money:<amount> |
give-money:100 |
| Economy | give-money-<player> |
give-money-<player>:<amount> |
give-money-{player}:100 |
| Economy | take-money |
take-money:<amount> |
take-money:50 |
| Economy | take-money-<player> |
take-money-<player>:<amount> |
take-money-{player}:100 |
| Economy | eco-set |
eco-set:<amount> |
eco-set:1000 |
| Economy | eco-set-<player> |
eco-set-<player>:<amount> |
eco-set-{player}:0 |
| Economy | eco-balance |
eco-balance |
Check current balance |
| Economy | eco-balance-<player> |
eco-balance-<player> |
eco-balance-{player} |
| Capture | catch |
catch:<catcher> |
catch:transfer-amount |
🔄 Available Variables
Variables that can be embedded in action content:
| Variable | Description |
|---|---|
{viewer} |
Name of the player who triggered the action |
{player} |
Name of the clicked head's owner (same as {viewer} in regular GUIs) |
{target} |
Same as {player} |
{online} |
Current server online player count |
{meta:key} |
Temporary variable value stored via set, can be passed across menus |
📖 Full action reference: docs/actions-reference.md
Use condition for "only execute if", with unlimited nesting:
📋 Expand for example
actions:
- 'tell: &bNormal action'
- condition: 'perm *vip.use'
actions:
- 'tell: &aYou are VIP!'
- condition: 'perm *admin.use'
actions:
- 'tell: &6Super admin'
deny:
- 'tell: &cNot an admin'
deny:
- 'tell: &cVIP required'| Syntax | Meaning |
|---|---|
perm *node |
Has permission → execute actions |
!perm *node |
Lacks permission → execute actions |
| (empty) | Always true (else fallback) |
Condition met →
actions. Not met →deny.denyis optional, skips if omitted.
📋 Expand for example
actions:
left:
- 'tell: &bLeft click'
right:
all:
- 'tell: &eRight click'
- 'open: detail-menu'Without
left/right, both clicks trigger. Fully backward compatible. All actions underleftexecute simultaneously.rightsupportsall(right + shift-right) andshift(shift-right only) groups.
🧠 Expand
Blocking Type
Add the depend-expansions field in the GUI config, for example:
depend-expansions:
- Vault
- Player
If the Vault and Player PAPI expansions are not installed, the GUI cannot be opened.
Hint Type
| Category | Detection Logic | Report Behavior |
|---|---|---|
| ① Known PAPI Expansion | Prefix matching against mapping table (20+) | Hints missing expansion + provides [Install] button |
| ② PAPI Built-in Expansion | Matches built-in expansions like statistic |
Hints "no install needed, bundled with PAPI" but still fails to resolve |
| ③ Registered but Unresolved | Prefix exists in currently registered PAPI placeholders on the server | Hints "registered but unresolved", may be a custom placeholder from another plugin, manual check recommended |
| ④ Unrecognized Source | Not in known mapping table, not registered in PAPI | Hints "unrecognized", suggests checking spelling |
| ⑤ PAPI Not Installed | Hints to install PlaceholderAPI plugin |
🔍 Expand
When there are errors in the GUI config, using the open command without the -s flag will automatically detect and prompt error messages.
| Category | Check | Level |
|---|---|---|
| Basic Fields | GUI title is empty | ⚠ Warning |
| Layout Validation | Character used in layout but not defined in items | ⚠ Warning |
| Character defined in items but not used in layout | 💡 Hint | |
| Layout exceeds 6 rows (max 54 slots) | ⚠ Warning | |
| A row exceeds 9 slots | ⚠ Warning | |
| Layout is entirely empty | ⚠ Warning | |
| Pagination | Only prev page without next page (or vice versa) | ⚠ Warning |
| Page button char configured but not defined in items | ⚠ Warning | |
enable typo (should be enabled) |
⚠ Warning | |
disable typo (should be disabled) |
⚠ Warning | |
Has enabled sub-block but missing disabled sub-block |
⚠ Warning | |
Has disabled sub-block but missing enabled sub-block |
⚠ Warning | |
Missing both enabled and disabled sub-blocks |
⚠ Warning | |
| Page button char does not appear in layout | ⚠ Warning | |
| Prev and next page use the same character | ⚠ Warning | |
| Player Heads | Both online and offline players are not displayed | ⚠ Warning |
| Head char configured but not defined in items | ⚠ Warning | |
Missing player-head flag |
⚠ Warning | |
| Head char does not appear in layout | ⚠ Warning | |
| Head char conflicts with page button char | ⚠ Warning | |
| Item Material | enable/disable general typo |
⚠ Warning |
| Item type is not a valid material name | ⚠ Warning | |
Material in enabled/disabled sub-block is invalid |
⚠ Warning | |
| Permission Check | No-permission message set but no permission node configured | 💡 Hint |
| Action Check | Target GUI file referenced by open action does not exist |
⚠ Warning |
🔔 Expand
- When a player joins or leaves the server, all online players who have this plugin's GUI open will automatically refresh
While a player has the GUI open and another player joins or leaves, the player heads in the GUI will update accordingly.
- When refreshing, the current page is preserved, open-actions (sounds/messages) are not triggered, and no detection hints pop up — only a silent re-render
If a player leaves the server while another player is in a catcher input state, the catcher operation is immediately cancelled.
- When players join/leave frequently, multiple refresh requests within 500ms are merged into one to avoid performance waste
⏳ Expand
Head skin loading uses a three-tier strategy:
Tier 1
- If the target player is currently on the server, read their character profile directly from the server internals to get skin texture data
- Advantage: if the player changed their skin via SkinsRestorer, the latest skin is retrieved without additional queries
- For cross-version compatibility, multiple read paths are prepared internally — trying the standard method first, then indirect methods, and even multiple possible field names
Tier 2
- If the player is offline but the server has SkinsRestorer installed, query SkinsRestorer's local storage
- For compatibility with different SkinsRestorer versions (15.x, 14.x, older), three sets of API interfaces are attempted — whichever works is used
Tier 3
- If the first two tiers fail (e.g., premium player on an offline server), fall back to the Mojang official API:
1. Look up UUID by player name
2. Look up skin data by UUID - This process requires network requests and may take hundreds of milliseconds to two seconds, so it runs fully async without blocking the main thread. The GUI displays a placeholder head first, and the skin is swapped in once retrieved
⚡ Cache Mechanism
- All retrieved skins are cached, storing up to 500 entries with a 30-minute expiry. Uses an LRU (Least Recently Used) eviction policy — when the limit is exceeded, the least recently used cache entries are automatically cleared
- This means the same player appearing repeatedly in the list doesn't require repeated queries — instant retrieval
Register custom commands in plugins/XPListGUI/commands.yml to open GUIs directly — players type your command and the corresponding menu opens instantly.
📋 Field Reference
| Field | Type | Description |
|---|---|---|
name |
String | Required, command name (without /) |
aliases |
List | Command aliases, register multiple shortcuts |
permission |
String | Permission node required, empty = public |
permission-message |
String | Message shown when lacking permission, supports & color codes |
gui |
String | Required, GUI name (matches a file in lists/, without .yml) |
subcommands |
Map | Sub-commands for multi-level menus under one command |
📋 Basic Example
commands:
menu:
name: "menu"
aliases: ["cd", "gui"]
gui: "example" # /menu opens the example GUI
vip:
name: "vip"
permission: "xplistgui.vip"
permission-message: "&cVIP only"
gui: "vip_menu" # /vip opens the vip_menu GUI📋 Subcommands (Multi-level Menus)
Use subcommands to extend one command into multiple child entries:
commands:
servermenu:
name: "server"
aliases: ["sm"]
gui: "server_main" # /server → main menu
subcommands:
shop: # /server shop → shop GUI
gui: "shop"
pvp: # /server pvp → pvp GUI
permission: "server.pvp"
gui: "pvp"💡 GUIs opened via custom commands automatically skip smart detection hints — no
-sneeded.
Use catch:<catcher-name> in actions to close the GUI and wait for the player to type in chat — enabling multi-step "click → type → execute" workflows.
📋 How It Works
1. Player clicks item → triggers catch:xxx action
2. GUI closes → start phase executes (prompts the player)
3. Player types in chat → end phase triggers
4. Player types cancel keyword → cancel phase triggers
5. Timeout expires → auto-cancels to prevent getting stuck
The typed value is read via the {meta:input} variable and can be used in all subsequent actions.
📋 Configuration Example
Define the catcher (in the GUI config's catchers section):
catchers:
transfer-amount:
timeout: 60000 # Timeout in ms, default 60000 = 1 min
start: # Executed when entering capture mode
- 'close'
- 'tell: &eEnter transfer amount (&7type C to cancel&e)'
cancel: # Executed when player cancels
- 'tell: &cCancelled'
end: # Executed after player inputs
condition: "input == C" # Condition: input "C" → actions, else → deny
actions:
- 'tell: &cCancelled'
deny:
- 'tell: &aYou entered: {meta:input}'Trigger in item actions:
actions:
- 'catch:transfer-amount'📋 Field Reference
| Field | Description |
|---|---|
timeout |
Timeout in ms, auto-cancels when expired, default 60000 |
start |
Action list executed when entering catch mode (usually close + tell prompt) |
cancel |
Action list executed when player inputs the cancel keyword |
end |
Action executed after player input, supports condition branching |
{meta:input} |
The text the player typed, available in end.deny |
Use
"input == xxx"inend.conditionto check if the typed text matches the cancel keyword.
Kotlin
import org.example.xplistgui.api.XPApi
XPApi.openGUI(player, "shop")
XPApi.openGUISilent(player, "vip_menu")
XPApi.openGUIPage(player, "players", 3)
val guis: List<String> = XPApi.getGuiList()Java
import org.example.xplistgui.api.XPApi;
XPApi.INSTANCE.openGUI(player, "shop");
XPApi.INSTANCE.openGUISilent(player, "vip_menu");📋 Expand for placeholder list
| Placeholder | Description | Example Output |
|---|---|---|
%xplistgui_gui_count% |
Number of registered GUIs | 5 |
%xplistgui_gui_list% |
GUI name list | example, shop |
%xplistgui_has_gui_<name>% |
Whether a GUI exists | true |
%xplistgui_online_count% |
Server online player count | 12 |
%xplistgui_head_count_<name>% |
Total head count in a GUI | 28 |
📦 Expand for build commands
# Release version
./gradlew build
# Output: build/libs/XPListGUI-1.2.jar
# Development version
./gradlew taboolibBuildApi -PDeleteCodeThis project is open-sourced under the CC BY-NC-ND 4.0 license. See LICENSE for details.
Made with ❤️ by XyLuoDys