
Team-HomeGUI-Plugin
Version 1.3.0 | PaperMC 1.21 | Java 21
A feature-rich PaperMC plugin combining a team system with a GUI-based home manager, cooldowns, economy support, PlaceholderAPI integration, and Essentials override.
Features
| Category | Feature |
|---|---|
| Teams | Create, invite, leave, kick, disband |
| Roles | Owner / Admin / Member hierarchy |
| Team Chat | Toggle private/global chat per-player |
| Team Home | Set and teleport to team home |
| Personal Homes | Up to 5 personal homes via inventory GUI |
| Confirm Delete | Confirmation GUI before deleting a home |
| Cooldowns | Per-player teleport cooldown |
| Warmup | XP-bar countdown with optional freeze |
| Economy | Vault-based charges for set / teleport / create |
| Anti-PvP | Disable friendly fire between team members |
| PlaceholderAPI | %team_*% placeholders |
| Essentials | Intercepts /home and /sethome to open the GUI |
Requirements
| Dependency | Required |
|---|---|
| PaperMC 1.21 | ✅ |
| Java 21 | ✅ |
| Vault | Optional (economy features) |
| PlaceholderAPI | Optional |
| EssentialsX | Optional (GUI override) |
Installation
- Build with Maven:
mvn clean package - Copy the resulting
.jarfromtarget/to your server'splugins/folder - Start the server — config files are generated automatically
- Edit
plugins/Team-HomeGUI-Plugin/team-config.ymlandhome-config.yml - Reload with
/reload confirmor restart
Commands
Player Commands
| Command | Description |
|---|---|
/team create <name> |
Create a new team |
/team invite <player> |
Invite a player to your team |
/team invite accept |
Accept a pending invite |
/team invite deny |
Deny a pending invite |
/team leave |
Leave your team (owner must confirm) |
/team kick <player> |
Kick a member (owner/admin) |
/team info [team] |
Show team info |
/team members [team] |
List team members |
/team list [page] |
Browse all teams |
/team home |
Teleport to team home |
/team sethome |
Set the team home (owner/admin) |
/team chat enable|disable |
Toggle team-only chat |
/team msg <message> |
Send a team message |
/team color <color> |
Change team display color |
/team settings name <name> |
Rename your team |
/team settings color <color> |
Change color |
/team settings members-can-invite <on|off> |
Toggle invite permissions |
/team settings promote <player> |
Promote to Admin |
/team settings demote <player> |
Demote from Admin |
/team settings transfer <player> |
Transfer ownership |
/home |
Open home GUI |
/sethome [name] |
Set a home (opens GUI) |
/delhome <name> |
Delete a home |
Admin Commands
| Command | Description |
|---|---|
/team admin create <password> |
Create the admin team |
/team admin add <player> |
Add player to admin team |
/team admin ban <player> |
Ban from admin commands |
/team admin unban <player> |
Unban from admin commands |
/team admin manage <team> |
Show team details |
/team admin remove <team> |
Disband a team |
/team admin list |
List all teams |
/team admin settings <team> <option> [value] |
Modify any team's settings |
Permissions
| Permission | Description | Default |
|---|---|---|
teamplugin.create |
Create a team | true |
teamplugin.admin |
Admin commands | op |
teamplugin.sethome |
Set team home | true |
teamplugin.homes.3 |
Unlock 3 personal homes | false |
teamplugin.homes.4 |
Unlock 4 personal homes | false |
teamplugin.homes.5 |
Unlock 5 personal homes | false |
teamplugin.color |
Change team color | true |
Home GUI Layout
[ ][ ][ ][ ][T][ ][ ][ ][ ] ← T = Team Home
[ ][1][2][3][4][5][ ][ ][ ] ← Personal homes (bed icons)
[ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][S][S][S][S][S][ ][ ][ ] ← S = Set (lime dye) or Delete (red dye)
[ ][ ][ ][ ][ ][ ][ ][ ][ ]
Delete Confirmation GUI (new in 1.3.0)
[ ][ ][ ][ ][ ][ ][ ][ ][ ]
[ ][ ][🔴][ ][🛏][ ][🟢][ ][ ]
[ ][ ][ ][ ][ ][ ][ ][ ][ ]
No Delete? Yes
(slot 12) (slot 14) (slot 16) ← user-visible slot numbers (1-indexed)
PlaceholderAPI
| Placeholder | Returns |
|---|---|
%team_name% |
Team display name |
%team_color% |
Team color code |
%team_role% |
Player role (Owner/Admin/Member) |
%team_count% |
Number of members |
%team_has_home% |
true / false |
%team_homes_count% |
Number of personal homes set |
Building
git clone <repo>
cd Team-HomeGUI-Plugin
mvn clean package
# Output: target/Team-HomeGUI-Plugin-1.3.0.jar
Ченджлог
1.3.3Релиз1.21.9, 1.21.10, 1.21.11 · 16 июля 2026 г.
[1.3.3] — Real Root Cause of "Homes Lost on Every Restart" (confirmed via server log)
Critical Bug Fix
BUG-09:
load: STARTUPinplugin.ymlcaused every home/team-home to be silently dropped on every single server start, which is the actual cause behind the "restart wipes all home data" report (BUG-08's corruption-safety fix in 1.3.2 was good defensive practice, but was not the real trigger — confirmed by a live server log showing no corruption warnings, but repeatedLocation.deserialize/IllegalArgumentException: unknown worlderrors at plugin-enable time).What was happening:
load: STARTUPenabled this plugin before any Minecraft world was created (log showedEnabling Team-HomeGUI-Pluginat 23:19:58, thenPreparing level "world"only at 23:20:00 — two seconds later).HomeManager.load()/TeamManager.load()run duringonEnable(), deserializing storedLocationobjects.Location.deserialize()must resolve the world by name; with no worlds registered yet, it throwsIllegalArgumentException: unknown world.- Bukkit logs that as an error and returns
nullfor the location; this plugin's loaders skipnulllocations, so the home/team-home never enters memory. - On the next graceful
/stop,save()faithfully writes the now-incomplete in-memory state back to disk — permanently erasing the data, every time.
Fix: removed
load: STARTUPfromplugin.ymlso the plugin enables in the defaultPOSTWORLDphase (after worlds are ready), which is required for any plugin that deserializesLocations at startup. Relative load order versus DiscordSRV / PlaceholderAPI / Essentials remains handled correctly by the existingsoftdepend.
1.3.0Релиз1.21.9, 1.21.10, 1.21.11 · 2 июня 2026 г.
[1.3.1] — Click Event Fix
Critical Bug Fixes
- Inventory item jitter on click — clicking on valid inventory menu items would bounce back
to their original position. The issue was that
event.setCancelled(true)was called for ALL clicks before checking if the click was actually on a valid menu slot. Now the event is only cancelled for clicks on valid interactive items (Team Home banner, personal homes, action buttons, and confirm-delete dialog). Clicks outside these areas are ignored naturally without artificial cancellation.
1.3.0Релиз1.21.9, 1.21.10, 1.21.11 · 31 мая 2026 г.
[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
1.2.0Релиз1.21.9, 1.21.10, 1.21.11 · 26 мая 2026 г.
[1.2.0] — Bug Fix Release
Fixed
🔴 Critical
HomeManager/TeamManager—save()not synchronized → homes/teams lost on server shutdownsaveAsync()dispatches aBukkitRunnableto a worker thread. When the server stops,onDisable()callssave()on the main thread while that worker thread may still be executing a previoussave(). BecauseYamlConfigurationis not thread-safe, the two threads interleaved theirdata.set(…)anddata.save(…)calls, producing a corrupted or empty file. The result was that all personal homes (and team data) were silently wiped every time the server restarted. Bothsave()methods are nowsynchronized. Additionally,onDisable()callsgetScheduler().cancelTasks(this)before saving, eliminating the race window entirely.HomeService.getPlayerMaxHomes()— wrong config key → home limit always 2 The method readsettings.default-max-homesbut the actual key inhome-config.ymlisdefault-max-homes(root level, nosettingsprefix). The missing prefix causedgetInt()to always fall back to its hardcoded default of2, capping every player at two homes regardless of what was configured. Fixed to read the correct keydefault-max-homes.
🟡 Medium
HomeService.teleportHome()— wrong path and wrong type fordisabled-worlds→ world blacklist never enforced via GUIgetString("settings.disabled-worlds", "")was wrong on two counts: the path prefixsettings.does not exist inhome-config.yml, and the value is a YAML list, not a plain string. The method always received an empty string and silently skipped the world check entirely, allowing players to teleport intoworld_netherandworld_the_endthrough the GUI regardless of the blacklist. Fixed to usegetStringList("disabled-worlds")at the correct root path.HomeHandler.handlePlayerSetHome()—player.getLocation()not cloned before storagehomeManager.setHome(…, player.getLocation())stored a direct reference to Bukkit's mutableLocationobject. Bukkit may reuse this object internally, meaning the stored coordinates could silently change after the call. Fixed by passingplayer.getLocation().clone()instead. (HomeService.setHome()already cloned correctly; this aligns the two code paths.)
Changed
- Version bumped
1.1.0 → 1.2.0.
1.1.0Релиз1.21.9, 1.21.10, 1.21.11 · 21 мая 2026 г.
[1.1.0] — Bug Fix Release
Fixed
🔴 Critical
AdminHandler.handleAdminAdd()— admin team auto-creation always failed Previously the method calledhandleAdminCreate()with a mismatchedargsarray (args[2]was the placeholder string"dummy"instead of the real password). The password check always failed, so/team admin add <player>silently broke whenever the admin team did not yet exist. The method now creates the admin team directly viaTeamManager.createTeam(), skipping the redundant password validation entirely.HomeService.canSetHome()— economy charged before slot-limit check If a player had already reached their home limit, the old code debited their balance first and then checked the limit, causing money to be lost without a home being set. Validation order is now: name format → slot limit → economy charge. The same fix was applied toHomeHandler.handlePlayerSetHome().Dual cooldown maps — players could bypass home teleport cooldown The
/homecommand (HomeHandler) wrote cooldowns toHomeManager.teleportCooldownswhile the GUI (HomeService/MenuListener) wrote toCooldownManager.homeCooldowns. Because neither path read the other map a player could alternate between the command and the GUI to teleport with no cooldown delay.HomeManager.teleportCooldownshas been removed entirely. All three paths —/homecommand, personal-home GUI click, and team-home GUI click — now useCooldownManagerexclusively.
🟡 Medium
CooldownManager.checkTeamCooldown()— permission nodeteamplugin.team.nocooldownwas missing fromplugin.ymlThe permission existed in code but was never declared, so permission-management plugins could not grant it and the bypass never worked. Added toplugin.ymlwithdefault: op.MenuListener— wrong config key used for delete-home success message The listener looked upmessages.del-success, a key that does not exist inhome-config.yml. The correct key ismessages.delhome-success. Players always saw the hardcoded fallback string instead of the configured message.saveAsyncFuture()— unsafe method still callable BothTeamManagerandHomeManagerkept asaveAsyncFuture()method that was documented as unsafe (causesConcurrentModificationExceptionwithFileConfiguration). The method is now marked@Deprecatedand throwsUnsupportedOperationExceptionto prevent accidental future use.CooldownManager.cleanupExpiredCooldowns()— never called → slow memory leak The cleanup method existed but was never scheduled. A repeating async task (every 10 minutes) is now registered inTeamPlugin.onEnable()to drain stale entries from the cooldown maps.
🟢 Minor
AdminHandler— hardcoded§colour codeshandleAdminList()andshowTeamInfo()used raw§codes which bypass the plugin's colour API. Replaced with&-codes processed viaTeamPlugin.color().
Added
TeamPlugin.getCooldownManager()— public getter so handlers can share the singleCooldownManagerinstance without reaching throughhomeService.
Changed
HomeManager:teleportCooldownsmap and its getter/setter removed. Oldhomes.ymlfiles that contain acooldownsub-key are silently ignored on load and not written back, providing a clean migration path.- Version bumped
1.0.9 → 1.1.0.
1.0.9Релиз1.21.9, 1.21.10, 1.21.11 · 17 мая 2026 г.
[1.0.9] - 2026-05-17
Fixed
- Admin team creation always fails —
AdminHandler.handleAdminCreate()was readingargs[1](the literal string"create") as the password instead ofargs[2](the actual password supplied by the player). Every/team admin create <password>call would compare"create"against the configured password, causing it to always fail with "incorrect password". Fixed by readingargs[2]. - Tab-completing
/team hometeleports the player —TeamCommand.onTabComplete()had acase "home"branch inside theargs.length == 2block that calledplugin.getHomeService().teleportHome(player, args[0])(i.e., teleported the player to home"home"every time they pressed Tab after/team home). Replaced with a proper tab-completion loop that suggests the player's existing home names. - GUI sethome bypasses economy charge — Clicking the LIME_DYE "Set Home" button in the Home GUI called
homeManager.setHome()directly, skipping the economy deduction andcanSetHome()validation path insideHomeService. Fixed by routing the call throughHomeService.setHome(), which handles both economy and limit checks consistently with the command-based flow.
1.0.7Релиз1.21.9, 1.21.10, 1.21.11 · 8 мая 2026 г.
[1.0.7] - 2026-05-08
Added
- XP Bar Countdown — During the teleport warmup, the experience bar now counts down visually (level number = seconds remaining, bar fill = proportion left). The bar is restored to its original value immediately after the teleport fires or is cancelled.
Fixed
- Warmup shows but teleport never happens —
HomeService.teleportHome()now callscanTeleportHome()before starting the warmup countdown. Previously the validation step was skipped entirely: the cooldown was set, the "Teleporting in N seconds…" message was sent, but the destinationLocationcould benull, so the actualplayer.teleport()silently failed or threw aNullPointerException. - Home limit bypass via EssentialsX
/sethome—EssentialsInterceptListener(priorityHIGHEST) now intercepts all/homeand/sethomeevents regardless of arguments, cancels them before EssentialsX can act, and opens our Home GUI instead. This prevents EssentialsX from writing homes to its own userdata and bypassing our home-count limits. setHome()skipped limit check —HomeService.setHome()now callscanSetHome()before writing to storage, enforcing the configured home maximum on every code path.
Changed
/home [anything]→ always opens GUI — Typing/home,/home home1, or any variant now opens the Home GUI. Players select which home to teleport to from there./sethome [anything]→ always opens GUI — Typing/sethome,/sethome myhouse, or any variant now opens the Home GUI. Players pick which slot to set from there.softdependinplugin.ymlnow includesEssentialsto guarantee correct load order relative to EssentialsX.
1.0.6Релиз1.21.9, 1.21.10, 1.21.11 · 7 мая 2026 г.
[1.0.6] - 2026-05-07
Added
- Team SetHome Command - Team owners can now set shared team homes with
/team sethome - Team Owner Disband - Team owners can disband teams by using
/team leave - Enhanced PlaceholderAPI - Added 18 new placeholders for teams and homes
- GUI Color Improvements - Visual distinction: cyan (ready), gray (unset), black (no permission)
- Missing Messages - Added "owner-cannot-leave" and "team-sethome-success" messages
Changed
- Major Architecture Refactoring - Complete code quality improvement following SOLID principles
- Eliminated God Class - TeamCommand.java reduced from ~400 lines to ~100 lines, now only handles routing
- New Service Layer - Created HomeService, CooldownManager, TeamValidator for better separation of concerns
- Handler Refactoring - Moved chat, color, and list logic from TeamCommand to dedicated handler classes
- Code Deduplication - Unified cooldown management and home access logic across the codebase
- Type Safety - Created GUISlots enum to eliminate magic numbers and prevent GUI slot errors
- Validation Centralization - TeamValidator provides consistent team name validation with offensive word filtering
- Memory Management - Improved performance with better object lifecycle management
- Default Warmup Time - Set to 5 seconds instead of 0 for better teleport experience
Fixed
- Message System - All placeholders now work correctly with proper config separation
- Personal Home Messages -
/sethomenow shows correct personal home messages instead of team messages - Config Issues - Fixed missing messages that caused "message not found" errors
- Placeholder Conflicts - Personal homes use home-config, team messages use team-config
- Team Home Messages - Fixed team sethome functionality and messaging
- Owner Leave Functionality - Team owners can now properly disband their teams
Enhanced
- PlaceholderAPI Integration - Added comprehensive placeholders:
- Team:
%team_name%,%team_colored_name%,%team_role%,%team_count%,%team_has_team%,%team_is_owner%,%team_is_admin%,%team_owner%,%team_has_home%,%team_home_world%,%team_home_x%,%team_home_y%,%team_home_z% - Homes:
%team_homes_count%,%team_homes_max%,%team_homes_list%,%team_has_homes%
- Team:
- GUI Visual Experience - Better color coding for different home states
- Code Maintainability - Better code organization and single responsibility principle
- Developer Experience - Clearer code structure and better documentation
Комментарии
Загружаем…