Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -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/<short-description>` or `fix/<short-description>`.
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).
240 changes: 164 additions & 76 deletions README.md
Original file line number Diff line number Diff line change
@@ -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 <namespace:id> — open a GUI for yourself
/guiapi open <namespace:id> <targets> — open a GUI for target players
/guiapi list — list all loaded GUI definitions
```
| Command | Description |
|---------|-------------|
| `/guiapi open <id>` | Open a GUI for yourself |
| `/guiapi open <id> <targets>` | 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.

Expand All @@ -32,91 +36,166 @@ execute as @a[tag=admin] run guiapi open example:admin_panel @s

---

## File Location

GUI definition files go in:

```
data/<namespace>/gui/<name>.json
```

The GUI ID used in commands is `<namespace>:<name>` — matching the file path under `gui/`.

---

## JSON Schema

GUI definition files go in: `data/<namespace>/gui/<name>.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 `<namespace>:<name>` (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" }
]
}
```

---

Expand All @@ -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/<ns>/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/<ns>/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).
Loading