1.3.0
Compatibility
Changes
[1.3.0] — Bug-fix & Feature Release
New Feature
- Confirm-Delete Home GUI — clicking the red dye (delete) button in the Home GUI now
opens a 27-slot confirmation dialog instead of deleting instantly.
- Slot 11 (user-facing 12) → 🔴 Red glass pane "No" — cancel
- Slot 13 (user-facing 14) → 🛏 Red bed "Delete Home?" — centre icon
- Slot 15 (user-facing 16) → 🟢 Lime glass pane "Yes" — confirm permanent deletion
Critical Bug Fixes
- NPE crash when opening Home GUI —
loc.getWorld().getName()was called without a null check; if a world was deleted or unloaded after a home was saved, opening the GUI caused aNullPointerExceptionand crashed the inventory for that player. Now shows"World no longer exists"gracefully for both personal homes and team home. - Race condition in HomeManager — inner
HashMap<String, Location>was not thread-safe; the async save thread iterating it while the main thread wrote to it could causeConcurrentModificationExceptionand silent data loss. Changed toConcurrentHashMapfor inner maps.
Bug Fixes — Logic
- Mass message-key mismatch — dozens of
plugin.msg(key)calls used keys that did not exist inteam-config.yml, causing players to see raw"Message not found: <key>"strings. All handlers now reference the correct existing keys or new keys added in this release:TeamHandler:invite-no-permission,invite-already-in-team,leave-success,player-left-team,player-not-in-team,kick-no-permission,cannot-kick-owner,kick-broadcast(updated placeholders{kicker}/{kicked})ChatHandler:chat-already-enabled,chat-already-disabled; team-message format now readssettings.chat-format(was hardcoded missing key"team-chat-format") and correctly replaces{team}and callsTeamPlugin.color()SettingsHandler: all promote / demote / transfer / name / invite messages now match config keys;"settings-name-success","settings-invite-changed","settings-transfer-success/received/broadcast"and"settings-promote-self-error"addedAdminHandler:"wrong-password","admin-team-already-exists","admin-ban-success","admin-unban-success","admin-add-success/target","admin-already-banned","admin-not-banned","admin-disbanned-target","admin-remove-team-success"alignedListHandler:"list-entry"and"list-footer"added to config
SettingsHandler.handleAdminSettings()always returned "team not found" — the handler was readingargs[1](which contains the literal string"settings") instead ofargs[2]as the team ID. Fixed all admin-settings argument indices accordingly.- Admin
/team admin settingssub-command was unreachable —AdminHandler.handleAdmin()had no"settings"case in its switch statement. Added routing toSettingsHandler. Team.canInvite()ignored Admin role — the public method only checked Owner andmembersCanInvite, making the Admin role unable to invite whenmembersCanInvite=false. Now consistent withTeamHandler.canInvite().- No max-members enforcement on invite —
TeamHandler.handleInvite()never checked the configuredsettings.max-memberslimit. Teams could grow without bound. Fixed. - Self-invite not caught — a player could invite themselves; added explicit check using
existing
"invite-self-error"config key. - Pending-invite not checked — a target with a pending invite from any team could receive
multiple invites. Now uses the existing
"invite-already-pending"key. GUISlotsenum had duplicate slot values —DELETE_DYE_*andSET_DYE_*both pointed to slots 29–33 (which were never used; real action slots are 20–24). Replaced withACTION_1..5+ newCONFIRM_NO / CONFIRM_BED / CONFIRM_YESslots.ListHandlerreturned teams in random order —ConcurrentHashMap.values()has no guaranteed iteration order; added alphabetical sort.
Bug Fixes — Performance / Safety
Team.getOwnerName()could block the main thread —Bukkit.getOfflinePlayer(UUID)may perform disk I/O to read playerdata files. Now checks the online-player cache first, thengetOfflinePlayerIfCached()(no I/O), and only falls back to the disk call for completely unknown players.- WarmupListener did not restore custom walk/fly speed — after a warmup the speed was always reset to Bukkit defaults (0.2 / 0.1), overriding server-rank speed multipliers. The actual speeds are now saved before freezing and restored on cancel or completion.
HomeManager.getHomeNames()returned a mutable live view — callers iterating the result while a concurrent modification occurred could getConcurrentModificationException. Now returnsSet.copyOf().- Home name validation inconsistency —
HomeServiceallowed-and had no length cap, whileHomeHandlerdid not allow-and enforced 16 chars. Unified to the stricter^[a-zA-Z0-9_]{1,16}$pattern across both paths. gui.show-coordinatesdefault mismatch — Java fallback wastruewhile the config file default isfalse. Changed Java fallback tofalse.
Config Changes (team-config.yml)
Added keys:
cannot-kick-owner, team-full, chat-already-enabled, chat-already-disabled,
list-entry, list-footer, admin-target-is-banned, admin-add-success,
admin-add-target-msg, admin-already-banned, admin-not-banned,
admin-removed-from-team, admin-cannot-remove-admin-team, admin-disbanned-target,
admin-remove-team-success, settings-name-success, settings-name-changed-broadcast,
settings-name-failed, settings-invite-changed, settings-invite-changed-broadcast,
settings-transfer-success, settings-transfer-received, settings-transfer-broadcast,
settings-help, admin-settings-help, settings-promote-self-error,
usage-settings-name/color/invite/promote/demote/transfer, usage-admin-settings
Updated keys:
kick-broadcast→ now uses{kicker}and{kicked}placeholderssettings-name-broadcast→ now uses{name}and{player}placeholders
Projects on Modrinth are automatically available through a Maven repository for use with JVM build tools such as Gradle. To learn more about the Modrinth Maven API, click here.
Note: When available, you should use the creator's maven repo instead as it will have transitive dependency information that the Modrinth Maven API does not. You may also end up with duplicate dependencies if you use a mix of Modrinth and non-Modrinth Maven repositories for your dependencies, because the group identifier will be different when served through the Modrinth Maven API.
Maven coordinates:
Version ID:
build.gradle:
repositories {
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
// forRepositories(fg.repository) // Uncomment when using ForgeGradle
filter {
includeGroup "maven.modrinth"
}
}
}
// Standard Gradle dependency
dependencies {
implementation "maven.modrinth:G9WejUAk:udQlH032"
}
// Legacy Loom dependency
dependencies {
modImplementation "maven.modrinth:G9WejUAk:udQlH032"
}

