From 1b0614d116c7f650f2a9dd49afc84b564b912343 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 16 May 2026 13:48:05 +0000 Subject: [PATCH] docs: update README, add SECURITY + CONTRIBUTING, update example datapack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README: - Remove outdated dialog GUI section (type not supported) - Document all fields: click_type, glint, condition, actions, run_with, paging - Add examples: minimal button, left/right on same slot, conditional button, multi-page GUI, console command - Update commands table (reload, help added) - Update architecture section (modmenu package) - Fix slot typo in multi-page example SECURITY.md: - Threat model: run_with console, command injection, state map, slot protection - Reporting process via GitHub Security Advisories - Known non-issues CONTRIBUTING.md: - Dev setup, code style, PR guidelines, bug report and feature request format Example datapack: - admin_panel.json: legacy action→actions, add left/right click_type on slot 12, add shift+condition+glint admin tools button, run_with console - sub_menu.json: legacy action→actions, run_with console, feedback messages - welcome_dialog.json: removed (dialog type is unsupported, registry skips it) - welcome.json: new barrel-format welcome screen with rules accept/kick flow - showcase.json: new — demonstrates every feature: click_type (left/right/shift), glint, has_tag condition, score_gt condition, multi-action, run_with console, 2-page navigation - pack.mcmeta: updated description --- CONTRIBUTING.md | 75 ++++++ README.md | 240 ++++++++++++------ SECURITY.md | 55 ++++ .../data/example/gui/admin_panel.json | 56 ++-- .../data/example/gui/showcase.json | 144 +++++++++++ .../data/example/gui/sub_menu.json | 26 +- .../data/example/gui/welcome.json | 32 +++ .../data/example/gui/welcome_dialog.json | 17 -- example-datapack/pack.mcmeta | 2 +- 9 files changed, 521 insertions(+), 126 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 SECURITY.md create mode 100644 example-datapack/data/example/gui/showcase.json create mode 100644 example-datapack/data/example/gui/welcome.json delete mode 100644 example-datapack/data/example/gui/welcome_dialog.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..29ce4bb --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,75 @@ +# Contributing to GUI API + +Thanks for your interest. This document covers how to contribute code, report bugs, and suggest features. + +--- + +## Before You Start + +- Check [open issues](https://github.com/ToolkitMC/guiAPI/issues) to avoid duplicate work. +- For security vulnerabilities, see [SECURITY.md](SECURITY.md) — do not open a public issue. +- For large changes, open an issue first to discuss the approach. + +--- + +## Development Setup + +**Requirements:** Java 21, Git. + +```bash +git clone https://github.com/ToolkitMC/guiAPI.git +cd guiAPI +./gradlew build +``` + +The built jar is at `build/libs/guiapi-1.0.0.jar`. + +To run in a local Minecraft instance, use Fabric's `runServer` task or drop the jar into a test server's `mods/` folder. + +--- + +## Code Style + +- **Java 21** — records, switch expressions, and sealed types are welcome. +- **Indentation:** 4 spaces, no tabs. +- **Line length:** soft limit of 120 characters. +- **Naming:** camelCase for methods/fields, PascalCase for classes, UPPER_SNAKE for constants. +- **Comments:** English only. Explain *why*, not *what*. +- All server-side code lives under `src/main`. Client-side code (Mod Menu integration) is `modCompileOnly` — guard with `@Environment(EnvType.CLIENT)` where necessary. +- Do not add runtime dependencies beyond Fabric API without discussion. + +--- + +## Pull Requests + +1. Fork the repo and create a branch: `feat/` or `fix/`. +2. Keep commits focused. One logical change per commit. +3. Write a clear PR description: what changed, why, and how to test it. +4. Update `README.md` and the example datapack if your change affects the JSON schema or commands. +5. Target the `main` branch. + +PRs that break backwards compatibility in the JSON schema require a version bump discussion. + +--- + +## Reporting Bugs + +Open a [GitHub issue](https://github.com/ToolkitMC/guiAPI/issues/new) with: + +- GUI API version and Minecraft version +- Fabric Loader and Fabric API version +- The GUI JSON that triggers the bug (trimmed to the minimal reproducer) +- Expected behaviour vs actual behaviour +- Server log / crash report if applicable + +--- + +## Suggesting Features + +Open a [GitHub issue](https://github.com/ToolkitMC/guiAPI/issues/new) tagged `enhancement`. Include a JSON example of how the feature would look from a datapack author's perspective. + +--- + +## License + +By contributing, you agree that your contributions are licensed under the project's [MIT License](LICENSE). diff --git a/README.md b/README.md index e3b90fa..a2077b9 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,29 @@ # GUI API — Fabric 1.21.1 -A Fabric mod that lets datapacks define and open barrel GUIs or custom dialog screens via JSON files. -No macros. No external dependencies beyond Fabric API. +A Fabric mod that lets datapacks define and open chest GUIs via JSON files. +No client mod required. No macros. No external dependencies beyond Fabric API. --- ## Installation 1. Drop `guiapi-1.0.0.jar` into your `mods/` folder. -2. Drop the `example-datapack/` folder (or your own datapack) into `world/datapacks/`. -3. Run `/reload`. +2. Drop your datapack into `world/datapacks/`. +3. Run `/reload` or `/guiapi reload`. + +Optionally install [Mod Menu](https://modrinth.com/mod/modmenu) to see loaded GUIs in the mod list. --- ## Commands -``` -/guiapi open — open a GUI for yourself -/guiapi open — open a GUI for target players -/guiapi list — list all loaded GUI definitions -``` +| Command | Description | +|---------|-------------| +| `/guiapi open ` | Open a GUI for yourself | +| `/guiapi open ` | Open a GUI for target players | +| `/guiapi list` | List all loaded GUI definitions | +| `/guiapi reload` | Reload all datapack resources (including GUIs) | +| `/guiapi help` | Show command and JSON field reference in-game | **Permission level 2** (OP) required. @@ -32,91 +36,166 @@ execute as @a[tag=admin] run guiapi open example:admin_panel @s --- +## File Location + +GUI definition files go in: + +``` +data//gui/.json +``` + +The GUI ID used in commands is `:` — matching the file path under `gui/`. + +--- + ## JSON Schema -GUI definition files go in: `data//gui/.json` +### Top-level fields + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `title` | string | `"GUI"` | Inventory title. Supports `§` color codes. | +| `rows` | int 1–6 | `3` | Number of rows (9 slots each). | +| `buttons` | array | `[]` | List of button definitions. | + +### Button fields -The GUI ID used in commands is `:` (matching the file path). +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| `slot` | int | `0` | Zero-based slot index (0–`rows*9-1`). | +| `page` | int | `0` | Which page this button appears on. | +| `item` | string | `"minecraft:stone"` | Item ID, e.g. `minecraft:diamond`. | +| `name` | string | `""` | Display name. Supports `§` color codes. | +| `lore` | string[] | `[]` | Lore lines shown below the name. | +| `glint` | boolean | `false` | Apply enchantment glint effect. | +| `click_type` | string | `"any"` | Which click triggers actions: `any` · `left` · `right` · `shift` | +| `condition` | object | — | Visibility condition (see below). | +| `actions` | array | `[close]` | Actions executed in order on click (see below). | + +> **Legacy:** A single `"action": {}` object is still accepted for backwards compatibility. + +### `click_type` values + +| Value | Triggers on | +|-------|-------------| +| `any` | Left click, right click, or shift+click (default) | +| `left` | Left click only | +| `right` | Right click only | +| `shift` | Shift+left click only | + +### Action types + +| Type | `value` field | `run_with` | Description | +|------|--------------|------------|-------------| +| `run_command` | Command string (with or without leading `/`) | `player` · `console` | Run a command. Default executor: player. | +| `close` | — | — | Close the GUI. | +| `open_gui` | `namespace:name` | — | Close and open another GUI. | +| `message` | Text string | — | Send a chat message to the player. | +| `next_page` | — | — | Go to the next page. | +| `prev_page` | — | — | Go to the previous page. | +| `goto_page` | Page index (string) | — | Jump to a specific page. | + +Multiple actions are executed in order. `close`, `open_gui`, `next_page`, `prev_page`, and `goto_page` stop the chain after executing. + +### Condition types + +Conditions control button **visibility**. Hidden buttons cannot be clicked. + +| Type | `value` format | Visible when | +|------|---------------|--------------| +| `has_tag` | Tag name | Player has the scoreboard tag | +| `score_gt` | `"objective:threshold"` | Player's score > threshold | +| `score_lt` | `"objective:threshold"` | Player's score < threshold | +| `score_eq` | `"objective:value"` | Player's score == value | --- -### Barrel GUI +## Examples -Rendered server-side as a chest inventory. **Client mod not required.** +### Minimal button ```json { - "type": "barrel", "title": "§6My GUI", "rows": 3, "buttons": [ { - "slot": 4, + "slot": 13, "item": "minecraft:diamond", "name": "§bClick Me", "lore": ["§7Does something useful."], - "action": { - "type": "run_command", - "value": "/say hello" - } + "actions": [ + { "type": "run_command", "value": "say hello" }, + { "type": "close" } + ] } ] } ``` -| Field | Type | Description | -|-------|------|-------------| -| `type` | string | `"barrel"` (default) | -| `title` | string | Inventory title. Supports `§` color codes. | -| `rows` | int 1–6 | Number of rows (9 slots each). | -| `buttons` | array | List of slot definitions. | -| `buttons[].slot` | int | Zero-based slot index. | -| `buttons[].item` | string | Item ID, e.g. `minecraft:stone`. | -| `buttons[].name` | string | Display name shown on hover. | -| `buttons[].lore` | string[] | Lore lines shown below the name. | -| `buttons[].action.type` | string | `run_command` · `close` · `open_gui` · `message` | -| `buttons[].action.value` | string | Command string, GUI ID, or message text. | +### Right-click vs left-click on the same slot ---- +```json +{ + "slot": 13, + "item": "minecraft:paper", + "name": "§7Left or Right Click", + "click_type": "left", + "actions": [{ "type": "message", "value": "§aYou left-clicked!" }] +}, +{ + "slot": 13, + "item": "minecraft:paper", + "name": "§7Left or Right Click", + "click_type": "right", + "actions": [{ "type": "message", "value": "§eYou right-clicked!" }] +} +``` -### Dialog GUI +> Two buttons can share a slot if they have different `click_type` values. The first matching one wins. -Rendered client-side as a custom screen. **Requires the mod on the client.** +### Conditional button (tag-gated) ```json { - "type": "dialog", - "title": "§6Welcome", - "body": "Welcome to the server!\nPlease read the rules before playing.", - "actions": [ - { "label": "§aAccept", "type": "run_command", "value": "/say I accepted the rules" }, - { "label": "§cDecline", "type": "close", "value": "" } - ] + "slot": 4, + "item": "minecraft:nether_star", + "name": "§6Admin Only", + "condition": { "type": "has_tag", "value": "admin" }, + "actions": [{ "type": "open_gui", "value": "example:admin_panel" }] } ``` -| Field | Type | Description | -|-------|------|-------------| -| `type` | string | `"dialog"` | -| `title` | string | Dialog title bar text. | -| `body` | string | Body text. Supports `\n` for line breaks. | -| `actions` | array | Buttons shown at the bottom of the dialog. | -| `actions[].label` | string | Button label text. | -| `actions[].type` | string | `run_command` · `close` · `open_gui` · `message` | -| `actions[].value` | string | Command string, GUI ID, or message text. | +### Multi-page GUI -The server sends an `OpenDialogPayload` packet to the client, which then opens the screen locally. - ---- +```json +{ + "title": "§9Item Shop", + "rows": 4, + "buttons": [ + { "slot": 0, "page": 0, "item": "minecraft:apple", "name": "§aApple", "actions": [{ "type": "run_command", "value": "give @s minecraft:apple" }] }, + { "slot": 1, "page": 0, "item": "minecraft:bread", "name": "§aBread", "actions": [{ "type": "run_command", "value": "give @s minecraft:bread" }] }, + { "slot": 26, "page": 0, "item": "minecraft:arrow", "name": "§7Next →", "actions": [{ "type": "next_page" }] }, + { "slot": 0, "page": 1, "item": "minecraft:diamond", "name": "§bDiamond", "actions": [{ "type": "run_command", "value": "give @s minecraft:diamond" }] }, + { "slot": 27, "page": 1, "item": "minecraft:arrow", "name": "§7← Back", "actions": [{ "type": "prev_page" }] } + ] +} +``` -### Action Types +### Console command (elevated execution) -| Type | Behavior | -|------|----------| -| `run_command` | Runs a command as the player (uses the player's own permission level). | -| `close` | Closes the current GUI. | -| `open_gui` | Closes the current GUI and opens another by its ID (`namespace:name`). | -| `message` | Sends a chat message to the player (dialog: shows in action bar). | +```json +{ + "slot": 8, + "item": "minecraft:command_block", + "name": "§cGive OP Items", + "condition": { "type": "has_tag", "value": "admin" }, + "actions": [ + { "type": "run_command", "value": "give @s minecraft:netherite_sword", "run_with": "console" }, + { "type": "close" } + ] +} +``` --- @@ -134,25 +213,34 @@ Requires **Java 21**. ## Architecture ``` -src/main (server-side) - ├── GuiApiMod.java ModInitializer — registers packets, reload listener, command - ├── command/GuiCommand.java /guiapi open|list +src/main/java/dev/toolkitmc/guiapi/ + ├── GuiApiMod.java ModInitializer — registers reload listener and command + ├── command/ + │ └── GuiCommand.java /guiapi open|list|reload|help ├── gui/ - │ ├── GuiDefinition.java JSON data model (parsed on resource reload) - │ ├── GuiScreenHandler.java Blocks slot interaction; routes clicks to BarrelGuiHandler - │ ├── BarrelGuiHandler.java Opens inventory screens; dispatches button actions - │ └── OpenDialogPayload.java S→C network packet carrying a GUI ID - └── loader/GuiRegistry.java Loads data//gui/*.json from the active datapack set - -src/client (client-side) - ├── GuiApiClient.java ClientModInitializer — registers S→C packet handler - └── screen/DialogScreen.java Custom Screen implementation for dialog GUIs + │ ├── GuiDefinition.java JSON data model (title, rows, buttons, actions, conditions) + │ ├── GuiScreenHandler.java Extends GenericContainerScreenHandler; blocks slot interaction + │ └── BarrelGuiHandler.java Opens inventory screens; evaluates conditions; dispatches actions + ├── loader/ + │ └── GuiRegistry.java Loads data//gui/*.json on resource reload + └── modmenu/ + └── GuiApiModMenuEntry.java Optional Mod Menu info screen (compile-only dependency) ``` --- -## Known Limitations +## Security + +See [SECURITY.md](SECURITY.md). + +--- + +## Contributing + +See [CONTRIBUTING.md](CONTRIBUTING.md). + +--- + +## License -- **Dialog GUIs** require the mod installed on the client. Barrel GUIs are server-side only (vanilla-compatible clients work). -- `run_command` actions execute with the **player's own permission level** — the mod does not elevate permissions. If you need elevated execution, use a command block or function tag triggered server-side. -- Minecraft 1.21.1 has no native dialog API. This mod implements its own UI layer on top of Minecraft's `Screen` system. The native dialog API (`/dialog`) was added in 1.21.6. +MIT — see [LICENSE](LICENSE). diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..5bae28c --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,55 @@ +# Security Policy + +## Supported Versions + +| Version | Supported | +|---------|-----------| +| 1.0.x (Minecraft 1.21.1) | ✅ Active | + +Only the latest release receives security fixes. + +--- + +## Reporting a Vulnerability + +**Do not open a public issue for security vulnerabilities.** + +Report privately via GitHub's [Security Advisories](https://github.com/ToolkitMC/guiAPI/security/advisories/new) feature. + +Include: +- A clear description of the vulnerability +- Steps to reproduce +- Potential impact (e.g. command injection, privilege escalation, crash) +- Affected version(s) + +You can expect an initial response within **72 hours** and a patch within **14 days** for confirmed critical issues. + +--- + +## Threat Model + +GUI API runs entirely server-side. The attack surfaces to keep in mind: + +### Datapack authors (trusted by design) +GUI JSON files are loaded from the server's datapack directory. Anyone who can place files in `world/datapacks/` already has filesystem access to the server — GUI API does not expand that trust boundary. + +### `run_with: "console"` actions +Buttons with `"run_with": "console"` execute commands with server-level (console) permission. This is intentional and only reachable by players who trigger a button defined by the datapack author. If you expose such buttons without a `condition` guard, any player who opens the GUI can trigger the command. + +**Recommendation:** Always gate elevated-command buttons with a `condition` such as `has_tag` to restrict access. + +### Command injection +The `run_command` action passes the `value` string directly to the Minecraft command dispatcher. The value is set at datapack authoring time — it is not interpolated with player input at runtime. There is no runtime injection surface from the player side. + +### Player inventory protection +All slot interactions inside a GUI inventory are consumed server-side. `quickMove` (shift-drag), `THROW` (Q), `CLONE` (middle-click), and all other `SlotActionType` variants other than `PICKUP` and `QUICK_MOVE` are silently discarded, preventing item duplication or extraction. + +### State map (`OPEN_GUIS`) +Per-player open state is stored in a server-side `HashMap`. It is cleared on screen close. There is no persistent state across sessions, no serialisation to disk, and no cross-player data sharing. + +--- + +## Known Non-Issues + +- **Client-side spoofing of slot clicks** — the server validates all slot interactions before acting. Clients cannot trigger actions on slots that have no button defined. +- **Permission escalation via `/guiapi open`** — the command requires permission level 2. Players cannot open GUIs for other players without OP. diff --git a/example-datapack/data/example/gui/admin_panel.json b/example-datapack/data/example/gui/admin_panel.json index 260ceef..293b78d 100644 --- a/example-datapack/data/example/gui/admin_panel.json +++ b/example-datapack/data/example/gui/admin_panel.json @@ -1,5 +1,4 @@ { - "type": "barrel", "title": "§6Admin Panel", "rows": 3, "buttons": [ @@ -8,40 +7,61 @@ "item": "minecraft:book", "name": "§aRules", "lore": ["§7Click to see the server rules."], - "action": { - "type": "message", - "value": "§6Rules: §fBe respectful. No griefing. No cheating." - } + "actions": [ + { "type": "message", "value": "§6Rules: §fBe respectful. No griefing. No cheating." } + ] }, { "slot": 12, "item": "minecraft:compass", "name": "§bSpawn", - "lore": ["§7Teleport to spawn."], - "action": { - "type": "run_command", - "value": "/tp @s 0 64 0" - } + "lore": ["§7Teleport to spawn.", "§8Left-click only."], + "click_type": "left", + "actions": [ + { "type": "run_command", "value": "tp @s 0 64 0" }, + { "type": "close" } + ] + }, + { + "slot": 12, + "item": "minecraft:compass", + "name": "§bSpawn", + "lore": ["§7See your coordinates.", "§8Right-click only."], + "click_type": "right", + "actions": [ + { "type": "run_command", "value": "execute as @s run data get entity @s Pos" } + ] }, { "slot": 14, "item": "minecraft:ender_pearl", "name": "§dMore Options", "lore": ["§7Open the second menu."], - "action": { - "type": "open_gui", - "value": "example:sub_menu" - } + "actions": [ + { "type": "open_gui", "value": "example:sub_menu" } + ] + }, + { + "slot": 15, + "item": "minecraft:nether_star", + "name": "§cAdmin Tools", + "lore": ["§7Only visible with the §cadmin§7 tag.", "§8Shift-click to open."], + "glint": true, + "click_type": "shift", + "condition": { "type": "has_tag", "value": "admin" }, + "actions": [ + { "type": "run_command", "value": "gamemode creative @s", "run_with": "console" }, + { "type": "close" } + ] }, { "slot": 16, "item": "minecraft:barrier", "name": "§cClose", "lore": [], - "action": { - "type": "close", - "value": "" - } + "actions": [ + { "type": "close" } + ] } ] } diff --git a/example-datapack/data/example/gui/showcase.json b/example-datapack/data/example/gui/showcase.json new file mode 100644 index 0000000..93c3964 --- /dev/null +++ b/example-datapack/data/example/gui/showcase.json @@ -0,0 +1,144 @@ +{ + "title": "§5Feature Showcase", + "rows": 6, + "buttons": [ + { + "slot": 0, + "page": 0, + "item": "minecraft:lime_stained_glass_pane", + "name": "§aLeft Click", + "lore": ["§7Triggers on left-click only."], + "click_type": "left", + "actions": [ + { "type": "message", "value": "§aYou left-clicked!" } + ] + }, + { + "slot": 0, + "page": 0, + "item": "minecraft:yellow_stained_glass_pane", + "name": "§eRight Click", + "lore": ["§7Triggers on right-click only."], + "click_type": "right", + "actions": [ + { "type": "message", "value": "§eYou right-clicked!" } + ] + }, + { + "slot": 1, + "page": 0, + "item": "minecraft:cyan_stained_glass_pane", + "name": "§bShift Click", + "lore": ["§7Triggers on shift+click only."], + "click_type": "shift", + "actions": [ + { "type": "message", "value": "§bYou shift-clicked!" } + ] + }, + { + "slot": 3, + "page": 0, + "item": "minecraft:enchanted_book", + "name": "§dGlint Item", + "lore": ["§7This item has enchantment glint."], + "glint": true, + "actions": [ + { "type": "message", "value": "§dShiny!" } + ] + }, + { + "slot": 5, + "page": 0, + "item": "minecraft:nether_star", + "name": "§6Admin Only", + "lore": ["§7Visible only with the §cadmin§7 tag."], + "glint": true, + "condition": { "type": "has_tag", "value": "admin" }, + "actions": [ + { "type": "message", "value": "§6You are an admin." } + ] + }, + { + "slot": 7, + "page": 0, + "item": "minecraft:gold_ingot", + "name": "§6Score Check", + "lore": ["§7Visible only if §6coins§7 score > 10."], + "condition": { "type": "score_gt", "value": "coins:10" }, + "actions": [ + { "type": "message", "value": "§6You have enough coins." } + ] + }, + { + "slot": 9, + "page": 0, + "item": "minecraft:paper", + "name": "§fMulti-Action", + "lore": [ + "§7Runs two commands,", + "§7sends a message, then closes." + ], + "actions": [ + { "type": "run_command", "value": "say Action 1" }, + { "type": "run_command", "value": "say Action 2" }, + { "type": "message", "value": "§7Both commands ran." }, + { "type": "close" } + ] + }, + { + "slot": 11, + "page": 0, + "item": "minecraft:command_block", + "name": "§cConsole Command", + "lore": [ + "§7Runs with console permission.", + "§8(run_with: console)" + ], + "condition": { "type": "has_tag", "value": "admin" }, + "actions": [ + { "type": "run_command", "value": "op @s", "run_with": "console" }, + { "type": "close" } + ] + }, + { + "slot": 53, + "page": 0, + "item": "minecraft:arrow", + "name": "§7Next Page →", + "lore": ["§8Page 1 / 2"], + "actions": [ + { "type": "next_page" } + ] + }, + { + "slot": 4, + "page": 1, + "item": "minecraft:ender_eye", + "name": "§5Page 2", + "lore": ["§7You made it to page 2!"], + "actions": [ + { "type": "message", "value": "§5Welcome to page 2." } + ] + }, + { + "slot": 45, + "page": 1, + "item": "minecraft:arrow", + "name": "§7← Previous Page", + "lore": ["§8Page 2 / 2"], + "actions": [ + { "type": "prev_page" } + ] + }, + { + "slot": 49, + "page": 1, + "item": "minecraft:barrier", + "name": "§cClose", + "lore": [], + "actions": [ + { "type": "close" } + ] + } + ] +} diff --git a/example-datapack/data/example/gui/sub_menu.json b/example-datapack/data/example/gui/sub_menu.json index a4cd561..9446333 100644 --- a/example-datapack/data/example/gui/sub_menu.json +++ b/example-datapack/data/example/gui/sub_menu.json @@ -1,5 +1,4 @@ { - "type": "barrel", "title": "§dMore Options", "rows": 1, "buttons": [ @@ -8,30 +7,29 @@ "item": "minecraft:clock", "name": "§eSet Day", "lore": ["§7Set time to day."], - "action": { - "type": "run_command", - "value": "/time set day" - } + "actions": [ + { "type": "run_command", "value": "time set day", "run_with": "console" }, + { "type": "message", "value": "§aTime set to day." } + ] }, { "slot": 4, "item": "minecraft:sunflower", "name": "§aClear Weather", "lore": ["§7Set weather to clear."], - "action": { - "type": "run_command", - "value": "/weather clear" - } + "actions": [ + { "type": "run_command", "value": "weather clear", "run_with": "console" }, + { "type": "message", "value": "§aWeather cleared." } + ] }, { "slot": 6, "item": "minecraft:arrow", - "name": "§7Back", + "name": "§7← Back", "lore": ["§7Return to admin panel."], - "action": { - "type": "open_gui", - "value": "example:admin_panel" - } + "actions": [ + { "type": "open_gui", "value": "example:admin_panel" } + ] } ] } diff --git a/example-datapack/data/example/gui/welcome.json b/example-datapack/data/example/gui/welcome.json new file mode 100644 index 0000000..36d9f2d --- /dev/null +++ b/example-datapack/data/example/gui/welcome.json @@ -0,0 +1,32 @@ +{ + "title": "§6Welcome!", + "rows": 2, + "buttons": [ + { + "slot": 2, + "item": "minecraft:written_book", + "name": "§aAccept Rules", + "lore": [ + "§7By clicking you agree to:", + "§f- Be respectful", + "§f- No griefing", + "§f- No cheating" + ], + "glint": true, + "actions": [ + { "type": "run_command", "value": "tag @s add rules_accepted" }, + { "type": "message", "value": "§aWelcome! Enjoy your stay." }, + { "type": "close" } + ] + }, + { + "slot": 6, + "item": "minecraft:barrier", + "name": "§cDecline", + "lore": ["§7You will be disconnected."], + "actions": [ + { "type": "run_command", "value": "kick @s You must accept the rules to play.", "run_with": "console" } + ] + } + ] +} diff --git a/example-datapack/data/example/gui/welcome_dialog.json b/example-datapack/data/example/gui/welcome_dialog.json deleted file mode 100644 index c8d3295..0000000 --- a/example-datapack/data/example/gui/welcome_dialog.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "type": "dialog", - "title": "§6Welcome!", - "body": "Welcome to the server!\n\nPlease read the rules before playing. Have fun and enjoy your stay.", - "actions": [ - { - "label": "§aAccept", - "type": "run_command", - "value": "/say I accepted the rules!" - }, - { - "label": "§cDecline", - "type": "close", - "value": "" - } - ] -} diff --git a/example-datapack/pack.mcmeta b/example-datapack/pack.mcmeta index 5e9d560..aeff16b 100644 --- a/example-datapack/pack.mcmeta +++ b/example-datapack/pack.mcmeta @@ -1,6 +1,6 @@ { "pack": { - "description": "GUI API example datapack", + "description": "GUI API example datapack — demonstrates all GUI API features", "pack_format": 48, "supported_formats": { "min_inclusive": 48, "max_inclusive": 48 } }