> For the complete documentation index, see [llms.txt](https://buildsystem.eintosti.de/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://buildsystem.eintosti.de/v4/managing-worlds/custom-statuses-and-categories.md).

# Custom Statuses & Categories

In BuildSystem 4.0, world statuses and navigator categories are dynamic registries configured via `/setup` or configuration files.

Two ideas are worth getting straight first:

* A **status** is the state a world is in, such as *In Progress* or *Finished*. Every world has exactly one.
* A **category** is a tab in the `/worlds` navigator. A category does not own worlds, it *selects* them, by listing the statuses and visibilities it wants to show.

So you never put a world into a category. You give the world a status, and every category listing that status shows it.

***

## Common Tasks

Recipes for what most servers want on day one. Everything here happens in `/setup`, which needs `buildsystem.setup`. The reference model starts at [§1](#1-core-concepts).

{% tabs %}
{% tab title="Add a status" %}
Say you want a **Needs Review** stage between *Almost Finished* and *Finished*.

1. Run `/setup` and open **World Statuses**.
2. Click the create button and type `Needs Review`. The id becomes `needs_review`.
3. Shift-click the new status to open its editor. Set the colour, icon, and an **order** of `4` so it sorts before *Finished*.
4. Drag it from the palette in your inventory into an empty slot in the preview grid above. That grid is the `/worlds setStatus` picker exactly as builders will see it.

Grant `buildsystem.setstatus.needs_review` to whoever should be able to assign it.

{% hint style="info" %}
A new status joins the default category automatically, so it appears in the navigator right away. Edit that category's member statuses if you want it somewhere else.
{% endhint %}
{% endtab %}

{% tab title="Add a category" %}
Say you want an **Events** tab holding only event builds.

1. Create a status for them first, for example `event`. A category with no statuses shows nothing.
2. Run `/setup` and open **Navigator Layout**.
3. Click the create category button and name it `Events`.
4. Right-click the new category to open its editor. Under **member statuses** add `event`, and under **visibilities** pick who it covers, normally `EVERYONE`.
5. Drag it from the palette into the navigator preview to choose its tab position.

The tab is now live, and so is a `/worlds events` shortcut generated from the category id.
{% endtab %}

{% tab title="Hide worlds" %}
There is no "hidden" flag on a world. A world disappears from the navigator when **no category lists its status**.

That is exactly how the built-in `hidden` status works. Nothing lists it, so setting a world to `hidden` takes it out of every tab while leaving it fully usable for anyone who teleports to it directly.

To hide a group of worlds, give them a status no category lists.
{% endtab %}

{% tab title="Lock a world" %}
Turn off **building allowed** on the status, not on the world.

1. `/setup` → **World Statuses** → shift-click the status.
2. Toggle **building allowed** off.

Every world carrying that status is now read-only. The built-in `archive` status already works this way. `buildsystem.bypass.archive` lets staff build anyway, and it covers *every* status with building disabled, not only `archive`.
{% endtab %}

{% tab title="Auto-advance" %}
A status can move a world forward the first time someone edits it.

In the status editor, set **progresses to**. The built-in `not_started` points at `in_progress`, so a fresh world flips to *In Progress* the moment the first block changes and nobody has to remember to set it.

Leave it empty for a status that should not move on its own.
{% endtab %}
{% endtabs %}

***

## 1. Core Concepts

### 1.1 Custom World Statuses (`BuildWorldStatus`)

Statuses define progress state and behavior. Each status object contains the following properties:

| Property            | Description                                                                                                            |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `id`                | Unique lower-case key (e.g., `in_progress`). Persisted in configuration files.                                         |
| `displayName`       | Display name in menus and chat. Supports standard color codes.                                                         |
| `color`             | Color prefix applied to the status.                                                                                    |
| `icon`              | `XMaterial` shown in selection menus.                                                                                  |
| `order`             | Sorting weight (lower numbers appear first in lists).                                                                  |
| `permission`        | **Dynamic Permission**: Node required to assign this status (see below).                                               |
| `buildingAllowed`   | Set `false` to block block place/break changes in carrying worlds.                                                     |
| `progressesTo`      | Optional status ID target for auto-progression on first block edit.                                                    |
| `statusSlot`        | Slot (0-26) the status occupies in the `/worlds setStatus` picker, set in the status editor.                           |
| `shownInStatusMenu` | Whether the status appears in the picker. Hidden statuses wait in the editor's palette.                                |
| `builtIn`           | Identifies seeded default statuses. Built-ins can be edited or deleted; the flag controls default re-seeding on reset. |

#### Dynamic Status Permissions

To change a world's status, a player requires a dynamic permission node derived from the status id:

* **Built-in statuses** keep their legacy node with underscores removed. For example `in_progress` → `buildsystem.setstatus.inprogress`.
* **Custom statuses** use their full id, so `my_status` → `buildsystem.setstatus.my_status`.

This keeps existing permission setups working while guaranteeing two custom statuses can never collapse onto the same node.

#### Universal Archive Bypass

A single bypass node covers all statuses where `buildingAllowed` is `false`: `buildsystem.bypass.archive`

***

### 1.2 World Visibility (`Visibility`)

* **`EVERYONE`**: Public worlds visible to all players.
* **`ADDED_PLAYERS`**: Restricted worlds visible only to the creator and whitelisted builders.

***

### 1.3 Navigator Categories (`NavigatorCategory`)

Categories define navigator menu tabs. They group worlds by visibility and status.

| Property           | Description                                                                                                              |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------ |
| `id`               | Unique lower-case key (e.g., `public`). Referenced by folders and layouts.                                               |
| `displayName`      | Title shown on the navigator tab item.                                                                                   |
| `color` / `icon`   | Styling in menus.                                                                                                        |
| `visibilities`     | Allowed visibilities grouped (`EVERYONE`, `ADDED_PLAYERS`, or both).                                                     |
| `statusIds`        | List of status IDs grouped in this category.                                                                             |
| `shownInNavigator` | Set `true` to render this category as a tab in the `/worlds` menu.                                                       |
| `navigatorSlot`    | Tab position (0-26) in the navigator GUI.                                                                                |
| `builtIn`          | Identifies seeded default categories. Built-ins can be edited or deleted; the flag controls default re-seeding on reset. |

#### Per-Category Commands & Visibility

Every category gets a `/worlds <id>` command shortcut that opens its tab, generated from the navigator. Creating a category adds its command, and deleting one removes it. The dynamic permission `buildsystem.navigator.category.<categoryId>` controls both the shortcut and whether the tab shows in the navigator. It is granted by default, so denying it hides a category *and* disables its command together. If a category id matches a real subcommand (e.g. `delete`), the subcommand always wins the name and the shortcut is dropped.

***

## 2. Default Configurations

Built-in statuses and categories seeded on first plugin boot:

### Built-in Statuses (`statuses.yml`)

| ID                | Display Name    | Color             | Icon         | Order | Block Modify | Auto-Progress |
| ----------------- | --------------- | ----------------- | ------------ | ----- | ------------ | ------------- |
| `not_started`     | Not Started     | `&c` (Red)        | `RED_DYE`    | 1     | Allowed      | `in_progress` |
| `in_progress`     | In Progress     | `&6` (Gold)       | `ORANGE_DYE` | 2     | Allowed      | None          |
| `almost_finished` | Almost Finished | `&a` (Green)      | `LIME_DYE`   | 3     | Allowed      | None          |
| `finished`        | Finished        | `&2` (Dark Green) | `GREEN_DYE`  | 4     | Allowed      | None          |
| `archive`         | Archive         | `&3` (Aqua)       | `CYAN_DYE`   | 5     | **Blocked**  | None          |
| `hidden`          | Hidden          | `&7` (Gray)       | `BONE_MEAL`  | 6     | Allowed      | None          |

A status is shown in the navigator purely through category membership. There is no per-status visibility flag. The built-in `hidden` status stays out of the navigator simply because no built-in category lists it (see [§3](#3-world-to-category-mapping-logic)).

### Built-in Categories (`categories.yml`)

| ID        | Display Name | Color | Icon          | Visibilities                | Grouped Statuses                                            | Slot |
| --------- | ------------ | ----- | ------------- | --------------------------- | ----------------------------------------------------------- | ---- |
| `public`  | Worlds       | `&b`  | `PLAYER_HEAD` | `EVERYONE`                  | `not_started`, `in_progress`, `almost_finished`, `finished` | 11   |
| `archive` | Archive      | `&3`  | `PLAYER_HEAD` | `EVERYONE`, `ADDED_PLAYERS` | `archive`                                                   | 12   |
| `private` | Private      | `&a`  | `PLAYER_HEAD` | `ADDED_PLAYERS`             | `not_started`, `in_progress`, `almost_finished`, `finished` | 13   |

{% hint style="info" %}
`public` is the default category and `not_started` is the default status. The last remaining **status** can't be deleted, so a world always has a valid status to fall back to. **Categories** have no such guard, so you can delete every one, leaving the navigator empty until you reset to defaults.
{% endhint %}

***

## 3. World-to-Category Mapping Logic

When the `/worlds` navigator renders, it filters and groups worlds dynamically:

* **Inclusive Membership**: A world is grouped by a category if its visibility and status ID are listed. A world appears in every category that groups it. If tabs overlap, the world is listed under all matching categories simultaneously.
* **Primary Category**: If a world must resolve to a single category (e.g. choosing which folder a new world is saved under by default), the plugin assigns the first matching category based on the navigator slot layout, or falls back to the default `public` category.

If a world's status is not listed in any category, it is excluded from navigator listings. This is exactly how the built-in `hidden` status works. No category groups it, so worlds set to `hidden` never appear in the navigator.

***

## 4. Custom Skull Textures & %viewer%

Categories, worlds, and folders support custom player head skin textures when their icon material is set to `PLAYER_HEAD`.

* **Viewer's Head (`%viewer%`)**: Renders the head of the player viewing the menu.
* **Custom Skins**: Renders specified base64 skin textures, UUIDs, or usernames.
* **Default Fallbacks**: If blank or absent, heads resolve asynchronously:
  * **Private Worlds**: Renders the world creator's skin.
  * **Public Worlds**: Renders a head matching the world's folder name.
  * **Categories**: Renders the viewer's head for private groupings, or a standard navigator skin for public tabs.

***

## 5. Deleting & Resetting Safeguards

* **Deleting a Status**: Drop it onto the delete target in the status editor. Worlds carrying it are reassigned to the default status (`not_started`), and the id is pruned from every category's status list. The last remaining status can't be deleted.
* **Deleting a Category**: Drop it onto the delete target (textured skull) in the navigator editor. Because statuses are shared, worlds re-route to another matching tab (or the default) on the next render, and folders pinned to the category are re-homed to the default. Any category can be deleted, including the last. The navigator then stays empty until you reset to defaults.
* **Resetting to Defaults**: Each layout editor has a reset control. **Left-click** resets only the layout (built-in entries return to their default slots; custom entries are kept but removed from the menu). **Right-click** resets everything, restoring any deleted built-in statuses or categories. Deleting `statuses.yml` and `categories.yml` and restarting achieves the same full reset.

***

## 6. The In-Game Setup Menu (`/setup`)

Administrators with `buildsystem.setup` permission can edit registries directly in-game. The hub branches into three configuration panels:

### 6.1 Default World Icons

Sets default item representation used for Normal, Flat, Nether, End, or Void worlds upon creation. Features a reset control.

![Setup GUI Hub](/files/ea5ZpGvaLtxfV7YQ9Gk2)

### 6.2 World Statuses

The status editor is the drag-and-drop counterpart of the **Navigator Layout Editor** (§6.3): the top inventory is a live preview of the `/worlds setStatus` picker, and your own inventory holds the controls plus a palette of statuses not currently in the picker. This is where the picker's appearance is configured.

* **Arrange**: Pick a status up from the preview or palette and drop it onto a slot to place it; drop it outside the preview to take it out of the picker.
* **Create / Edit / Delete**: Click the create button to name a new status, shift-click a status to open its property editor, or drop a status onto the delete target to remove it.
* **Property editor**: Name, color (dye picker), icon (item picker), order, the `buildingAllowed` toggle, and the auto-progression target a world advances to on first edit. Toggle items show their state in the lore (e.g. `Currently: Enabled/Disabled`) and glint when active.

### 6.3 Navigator Layout Editor (Drag & Drop)

Handles positioning, tab properties, category creation, and deletion in a single screen. The top inventory displays a live 27-slot preview of the navigator.

* **Category Creation & Edits**: Click the create category button (textured skull) to register a new category. Right-click any category to open its property editor (name, color picker, skull textures, grouped visibilities, member statuses listed in order, and slot).
* **Inventory snapshotting**: Player inventories are snapshotted and cleared, then replaced with menu controls (available categories, back button, delete category skull, and layout pins). Real inventories are restored when the GUI closes, the player disconnects, or the server stops.
* **Drag-and-Drop Placement**: Click a category in the palette to pick it up, and click a preview slot above to place it. While held on the cursor, the category previews as a design-color glass pane. Dropping a category outside the preview removes it from the layout (returns it to the palette). Clicking is cancelled, protecting control items.
* **Settings Button**: The settings gear button is part of the layout. It can be moved, dropped outside to remove it from the navigator, or re-added from the palette. A settings slot value of `-1` hides it.

![Navigator Layout Editor](/files/dwm4hkiDviD11i0R9tfU)

***

## 7. Shared GUI Components

The `/setup` editors share the following GUI components:

* **16-Color Dye Picker**: Opens a panel of dye swatches sorted by color family, corresponding 1:1 to legacy chat codes. Selection carries an enchant glint. Click "Back" to return to the editor.

  ![Color Picker Panel](/files/TeKckkGAiWulx3WPOYHA)
* **Item Picker Grid**: Displays every obtainable Minecraft item. The left 8 columns show items, and the 9th column houses scrolling navigation arrows (scrolls one row at a time). The name filter searches both raw material keys (e.g. `minecraft:stone`) and display names. Item names in the grid are colorized.
* **Player Head Customizer**: Right-click the icon control item to input a base64 texture, `viewer`, or `none`.
