BentoBox 3.22.0
A commands-and-visibility release. BentoBox now registers its commands with Paper's Brigadier API, so players see subcommands and completions while they type rather than only after pressing enter or TAB. Alongside it, six new bStats charts finally answer which addon versions are actually deployed and which commands players struggle with, and worlds that generate no structures now protect themselves from the /locate-style searches that could freeze a server outright.
It also carries two fixes worth upgrading for on their own: data queued by addons during shutdown is no longer discarded, which was silent, intermittent data loss on every restart, and BentoBox commands are back in the Bukkit command map, so NPCs, signs and GUI plugins can run them again.
Compatibility
✔️ Paper Minecraft 1.21.5 – 26.2 ✔️ Java 25+
Upgrading
- As always, take backups just in case. (Make a copy of everything!)
- Stop the server.
- Replace the BentoBox jar with this one.
- Start the server.
- ⚙️ A new
general.brigadier-commandsoption is added on first run, defaulting on. If another plugin conflicts with Brigadier registration, set it tofalseto fall back to the legacy command map — BentoBox also falls back automatically if it cannot hook the registrar. - 🔺 In worlds whose generator places no structures (classic void skyblock, AOneBlock), structure searches are now suppressed automatically with no configuration. If you have a converted world that contains pre-existing structures, force-enable them per structure in the game mode's own
structuressettings. - 🔡 A new Traditional Chinese (
zh-TW) locale is included. No existing locale keys changed, so custom locale files need no work.
Highlights
- 🔺 Addon data queued during shutdown is no longer discarded — silent, intermittent data loss on every restart, affecting nine bundled addons.
- ⚙️ 🔺 Brigadier command registration — subcommands and completions now appear as the player types, with an off-switch if it clashes with another plugin.
- Six new bStats charts — addon and game mode versions, addon API levels, non-default settings, and how/where commands fail.
- 🔺 Structureless worlds protect themselves — no more main-thread freezes from cartographer trades, dolphins or treasure maps searching a void world to the border.
/island settingsworks out in the world — instead of "You do not have an island!", players see what the world's protection rules let them do where they are standing.Island.setFlagwrites are no longer discarded — setting a protection rank on an island that had no entry for that flag silently did nothing, which is every freshly created island.- Repeated warnings stay throttled — a player alternating between two limit messages no longer bypasses the 4-second notification cooldown.
- BlueMap marker sets stay in their own worlds — an addon's marker set no longer appears in the sidebar of every other map on the server.
- Multiverse tidy-up — BentoBox worlds get
auto-load: falseeven when Multiverse already knew about them, and dead seed-world registrations are gone. - 🔡 Traditional Chinese (
zh-TW) locale contributed by @qwe664.
New Features
⚙️ 🔺 Brigadier command registration
[PR #3036] — Closes #3023 (phases 1 and 2)
The SimpleCommandMap reflection in CommandsManager is replaced with registration through Paper's Brigadier API, and the CompositeCommand subcommand tree is exposed to clients. The practical effect for players is that /island t… offers team in the chat bar immediately, instead of the client knowing nothing until enter or TAB.
Paper only permits Brigadier registration inside its COMMANDS lifecycle callback, which fires before addons are enabled — so game mode commands such as /island and /acid do not exist yet at that point. BrigadierCommandRegistrar therefore captures the live CommandDispatcher inside that window and registers nodes into it afterwards as addons come up. Design consequences worth knowing:
- No stale references. Nodes never capture a
CompositeCommand; they resolve it by label on every use, so a/bentobox reloadthat swaps command objects leaves nothing behind. - Aliases and the
plugin:labelform are redirects, not duplicated trees — a game mode admin tree runs to dozens of nodes and every one is sent to every client. - Console still sees player-only commands, so
/aifrom console reports "This command is only available in-game" rather than Brigadier's generic unknown-command error. - The literal tree is depth-capped at 4. Deeper subcommands still work — they fall through to a greedy argument — they are just not pre-advertised.
⚙️ Config note: guarded by the new
general.brigadier-commands(defaulttrue). Turning it off restores the legacy command map path, which is kept intact as the fallback.
Still to come in phase 3: tooltips on subcommand names, and typed arguments (red-underline validation, quoting-free multi-word island names).
Addon version, feature adoption and command failure metrics
Six new bStats charts, all of them already configured on bstats.org/plugin/bukkit/BentoBox/3555:
| Chart | What it answers |
|---|---|
addonVersions / gameModeVersions |
Which version of each addon and game mode is deployed |
addonApiVersions |
Which BentoBox API levels addons in the wild are built against |
features |
Which settings servers change away from their defaults |
commandFailures |
How commands fail, by kind |
commandFailurePaths |
Which commands fail most |
Previously we knew that a server ran Level, not which Level — leaving no way to see how long the tail is after a release, or to judge when a breaking API change is safe. The settings chart reports only deviations from default, so every bar is a signal: a default-off setting appears under its own name when enabled (chunk-pregen), and a default-on setting appears as no-<name> when disabled (no-brigadier-commands). 24 settings are covered.
Privacy: only the failure kind and a stable command key (derived from the hard-coded permission, e.g.
bskyblock.island.team.invite) are submitted — never arguments, player names, or anything typed. bStats data is public. Distinct keys are capped at 200 between submissions and only the busiest 20 sent.
Three charts were also removed: the addons, gameModeAddons and hooks advanced pies had no matching chart IDs on bstats.org, so every submission was discarded on arrival — which is why the Hooks pie never showed anything. The addonsBar, gameModeAddonsBar and hooksBar charts carry identical data and do work.
🔺 Automatic structure-search suppression in structureless worlds
[PR #3035]
A production server froze repeatedly and entered a reboot loop: a cartographer villager levelling up rolled an explorer-map trade, which runs findNearestMapStructure synchronously on the main thread. In a void world the structure can never be found, so the search evaluates jigsaw placement and full noise columns out to the radius cap and blows past the 60-second watchdog. The level-up never saves, so after the auto-restart the same villager triggers the same search — indefinitely.
StructureListener now asks the world's ChunkGenerator whether it places structures at all. If it does not, every structure search in that world is suppressed with no configuration needed — such a search is always the pathological scan-to-the-cap case, never a success.
- A per-world
structuresentry oftruestill force-enables a structure, as an escape hatch for converted worlds containing pre-existing structures. - Game modes that do generate structures are unaffected: SkyGrid, Boxed (
allow-structures), CaveBlock (overworld) and AcidIsland (make-structures) all reporttrue. AOneBlock and BSkyBlock reportfalseand get automatic protection. No addon changes required.
⚙️ Config note: the
world.disabled-structurescomments now explain how to list valid structure keys in-game (tab-complete/locate structure) and give a concrete example for admins still on released versions.
/island settings opens out in the world
/island settings used to answer "You do not have an island!" whenever the player was not standing on one and owned none — out in the wilderness, or in a game mode where players never own islands at all. That is the moment a player most wants to ask what rules apply where I am standing?
The panel now opens anyway, with a single read-only World Protections tab listing each protection flag as active or disabled for the world. That is the same value an admin edits on their own World Protections tab, and it is what actually governs a player off-island: with no island under them, FlagListener resolves a protection flag to the world's on/off switch rather than to any rank. Can I break blocks out here, can I place them — answered without asking an admin.
The settings tab is not shown in that case: settings are island-level and there is no island to configure. Nothing is clickable, and a player standing on or owning an island sees exactly the panel they always did.
Bug Fixes
🔺 Database writes queued during shutdown were discarded
[PR #3044]
Queued writes are drained by an async repeating task that stops the moment BentoBox is disabled, after which store(...) silently dropped anything still queued. The server disables Pladdons before BentoBox, so everything an addon persisted from onDisable() was queued into a pipeline about to be abandoned — whether the drain task got a tick in first was a race, which is why this presented as intermittent data loss with nothing in the log.
Nine bundled addons save state in onDisable and all nine are Pladdons: Boxed, AOneBlock, Raft, Challenges, Limits, DragonFights, CheckMeOut, ControlPanel, InvSwitcher. Third-party Pladdons doing the same were affected identically. Reproduced end to end with InvSwitcher: pick up items, restart while still connected, and the shutdown save never landed — then the stale stored inventory was re-applied on join, making it an active rollback rather than just a lost write.
Shutdown writes are now drained before the database is closed.
🔺 BentoBox commands unusable from NPCs, signs and GUI plugins
Registering commands with Brigadier replaced the Bukkit command map registration outright, which took BentoBox commands out of the map every other plugin dispatches through. An NPC running /oneblock go for a player answered with the server's no-permission message and ran nothing, while typing the same command by hand worked and operators saw nothing wrong.
On Paper the command map is a facade over the Brigadier dispatcher, and it synthesises a Bukkit view of whatever node it finds there. A node registered outside Paper's own command API carries no APICommandMeta, so that view is a VanillaCommandWrapper — which demands minecraft.command.<label> and reports the no-permission message before the command ever runs. Anything resolving commands through the map was affected: NPC plugins, command signs, GUI plugins.
Commands are now registered with the command map as well, and first, so Brigadier merges the subcommand tree onto Paper's node instead of replacing it. Clients still get the literal tree, and the command map still hands back the real CompositeCommand, which checks its own permissions and reports for itself.
This only ever affected 3.22.0 development builds. Anyone running one can drop the
general.brigadier-commands: falseworkaround after upgrading.
Late-registered addon subcommands missing from completions
A top-level command's Brigadier children are baked in when its node is built, which happens before addons register their subcommands — so every addon subcommand missed the tree. They still ran, and still appeared in /<gamemode> help, but the client was never told they exist. refreshTrees() now rebuilds every registered top-level command at the end of enableAddons() and again after allLoaded(), relying on Brigadier merging same-named literals rather than replacing them.
The change-detection this relied on compared child counts read back from dispatcher.getRoot(), which returns Paper's wrapped node while the merge lands on the NMS copy — so the count never changed and the refreshed tree was never pushed to clients. PR #3051 resolves that as well: the refresh now compares the tree as it was built, which is the tree that was grafted on, so late subcommands reach clients as intended.
Permission-hidden subcommands failing with a Brigadier parse error
[PR #3042] — Fixes Border#179
/oneblock border color green produced an unhandled CommandException and never changed the border colour. Permission checks live in each literal's Brigadier requires predicate, and Brigadier silently skips a literal the source cannot use — so a player without the permission dead-ended at color with a red INCORRECT_ARGUMENT instead of "you do not have permission", and the line never reached CompositeCommand. Worse, the did-you-mean handler then suggested back the very line that had just failed, and re-dispatching it threw out of a scheduled task.
A command line of ours that Brigadier refuses to parse is now dispatched through the command tree, which checks its own permissions and reports for itself. The suggester never offers a line identical to what was typed, and dispatch failures are logged rather than escaping.
BlueMap marker sets attached to every map on the server
[PR #3041]
createMarkerSet() put every new marker set into every map, so an addon registering a warps set for one game mode had it appear in the sidebar of every other game mode's map, and of any non-BentoBox world's map too. A set created but never populated was listed everywhere, permanently empty. Worlds are now learned lazily from the markers added to a set, and only those worlds' maps get it. Island marker sets were never affected.
Also fixed: createMarkerSet() and removeMarkerSet() threw an NPE when called before BlueMap finished loading or during a /bluemap reload. getBlueMapAPI() is corrected from @NonNull to @Nullable (annotation-only, binary compatible).
Multiverse auto-load never cleared for known worlds
[commit 45c5402] — Relates to #3037
registerWorld() set auto-load: false inside a peek() on the import result, so it only ever ran on a first-time import. If Multiverse already had the world in worlds.yml the import fails with WORLD_EXIST_LOADED, the peek is skipped, and the world keeps auto-loading — which is what every BentoBox world in #3037's worlds.yml shows. The world is now looked up after the import and the flag cleared there. BentoBox creates and loads its own worlds in onEnable; Multiverse should not be autoloading them.
Dead seed-world Multiverse registration removed
Seed worlds (<world>/bentobox) were dropped in 3.16.1, but IslandWorldManager still looked one up for every game mode world and registered it with the world management hooks — always returning null. This registration is why stale acid/bentobox entries linger in Multiverse's worlds.yml: BentoBox deleted the folder on shutdown but never unregistered it. Removing the call stops future versions re-adding such an entry; existing stale entries still need /mv remove.
Island.setFlag silently discarded writes for unset flags
[PR #3053]
setFlag(Flag, int, boolean) only wrote when the flag was already a key in the island's flag map. Islands created through IslandsManager.createIsland start with an empty map, so an addon setting a protection rank on a fresh island had the write dropped with no error and no log line, and the flag stayed on its default rank. It was easy to miss because setSettingsFlag() writes unconditionally — SETTING and WORLD_SETTING flags worked fine while PROTECTION ranks quietly did not.
Absent flags are now written; the island is still only marked changed when the value actually differs. Addons that worked around this by pre-seeding the map can drop the workaround.
Notification throttle bypassed by alternating messages
[PR #3054] — Relates to Limits#293
Notifier remembered only the single last message sent to a user and sent whenever the new one differed. A player hitting two different limits back to back — mixed animal and monster limit messages while mining in AOneBlock — therefore bypassed the 4-second cooldown entirely: each message differed from the one before it, so every one of them spammed through.
The cache is now keyed by user and message, so each distinct message is throttled independently within the window.
Zombie flag listeners from addons dropped for missing dependencies
[PR #3048]
loadAddon() runs an addon's onLoad() before the hard-dependency check, so an addon later dropped by sortAddons() had already registered its flags — and FlagsManager registers flag listeners immediately, under the BentoBox plugin. Those listeners survived the drop: registered, firing on global events, backed by an addon that never enabled and never created its worlds. Removing Level from a server running ChunkBlock (which hard-depends on it) turned every PlayerJoinEvent into an NPE from Util.sameWorld().
Dropped addons are now marked MISSING_DEPENDENCY and have their flags and listeners unregistered, and Util.sameWorld() returns false for a null world rather than throwing.
Other Improvements
- 🔡 Traditional Chinese (
zh-TW) locale added, translated from the latesten-US.ymlwith placeholders and MiniMessage tags preserved [PR #3046] — thanks @qwe664. - SonarCloud reliability fixes across
AddonClassLoader,User,PVPListener,PlaceholderGrouper,PurgeRegionsService, the Multiverse hooks and the placeholder dump command, plus the Modrinth publish action pinned to a fixed SHA [commit c5d66ca]. - Refactored to
getFirst()for readability in several classes [commit 23c2b84]. - CI: the shared platform-publish workflow pin was bumped to the multipart-JSON fix, so a semicolon anywhere in a release body no longer truncates the CurseForge and Hangar upload metadata [PR #3047].
- Test suite grew to 3430 tests, 0 failures, with new coverage for Brigadier registration, tree refresh and command map registration, bStats accumulation and capping, command failure classification, structureless-world suppression, BlueMap world scoping, the unparseable-command path, the shutdown write drain, per-message notification throttling and the off-island settings panel.
Legend
| Marker | Meaning |
|---|---|
| 🔡 | Locale files may need regenerating or updating |
| ⚙️ | Config options added, renamed or removed |
| 🔺 | Special attention needed |
What's Changed
- 🔺 Auto-suppress structure searches in structureless worlds by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3035
- ⚙️ 🔺 Register commands with Paper's Brigadier API (#3023 phases 1 and 2) by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3036
- Add addon version, feature adoption and command failure metrics by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3038
- Rebuild the Brigadier tree once addons have enabled by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3040
- Scope addon-created BlueMap marker sets to their own worlds by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3041
- Run our own commands that Brigadier will not parse by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3042
- 🔺 fix: do not discard database writes queued during shutdown by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3044
- 🔡 Add Traditional Chinese (zh-TW) localization by @qwe664 in https://github.com/BentoBoxWorld/BentoBox/pull/3046
- Clean up flags of addons dropped for missing dependencies by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3048
- ci: bump publish-platforms pin to the multipart-JSON fix by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3047
- 🔺 Keep BentoBox commands in the Bukkit command map by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3051
- Fix Island.setFlag silently ignoring flags not already in the map by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3053
- Show the settings panel when the player has no island by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3055
- Throttle notifications per distinct message, not just the last one by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3054
- Show the world's protection flags when the player is not on an island by @tastybento in https://github.com/BentoBoxWorld/BentoBox/pull/3056
New Contributors
- @qwe664 made their first contribution in https://github.com/BentoBoxWorld/BentoBox/pull/3046
Full Changelog: https://github.com/BentoBoxWorld/BentoBox/compare/3.21.0...3.22.0
Information
| Published on | August 1, 2026 |
|---|---|
| Downloads | 13 |