> 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/getting-started/migration-guide.md).

# Migration Guide

Upgrade reference for moving between major versions of BuildSystem.

***

## Upgrading from v3 to v4

4.0.0 is a major release. The plugin migrates its data, `config.yml`, and `messages.yml` automatically on the first server start — a working server needs no manual steps. The breaking changes are the **runtime requirements** (Java and Minecraft versions) and the **`buildsystem-api`** surface for dependent plugins.

Read the sections below for the full picture: requirements, automatic conversion, configuration, permissions, commands, language, and API.

{% hint style="warning" %}
**Back up first.** Stop the server and copy the `plugins/BuildSystem/` folder (and any external world saves) before upgrading. The conversion is one-way: once worlds, statuses, and visibilities are written in the v4 format, an older build can no longer read them.
{% endhint %}

***

### 1. System Requirements

These are hard requirements — the plugin will not start otherwise.

| Requirement         | v3             | v4                                                                             |
| ------------------- | -------------- | ------------------------------------------------------------------------------ |
| **Java**            | 21             | **25**                                                                         |
| **Minecraft**       | 1.21+          | **26.1 – 26.2**                                                                |
| **Server software** | Spigot / Paper | Spigot / Paper (game-rule access bridged through XSeries, so both run cleanly) |

Upgrade your server's JDK to **25** before dropping in the new jar.

***

### 2. Automatic Data & File Conversion

On the first 4.0.0 boot the plugin upgrades each legacy structure in place. Nothing is lost; unrecognized values fall back to a sensible default.

| Data Element                      | Legacy Format (v3)                                          | New Format (v4)                                             | Notes                                                                                                                                                                                      |
| --------------------------------- | ----------------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Storage keying**                | `worlds.yml` / `folders.yml` keyed by world/folder **name** | Keyed by a stable **UUID**, with the name stored as a field | A rename is now a field update, not a key move, so renames no longer leave orphaned entries. Folder `parent` references switch from the parent's name to its UUID.                         |
| **World status**                  | `IN_PROGRESS` (uppercase enum)                              | `in_progress` (lowercase registry id)                       | Unrecognized values default to `not_started`.                                                                                                                                              |
| **World visibility**              | `private: true / false`                                     | `visibility: ADDED_PLAYERS / EVERYONE`                      | One-to-one mapping.                                                                                                                                                                        |
| **Folder category**               | `category: PUBLIC` (uppercase enum)                         | `category: public` (lowercase registry id)                  | Unrecognized values default to `public`.                                                                                                                                                   |
| **Status display names**          | `status_in_progress: "&6In Progress"` in `messages.yml`     | `displayName` + `color` in `statuses.yml`                   | Seeded on first boot **only if `statuses.yml` does not exist yet**. The leading colour code is split into `color`; the remainder becomes `displayName`, preserving any rename/translation. |
| **`config.yml` / `messages.yml`** | Older layouts                                               | Versioned, auto-migrated                                    | Missing keys are appended and some keys are relocated; existing customisations are preserved.                                                                                              |

Before re-keying, the plugin writes a one-time backup next to each affected file:

* `plugins/BuildSystem/worlds.yml.v3.bak`
* `plugins/BuildSystem/folders.yml.v3.bak`

The migration also creates two new files for the now-customizable statuses and categories:

* `plugins/BuildSystem/statuses.yml`
* `plugins/BuildSystem/categories.yml`

{% hint style="info" %}
After the first boot, `statuses.yml` is authoritative. The `status_*` keys in `messages.yml` are no longer read and can be removed (see the Language File section below).
{% endhint %}

***

### 3. Configuration File Changes

`config.yml` is versioned and migrated on load, so custom values survive the upgrade. Two flags are gone and one is new.

#### Removed Keys

Silently ignored in v4 — safe to delete from `config.yml`:

* `settings.per-option-permissions` — superseded by the always-on `buildsystem.setting.<option>` nodes.
* `settings.restrict-template-access` — superseded by the deny-to-restrict `buildsystem.create.template.<name>` model.

#### New Keys

* `settings.world-permission-whitelist` (default: `[]`) — restricts the permission strings `/worlds setPermission` may assign. An empty list allows any value.

***

### 4. Permission Changes

Existing command permissions are unchanged. v4 adds the following.

#### New Static Nodes

| Permission                 | Default | Description                                                          |
| -------------------------- | ------- | -------------------------------------------------------------------- |
| `buildsystem.savetemplate` | `OP`    | Run `/worlds saveTemplate` to copy a live world into `templates/`.   |
| `buildsystem.edit.pin`     | `OP`    | Pin worlds to the top of navigator lists via the Edit GUI.           |
| `buildsystem.edit.icon`    | `OP`    | Set a world's custom icon material or skull texture in the Edit GUI. |

#### New Dynamic Nodes

Resolved at runtime from the id of the status, category, template, or generator:

| Permission Pattern                    | Default                                 | Description                                                                                                                                                                                  |
| ------------------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `buildsystem.create.<categoryId>`     | `OP`                                    | Create a world in a specific category (e.g. `buildsystem.create.public`). Admins with the create bypass may create in any category.                                                          |
| `buildsystem.navigator.<categoryId>`  | `true`                                  | Show the category in the navigator **and** enable its `/worlds <category>` shortcut. Deny it to hide a category and disable its command together.                                            |
| `buildsystem.setstatus.<statusId>`    | inherits `buildsystem.setstatus` (`OP`) | Assign a status to a world. Built-in statuses strip underscores for backwards compatibility (`in_progress` → `…inprogress`); custom statuses use their full id (`my_status` → `…my_status`). |
| `buildsystem.create.template.<name>`  | `true`                                  | Use a specific template. Deny the node to restrict it.                                                                                                                                       |
| `buildsystem.create.generator.<name>` | `true`                                  | Use a specific chunk generator. Deny the node to restrict it.                                                                                                                                |
| `buildsystem.setting.<option>`        | `true`                                  | Toggle a `/settings` option (e.g. `buildsystem.setting.noclip`). Deny to restrict.                                                                                                           |

***

### 5. Navigator Command Changes

The fixed `/worlds archive`, `/worlds private`, and `/worlds public` subcommands are replaced by per-category `/worlds <category>` shortcuts, generated from the navigator layout instead of being hardcoded.

Existing setups keep working: the three built-in categories still answer to `archive`, `private`, and `public`. A real subcommand always wins a name clash, so a custom category named like a command can never shadow it.

***

### 6. Language File (`messages.yml`) Changes

* **Default English**: delete `messages.yml` and restart to regenerate a fresh file with every v4 key.
* **Custom translations**: keep the file; the plugin appends new keys on startup. Localize the newly added keys manually.

#### Obsolete Keys (can be removed)

No longer read — their values were copied into `statuses.yml` on the first boot:

* `status_not_started`, `status_in_progress`, `status_almost_finished`, `status_finished`, `status_archive`, `status_hidden`

#### Changed Keys

* `world_navigator_create_world` — default changed to `&bCreate a World` because clicking the item now opens a category picker.

***

### 7. Developer API Changes

Plugins compiling against `buildsystem-api` must recompile against 4.0.0 and apply the changes below.

#### Breaking Changes

| API Element         | Legacy Code (v3)                                                | Upgrade Action (v4)                                                                                                                                                       |
| ------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Event package**   | `de.eintosti.buildsystem.api.event.BuildWorldManipulationEvent` | Import from `de.eintosti.buildsystem.api.event.world` instead.                                                                                                            |
| **World creation**  | `WorldService.createWorld(...)`                                 | Fluent builder: `worldService.newWorld(name)…build()`.                                                                                                                    |
| **World importing** | `WorldService.importWorld(name, ...)`                           | Fluent builder: `worldService.importWorld(name)…build()`.                                                                                                                 |
| **Properties**      | `Type<T>`                                                       | Renamed to `Property<T>`.                                                                                                                                                 |
| **Bukkit handle**   | `BuildWorld.getWorld()` returned a nullable `World`             | Now returns `Optional<org.bukkit.World>`.                                                                                                                                 |
| **World status**    | `BuildWorldStatus` (enum)                                       | Now an interface resolved via `BuildSystem.getStatusRegistry()`. Compare by `getId()` / `equals()`, never `==` or `switch`.                                               |
| **Categories**      | `NavigatorCategory` (enum)                                      | Now an interface resolved via `BuildSystem.getNavigatorCategoryRegistry()`; `Folder.getCategory()` returns it. Compare by `getId()` / `equals()`, never `==` or `switch`. |
| **Visibility**      | `isPrivateWorld()` / `setPrivateWorld(boolean)`                 | Replaced by `WorldData.getVisibility()` / `setVisibility(Visibility)` (`EVERYONE` / `ADDED_PLAYERS`).                                                                     |
| **API lookup**      | `BuildSystemProvider.register()` / `unregister()`               | No longer public. Resolve via `BuildSystemProvider.get()` or Bukkit's `ServicesManager`.                                                                                  |

#### Removed

* `BuildWorld.setLoaded(boolean)` — the read-only `isLoaded()` remains.
* `BuildWorld.asProfileable()` — resolve head icons through `Displayable.getHeadProfile()` / `getIconSkullTexture()`.
* `WorldData.getConfigFormat()` and `WorldData.getAllData()`.
* The public registration entrypoints on `BuildSystemProvider` (see above).

#### New (non-breaking)

Available to dependent plugins but not required for an upgrade:

* World lifecycle events in `event.world` (`BuildWorldCreateEvent`, `BuildWorldPostCreateEvent`, `BuildWorldDeleteEvent`, `BuildWorldPostDeleteEvent`, `BuildWorldRenameEvent`, `BuildWorldUnimportEvent`, `BuildWorldStatusChangeEvent`), plus backup and folder events in `event.backup` and `event.folder`.
* `WorldService.importWorlds()` for bulk import spread across ticks.
* Direct `WorldData` accessors (`getStatus()`/`setStatus()`, `isPhysics()`/`setPhysics()`, …) alongside the property objects.
* `WorldStatusRegistry` and `NavigatorCategoryRegistry`, exposed via `BuildSystem.getStatusRegistry()` / `getNavigatorCategoryRegistry()`.
* `Displayable.getIconSkullTexture()` / `getHeadProfile()` for custom head icons.
