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.
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.
1. System Requirements
These are hard requirements — the plugin will not start otherwise.
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.
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.bakplugins/BuildSystem/folders.yml.v3.bak
The migration also creates two new files for the now-customizable statuses and categories:
plugins/BuildSystem/statuses.ymlplugins/BuildSystem/categories.yml
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).
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-onbuildsystem.setting.<option>nodes.settings.restrict-template-access— superseded by the deny-to-restrictbuildsystem.create.template.<name>model.
New Keys
settings.world-permission-whitelist(default:[]) — restricts the permission strings/worlds setPermissionmay assign. An empty list allows any value.
4. Permission Changes
Existing command permissions are unchanged. v4 adds the following.
New Static Nodes
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:
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
messages.yml) ChangesDefault English: delete
messages.ymland 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 Worldbecause 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
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-onlyisLoaded()remains.BuildWorld.asProfileable()— resolve head icons throughDisplayable.getHeadProfile()/getIconSkullTexture().WorldData.getConfigFormat()andWorldData.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 inevent.backupandevent.folder.WorldService.importWorlds()for bulk import spread across ticks.Direct
WorldDataaccessors (getStatus()/setStatus(),isPhysics()/setPhysics(), …) alongside the property objects.WorldStatusRegistryandNavigatorCategoryRegistry, exposed viaBuildSystem.getStatusRegistry()/getNavigatorCategoryRegistry().Displayable.getIconSkullTexture()/getHeadProfile()for custom head icons.
Last updated