Skip to content

Release 3.16.0#2974

Merged
tastybento merged 34 commits into
masterfrom
develop
May 10, 2026
Merged

Release 3.16.0#2974
tastybento merged 34 commits into
masterfrom
develop

Conversation

@tastybento
Copy link
Copy Markdown
Member

No description provided.

Copilot AI and others added 30 commits May 1, 2026 19:07
When ISLAND_RESPAWN is enabled and the player's home block has been
removed, isSafeLocation() returns false and the respawn fell through to
world spawn (island at 0,0).

Fix: when the home is unsafe, try one block above first (covers slabs /
stairs), then scan the island protection-center upward to find any safe
spot, mirroring the logic already used by getAsyncSafeHomeLocation.

Adds three new unit tests covering the fallback paths.

Agent-Logs-Url: https://github.com/BentoBoxWorld/BentoBox/sessions/575de9c2-c8e6-4d36-a9e5-19775b2ea5ab

Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com>
When all quick sync location checks fail (home, home+1, center offsets),
anchor the respawn at the island protection center (so the player doesn't
appear at world spawn / 0,0) and schedule SafeSpotTeleport on the next
tick to scan the island and teleport the player to the nearest truly safe
spot. Removes the manual upward Y-scan in favour of this comprehensive
async search.

Adds testOnPlayerRespawnSafeSpotTeleportFallback unit test and updates
testOnPlayerRespawnUnsafeHomeNoSafeIslandLocation to explicitly cover
the no-island path (no SafeSpotTeleport triggered).

Agent-Logs-Url: https://github.com/BentoBoxWorld/BentoBox/sessions/48ea2d46-020e-47ab-98fe-9d456a85732b

Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com>
…wning-issue

fix: ISLAND_RESPAWN sends players to 0,0 when home block is removed
Both AdminTeamSetownerCommand and IslandTeamSetownerCommand previously
allowed ownership transfer to a player who already owned their maximum
allowed concurrent islands. AdminTeamSetownerCommand only emitted a
warning after the fact; IslandTeamSetownerCommand had no check at all.
This was a clean bypass of `concurrent-islands` and the per-player
`island.number.<n>` permission cap.

Both commands now compute the recipient's current concurrent island
count and permission-aware cap in canExecute and refuse the transfer
when at or above the limit. The user-facing path uses the existing
`commands.island.team.setowner.errors.at-max` locale entry; the admin
path gets a parallel `commands.admin.team.setowner.errors.at-max`
message that includes the count and limit so the admin can adjust the
recipient's permission if they really intend to allow it.

Closes #2908.
…ds-setowner

Refuse setowner when recipient is at the concurrent-islands cap
…tructureGrowEvent

- Remove the restrictive material filter from onSpread (was KELP/BAMBOO/BAMBOO_SAPLING only)
  so vines, cave vines, twisting vines, weeping vines, and all other spreading plants
  are now blocked when island members are offline
- Use e.getSource().getLocation() for the island check in onSpread (source = the plant
  causing the spread, always on the island)
- Add onStructureGrow(StructureGrowEvent) handler so trees and mushrooms that grow
  from saplings (birch, spruce, acacia, mangrove, etc.) are blocked when offline
- Extract repeated island-member check into private checkGrowth() helper
- Update testOnSpreadMembersOfflineTree: all spread is now blocked (no material filter)
- Add 6 new tests covering the StructureGrowEvent handler (doNothing, membersOnline,
  membersOffline, nonIsland, nonBentoBoxWorld, boneMealMembersOffline)

Agent-Logs-Url: https://github.com/BentoBoxWorld/BentoBox/sessions/31b42c78-d289-4a32-bded-77f467a43797

Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com>
…or multi-island kick

Agent-Logs-Url: https://github.com/BentoBoxWorld/BentoBox/sessions/6e406756-8081-4292-bb63-00f6fe227777

Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com>
…er review feedback

Agent-Logs-Url: https://github.com/BentoBoxWorld/BentoBox/sessions/ee9782ec-6693-42a2-a6e3-d0f6775b1387

Co-authored-by: tastybento <4407265+tastybento@users.noreply.github.com>
…on-home-location

Add regression test confirming island home location is set on the correct island during concurrent creation
…h-issue

Fix OFFLINE_GROWTH: block vine spread and tree growth from saplings
…ck-behavior

AdminTeamKick: require x,y,z coordinates when player is on multiple islands
Mirrors OraxenHook / ItemsAdderHook so addons can render the correct
icon and display name for CraftEngine custom blocks in panels and
GUIs without depending on CraftEngine directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…by-id

Add CraftEngineHook.getItemStack(id) lookup
…-y-value

Fix Dynmap area markers always rendering at y=64
Mirrors the recently-added getItemStack(id) helper. Lets addon command
handlers (e.g. /is value hand in Level addon) recognize a held
CraftEngine custom item without depending on CraftEngine directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…pers

feat(hooks): add CraftEngineHook.getItemId(item) lookup
Introduces WorldSettings#isTeamsDisabled() so game modes can opt out of
the team subsystem on a per-world basis. When enabled, every island/admin
team sub-command except trust, coop, untrust and uncoop refuses to run
and surfaces commands.island.team.errors.teams-disabled. Trust and coop
relationships remain available as the supported alternative.

Adds the admin team disbandall command for stripping pre-existing team
members and sub-owners from every island in a world when flipping the
flag on. Trusted and coop players are left alone.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat: per-world team disable + admin team disbandall (#2965)
Adds commands.admin.team.setowner.errors.at-max to the 22 non-English
locale files so the admin setowner cap-exceeded message is localised
instead of falling back to the raw key.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tastybento tastybento changed the title Release 3.15.1 Release 3.16.0 May 9, 2026
@tastybento tastybento requested a review from Copilot May 9, 2026 22:27
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Release 3.16.0 update that extends world/team configuration capabilities and fixes multiple gameplay/UX edge cases (offline growth handling, respawn safety, map marker rendering), with accompanying tests and locale updates.

Changes:

  • Add “teams disabled” world setting plumbing plus new admin team disbandall migration command, and enforce the setting across several team commands.
  • Improve event handling for offline growth (including StructureGrowEvent) and make island respawn behavior safer via additional fallbacks.
  • Adjust Dynmap area/polygon markers to respect world height ranges; add CraftEngine custom item helpers; bump build version and expand regression tests/locales.

Reviewed changes

Copilot reviewed 50 out of 51 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/test/java/world/bentobox/bentobox/managers/island/NewIslandTest.java Adds regression coverage ensuring island home is set on the correct island instance during creation.
src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/OfflineGrowthListenerTest.java Expands offline growth tests (spread materials + StructureGrowEvent).
src/test/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListenerTest.java Adds tests for unsafe-home respawn fallbacks and SafeSpotTeleport scheduling behavior.
src/test/java/world/bentobox/bentobox/hooks/DynmapHookTest.java Adds tests asserting marker Y-range uses world min/max height.
src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommandTest.java Updates/extends tests for ownership transfer concurrency limits.
src/test/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommandTest.java Adds test coverage for “teams disabled” behavior.
src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamSetownerCommandTest.java Adds test for concurrent-islands cap enforcement on admin setowner.
src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommandTest.java Reworks tests for updated admin kick semantics (xyz disambiguation + tab completion).
src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandAllCommandTest.java New tests for the disbandall confirmation flow and member removal behavior.
src/test/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommandTest.java Adds test ensuring add is refused when teams are disabled.
src/main/resources/locales/zh-HK.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/zh-CN.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/vi.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/uk.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/tr.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/ru.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/ro.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/pt.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/pt-BR.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/pl.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/nl.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/lv.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/ko.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/ja.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/it.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/id.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/hu.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/hr.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/fr.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/es.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/en-US.yml Adds admin team disbandall strings, adjusts admin kick params, adds teams-disabled message key.
src/main/resources/locales/de.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/resources/locales/cs.yml Adds admin setowner “at max concurrent islands” error translation.
src/main/java/world/bentobox/bentobox/managers/IslandWorldManager.java Exposes isTeamsDisabled(World) based on WorldSettings.
src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/OfflineGrowthListener.java Refactors offline growth checks and adds StructureGrowEvent support.
src/main/java/world/bentobox/bentobox/listeners/flags/worldsettings/IslandRespawnListener.java Adds safe respawn fallbacks and SafeSpotTeleport scheduling when needed.
src/main/java/world/bentobox/bentobox/hooks/DynmapHook.java Sets Dynmap area marker Y-range to world height limits.
src/main/java/world/bentobox/bentobox/hooks/CraftEngineHook.java Adds CraftEngine custom item ↔ ItemStack helper methods.
src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java Adds isTeamsDisabled() WorldSettings toggle with Javadoc.
src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamSetownerCommand.java Blocks when teams disabled; enforces concurrent-islands cap on ownership transfer.
src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamPromoteCommand.java Blocks promotion/demotion when teams disabled.
src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamLeaveCommand.java Blocks leave when teams disabled.
src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamKickCommand.java Blocks kick when teams disabled.
src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteCommand.java Blocks invite when teams disabled.
src/main/java/world/bentobox/bentobox/api/commands/island/team/IslandTeamInviteAcceptCommand.java Refuses TEAM invites when teams disabled (trust/coop unaffected).
src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamSetownerCommand.java Enforces concurrent-islands cap when transferring ownership as admin.
src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamKickCommand.java Reworks admin kick to support xyz disambiguation + tab completion.
src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamDisbandAllCommand.java New admin migration command to strip members/sub-owners from all islands in-world.
src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamCommand.java Registers the new admin disbandall subcommand.
src/main/java/world/bentobox/bentobox/api/commands/admin/team/AdminTeamAddCommand.java Blocks admin team add when teams are disabled.
build.gradle.kts Bumps build version to 3.16.0.

Comment thread src/main/resources/locales/en-US.yml
Comment thread src/main/java/world/bentobox/bentobox/api/configuration/WorldSettings.java Outdated
tastybento and others added 2 commits May 9, 2026 16:00
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
- AdminTeamDisbandAllCommandTest: replace any(Long.class) with anyLong()
  to avoid an autounbox NPE during Mockito stubbing.
- Sync the four keys added in #2975 (admin team disbandall.{description,
  confirmation,success} + island team errors.teams-disabled) into all 22
  bundled locales so non-English servers don't surface raw keys.
- Tighten WorldSettings#isTeamsDisabled() Javadoc to list the commands
  actually blocked rather than the broader "every island team *" claim,
  and to call out that read-only commands and trust/coop are unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tastybento
Copy link
Copy Markdown
Member Author

Code Review: PR #2974 — Release 3.16.0

Overview

Bundle of 51 files (1209 additions / 82 deletions, 30 commits) merging seven independent fixes/features for the
3.16.0 release. Major themes:

Strengths

  • API surface change is additive only (new defaults, new methods, new command). Binary-compatible — appropriate
    for a semver-minor bump.
  • Setowner cap fix is applied symmetrically in user and admin paths with the right num >= max boundary.
  • AdminTeamDisbandAllCommand is well-shaped: extends ConfirmableCommand, snapshots UUIDs before mutating the
    member map, filters < OWNER_RANK to keep owners, fires both TeamEvent and IslandEvent, supports console execution.
  • IslandRespawnListener fallback escalates cleanly: home → home+1 → island-center offsets → async
    SafeSpotTeleport(cancelIfFail=true). Anchoring the respawn at the protection center prevents the original 0,0 bug
    even if the async scan fails.
  • OfflineGrowthListener extraction of checkGrowth(Location, Cancellable) is a good DRY refactor; using
    e.getSource().getLocation() for spread is the right anchor for vines.
  • Tests added/updated across 10 classes covering the new code paths.
  • All 22 locale files synced for every new key (verified via missing-key sweep — count is 0).

Issues

  1. AdminTeamKickCommand — owner not excluded from kick targets [real]

AdminTeamKickCommand.java:98 (getMemberIslandsXYZ) filters by hasTeam() only:
return getIslands().getIslands(getWorld(), target).stream()
.filter(Island::hasTeam)
.collect(Collectors.toMap(i -> Util.xyz(i.getCenter().toVector()), i -> i));
If the target is the owner of an island that has a team, that island is included and
getIslands().removePlayer(island, targetUUID) will remove the owner from their own member map — leaving the island
in an inconsistent state. The original code had a different (also-questionable) guard that was dropped here.

Fix: either exclude owner-islands from the candidate map, or refuse with
commands.admin.team.kick.cannot-kick-owner when the target only owns the matched island(s) and direct admins to
setowner/disband. Already flagged by Copilot bot — unresolved.

  1. AdminTeamKickCommand — success-all message key now dead

Original code sent commands.admin.team.kick.success-all after iterating; the new single-island flow only sends
per-island success. The success-all entry still exists in locales but is no longer referenced. Either remove it or
note it as deprecated.

  1. CraftEngineHook — inconsistent paired-API return style

getItemStack(id) returns Optional; getItemId(item) returns nullable String. Pairs of "lookup by id / id
of item" usually align on one style. Minor — the existing hook file already mixes styles, so not a blocker.

  1. IslandRespawnListener — one-tick window at protection-center

When all sync fallbacks fail, the player respawns at island.getProtectionCenter() for one tick before
SafeSpotTeleport runs. If that center is in lava/void/blocks, fall damage or death is possible in that window.
Edge case (only when the island has no safe spot anywhere), but worth a comment so future readers don't "improve"
the protection-center anchor away.

  1. WorldSettings#isTeamsDisabled() — incomplete enforcement vs. Javadoc claim

The docstring lists the action commands accurately now (after the recent fix), but IslandTeamInviteRejectCommand
(and the bare /island team info command) are not gated. That's intentional — rejecting an existing invite or
viewing the team panel is harmless when teams are disabled — but if the docstring promises stricter behavior in
future versions, the listed commands should be exhaustive.

  1. State sharing in CompositeCommand singletons (pre-existing pattern)

AdminTeamKickCommand now stores both targetUUID and island as instance fields between canExecute and execute.
CompositeCommand instances are singletons; concurrent admin invocations would race. This pre-dates the PR (the
targetUUID field existed before), but the PR adds another field to the same shared mutable surface.

Test Coverage

Additions look proportionate:

  • New: AdminTeamDisbandAllCommandTest (187 lines), confirmation flow + event firing
  • Updated: AdminTeamKickCommandTest (multi-island disambiguation, tab completion)
  • Updated: IslandTeamSetownerCommandTest, AdminTeamSetownerCommandTest (cap enforcement)
  • Updated: IslandRespawnListenerTest (unsafe-home + SafeSpotTeleport fallbacks)
  • Updated: OfflineGrowthListenerTest (StructureGrowEvent + arbitrary spread material)
  • Updated: DynmapHookTest, IslandTeamInviteCommandTest, AdminTeamAddCommandTest, NewIslandTest

Gap: no test asserting that AdminTeamKickCommand refuses to kick when the target is the only island's owner —
directly correlates with issue #1 above.

Recommendation

Address #1 (owner kick) before tagging — clean inconsistency with low-cost fix and direct user-visible impact.
Tidy #2 (success-all) opportunistically. The rest are minor/pre-existing.

Risk profile is otherwise low: additive API, default-off feature flag, comprehensive locale sync, version bump
appropriate.

tastybento and others added 2 commits May 9, 2026 16:25
Issue 1: AdminTeamKickCommand could remove an island owner from their
own member map. getMemberIslandsXYZ now filters out owner-islands so
kick only considers islands where the target is a non-owner member.
When all matched team islands are owned by the target, the command
refuses with commands.admin.team.kick.cannot-kick-owner and directs the
admin to setowner / disband. The same refusal fires when an explicit
xyz argument resolves to an island the target owns. The locale message
text is updated to mention setowner/disband by name and synced across
all 22 translations. Two new test cases cover the owner-protection
paths; existing tests are updated so the target is a regular member,
not the owner.

Issue 2: removed the now-dead commands.admin.team.kick.success-all key
from all 22 locale files (en-US never had it; the new single-island
flow no longer emits it).

Issue 5: WorldSettings#isTeamsDisabled() Javadoc rewritten as an
exhaustive list — every command that refuses to run on one side, every
command that intentionally remains available on the other (including
trust/coop, panel/info commands, invite reject, and the admin commands
that operate on existing teams).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous commit added .claude/worktrees/feature-purge-regions-reset
as a gitlink (160000) because `git add -A` swept it in. It is a local
Claude Code worktree, not a submodule. Remove the gitlink and exclude
the worktrees directory going forward.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 9, 2026

@tastybento tastybento merged commit 7c9f431 into master May 10, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants