Перейти к содержимому
Mineforgian

Team-HomeGUI Plugin

A lightweight team system with team homes, private team chat, personal homes, and a clean home GUI

Загрузки
272
Подписчики
2
Обновлён
16 июля 2026 г.
Лицензия
MIT

Опубликован 4 мая 2026 г.

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

  1. Build with Maven: mvn clean package
  2. Copy the resulting .jar from target/ to your server's plugins/ folder
  3. Start the server — config files are generated automatically
  4. Edit plugins/Team-HomeGUI-Plugin/team-config.yml and home-config.yml
  5. Reload with /reload confirm or 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: STARTUP in plugin.yml caused 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 repeated Location.deserialize / IllegalArgumentException: unknown world errors at plugin-enable time).

    What was happening:

    1. load: STARTUP enabled this plugin before any Minecraft world was created (log showed Enabling Team-HomeGUI-Plugin at 23:19:58, then Preparing level "world" only at 23:20:00 — two seconds later).
    2. HomeManager.load() / TeamManager.load() run during onEnable(), deserializing stored Location objects. Location.deserialize() must resolve the world by name; with no worlds registered yet, it throws IllegalArgumentException: unknown world.
    3. Bukkit logs that as an error and returns null for the location; this plugin's loaders skip null locations, so the home/team-home never enters memory.
    4. 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: STARTUP from plugin.yml so the plugin enables in the default POSTWORLD phase (after worlds are ready), which is required for any plugin that deserializes Locations at startup. Relative load order versus DiscordSRV / PlaceholderAPI / Essentials remains handled correctly by the existing softdepend.


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 GUIloc.getWorld().getName() was called without a null check; if a world was deleted or unloaded after a home was saved, opening the GUI caused a NullPointerException and 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 cause ConcurrentModificationException and silent data loss. Changed to ConcurrentHashMap for inner maps.

Bug Fixes — Logic

  • Mass message-key mismatch — dozens of plugin.msg(key) calls used keys that did not exist in team-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 reads settings.chat-format (was hardcoded missing key "team-chat-format") and correctly replaces {team} and calls TeamPlugin.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" added
    • AdminHandler: "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" aligned
    • ListHandler: "list-entry" and "list-footer" added to config
  • SettingsHandler.handleAdminSettings() always returned "team not found" — the handler was reading args[1] (which contains the literal string "settings") instead of args[2] as the team ID. Fixed all admin-settings argument indices accordingly.
  • Admin /team admin settings sub-command was unreachableAdminHandler.handleAdmin() had no "settings" case in its switch statement. Added routing to SettingsHandler.
  • Team.canInvite() ignored Admin role — the public method only checked Owner and membersCanInvite, making the Admin role unable to invite when membersCanInvite=false. Now consistent with TeamHandler.canInvite().
  • No max-members enforcement on inviteTeamHandler.handleInvite() never checked the configured settings.max-members limit. 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.
  • GUISlots enum had duplicate slot valuesDELETE_DYE_* and SET_DYE_* both pointed to slots 29–33 (which were never used; real action slots are 20–24). Replaced with ACTION_1..5 + new CONFIRM_NO / CONFIRM_BED / CONFIRM_YES slots.
  • ListHandler returned teams in random orderConcurrentHashMap.values() has no guaranteed iteration order; added alphabetical sort.

Bug Fixes — Performance / Safety

  • Team.getOwnerName() could block the main threadBukkit.getOfflinePlayer(UUID) may perform disk I/O to read playerdata files. Now checks the online-player cache first, then getOfflinePlayerIfCached() (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 get ConcurrentModificationException. Now returns Set.copyOf().
  • Home name validation inconsistencyHomeService allowed - and had no length cap, while HomeHandler did not allow - and enforced 16 chars. Unified to the stricter ^[a-zA-Z0-9_]{1,16}$ pattern across both paths.
  • gui.show-coordinates default mismatch — Java fallback was true while the config file default is false. Changed Java fallback to false.

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} placeholders
  • settings-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 / TeamManagersave() not synchronized → homes/teams lost on server shutdown saveAsync() dispatches a BukkitRunnable to a worker thread. When the server stops, onDisable() calls save() on the main thread while that worker thread may still be executing a previous save(). Because YamlConfiguration is not thread-safe, the two threads interleaved their data.set(…) and data.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. Both save() methods are now synchronized. Additionally, onDisable() calls getScheduler().cancelTasks(this) before saving, eliminating the race window entirely.

  • HomeService.getPlayerMaxHomes() — wrong config key → home limit always 2 The method read settings.default-max-homes but the actual key in home-config.yml is default-max-homes (root level, no settings prefix). The missing prefix caused getInt() to always fall back to its hardcoded default of 2, capping every player at two homes regardless of what was configured. Fixed to read the correct key default-max-homes.

🟡 Medium

  • HomeService.teleportHome() — wrong path and wrong type for disabled-worlds → world blacklist never enforced via GUI getString("settings.disabled-worlds", "") was wrong on two counts: the path prefix settings. does not exist in home-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 into world_nether and world_the_end through the GUI regardless of the blacklist. Fixed to use getStringList("disabled-worlds") at the correct root path.

  • HomeHandler.handlePlayerSetHome()player.getLocation() not cloned before storage homeManager.setHome(…, player.getLocation()) stored a direct reference to Bukkit's mutable Location object. Bukkit may reuse this object internally, meaning the stored coordinates could silently change after the call. Fixed by passing player.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 called handleAdminCreate() with a mismatched args array (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 via TeamManager.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 to HomeHandler.handlePlayerSetHome().

  • Dual cooldown maps — players could bypass home teleport cooldown The /home command (HomeHandler) wrote cooldowns to HomeManager.teleportCooldowns while the GUI (HomeService / MenuListener) wrote to CooldownManager.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.teleportCooldowns has been removed entirely. All three paths — /home command, personal-home GUI click, and team-home GUI click — now use CooldownManager exclusively.

🟡 Medium

  • CooldownManager.checkTeamCooldown() — permission node teamplugin.team.nocooldown was missing from plugin.yml The permission existed in code but was never declared, so permission-management plugins could not grant it and the bypass never worked. Added to plugin.yml with default: op.

  • MenuListener — wrong config key used for delete-home success message The listener looked up messages.del-success, a key that does not exist in home-config.yml. The correct key is messages.delhome-success. Players always saw the hardcoded fallback string instead of the configured message.

  • saveAsyncFuture() — unsafe method still callable Both TeamManager and HomeManager kept a saveAsyncFuture() method that was documented as unsafe (causes ConcurrentModificationException with FileConfiguration). The method is now marked @Deprecated and throws UnsupportedOperationException to 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 in TeamPlugin.onEnable() to drain stale entries from the cooldown maps.

🟢 Minor

  • AdminHandler — hardcoded § colour codes handleAdminList() and showTeamInfo() used raw § codes which bypass the plugin's colour API. Replaced with &-codes processed via TeamPlugin.color().

Added

  • TeamPlugin.getCooldownManager() — public getter so handlers can share the single CooldownManager instance without reaching through homeService.

Changed

  • HomeManager: teleportCooldowns map and its getter/setter removed. Old homes.yml files that contain a cooldown sub-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 failsAdminHandler.handleAdminCreate() was reading args[1] (the literal string "create") as the password instead of args[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 reading args[2].
  • Tab-completing /team home teleports the playerTeamCommand.onTabComplete() had a case "home" branch inside the args.length == 2 block that called plugin.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 and canSetHome() validation path inside HomeService. Fixed by routing the call through HomeService.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 happensHomeService.teleportHome() now calls canTeleportHome() 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 destination Location could be null, so the actual player.teleport() silently failed or threw a NullPointerException.
  • Home limit bypass via EssentialsX /sethomeEssentialsInterceptListener (priority HIGHEST) now intercepts all /home and /sethome events 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 checkHomeService.setHome() now calls canSetHome() 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.
  • softdepend in plugin.yml now includes Essentials to 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 - /sethome now 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%
  • 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

Комментарии

Загружаем…