From b47e40c80d50a6730e666054678704e28be0cd6c Mon Sep 17 00:00:00 2001
From: lokspel <208148594+lokspel@users.noreply.github.com>
Date: Sun, 17 May 2026 22:16:43 +0400
Subject: [PATCH 1/4] Update dependencies (26.1.2 is supported now)
---
pom.xml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/pom.xml b/pom.xml
index 4e520441..5483d049 100644
--- a/pom.xml
+++ b/pom.xml
@@ -117,7 +117,7 @@
- * ARCHITECTURE PRINCIPLE: - * This class EXCLUSIVELY uses TAB's API when TAB is available. - * It does NOT contain any internal nametag logic - that belongs in NametagManager. - *
- * Key principles: - *
- * Usage Pattern: - *
- * NametagManager (orchestrator) - * ├─ Detects if TAB is available - * ├─ If TAB available: delegates to TabIntegration → uses TAB API exclusively - * └─ If TAB not available: uses internal packet-based system - *- */ public class TabIntegration { + private static final LegacyComponentSerializer LEGACY = LegacyComponentSerializer.legacySection(); + private final TabAPI tabAPI; + private NameTagManager nameTagManager; + private TabListFormatManager tabListFormatManager; + @Getter private final boolean available; - @Getter - private final boolean tablistFormattingEnabled; public TabIntegration() { TabAPI api = null; - boolean isAvailable; - boolean tablistEnabled = false; + NameTagManager nametags = null; + TabListFormatManager tabList = null; try { api = TabAPI.getInstance(); - isAvailable = api != null; - - // Check if TAB's tablist-name-formatting feature is enabled using their API - if (isAvailable) { - tablistEnabled = checkTablistFormattingEnabled(api); + if (api != null) { + nametags = api.getNameTagManager(); + tabList = api.getTabListFormatManager(); } - } catch (NoClassDefFoundError | Exception e) { - // TAB API not available - isAvailable = false; + } catch (NoClassDefFoundError | Exception ignored) { } this.tabAPI = api; - this.available = isAvailable; - this.tablistFormattingEnabled = tablistEnabled; + this.nameTagManager = nametags; + this.tabListFormatManager = tabList; + this.available = api != null; - if (this.available) { - registerVanillaNameHider(); - hideNametagsForOnlinePlayers(); + if (available) { + registerPlayerLoadHandler(); + Bukkit.getOnlinePlayers().forEach(this::syncPlayer); } } - private void registerVanillaNameHider() { - try { - EventBus eventBus = tabAPI.getEventBus(); - if (eventBus == null) { - return; - } + public void hideNametag(Player player) { + if (player == null) { + return; + } - eventBus.register(PlayerLoadEvent.class, event -> { - hideNametag(event.getPlayer()); + hideNametag(getTabPlayer(player)); + } - // hideNameTag() wipes any prefix/suffix TAB has for this player. - // Re-apply the lobby nametag immediately so it isn't lost on first join. - try { - Object playerObj = event.getPlayer().getPlayer(); - if (!(playerObj instanceof Player player) || !player.isOnline()) return; - dev.nandi0813.practice.manager.profile.Profile profile = - dev.nandi0813.practice.manager.profile.ProfileManager.getInstance().getProfile(player); - if (profile == null) return; - dev.nandi0813.practice.manager.inventory.InventoryUtil.setLobbyNametag(player, profile); - } catch (Exception ignored) { - } - }); + public void hideNametag(TabPlayer tabPlayer) { + if (nameTagManager == null || tabPlayer == null) { + return; + } + + try { + nameTagManager.hideNameTag(tabPlayer); } catch (Exception ignored) { } } - private void hideNametagsForOnlinePlayers() { - for (Player player : Bukkit.getOnlinePlayers()) { - hideNametag(player); + public boolean setTabListName(Player player, Component prefix, Component name, Component suffix) { + if (tabListFormatManager == null) { + return false; + } + + TabPlayer tabPlayer = getTabPlayer(player); + if (tabPlayer == null) { + return false; } - } - /** - * Checks if TAB's tablist-name-formatting feature is enabled using TAB's Developer API. - * Returns true if TabListFormatManager is available and functional. - */ - private boolean checkTablistFormattingEnabled(TabAPI api) { try { - // Use TAB API to check if TabListFormatManager is available - // If getTabListFormatManager() returns null, the feature is disabled - TabListFormatManager manager = api.getTabListFormatManager(); - return manager != null; - } catch (Exception e) { - // If there's an exception, the feature is not available + tabListFormatManager.setPrefix(tabPlayer, toLegacy(prefix)); + tabListFormatManager.setName(tabPlayer, toLegacy(name)); + tabListFormatManager.setSuffix(tabPlayer, toLegacy(suffix)); + return true; + } catch (Exception ignored) { return false; } } - /** - * Sets a player's nametag using ONLY TAB API. - * This method sets the nametag (above head) through TAB's NameTagManager. - * If tablist formatting is enabled in TAB, it also preserves the lobby tablist name. - * - * @param player The player - * @param prefix The prefix component - * @param nameColor The name color to apply to the player's actual name - * @param suffix The suffix component - * @param sortPriority The sort priority (currently unused in TAB integration) - */ - public void setNametag(Player player, Component prefix, NamedTextColor nameColor, Component suffix, int sortPriority) { - if (!available) return; - + private void registerPlayerLoadHandler() { try { - TabPlayer tabPlayer = tabAPI.getPlayer(player.getUniqueId()); - if (tabPlayer == null) return; - - // Get TAB's NameTagManager for nametag (above head) management - NameTagManager nameTagManager = tabAPI.getNameTagManager(); - if (nameTagManager == null) return; - - // Convert Components to legacy strings for TAB API - String prefixStr = componentToLegacy(prefix); - String suffixStr = componentToLegacy(suffix); - - // Apply name color to the prefix - // In TAB, the "team color" (name color) is set via the prefix's last color code - String colorCode = getColorCode(nameColor); - boolean originalPrefixEmpty = prefixStr.isEmpty(); - - // Handle prefix with color code - if (!originalPrefixEmpty) { - // Has actual prefix text - append color code if not already present - boolean endsWithColor = prefixStr.matches(".*§[0-9a-fA-Fk-oK-OrR]$"); - if (!endsWithColor) { - prefixStr = prefixStr + colorCode; - } - nameTagManager.setPrefix(tabPlayer, prefixStr); - } else { - // No prefix text, but we have a color - set color as prefix - nameTagManager.setPrefix(tabPlayer, colorCode); + EventBus eventBus = tabAPI.getEventBus(); + if (eventBus == null) { + return; } - // Set the suffix - nameTagManager.setSuffix(tabPlayer, suffixStr); - - // Preserve the lobby tablist name to prevent match nametag colors from affecting it. - // Only do this when our NAMETAG-MANAGEMENT toggle is enabled. - if (PermanentConfig.NAMETAG_MANAGEMENT_ENABLED) { - if (tablistFormattingEnabled) { - setLobbyTabListName(player); - } else { - preserveTabListNameInternal(player); + eventBus.register(PlayerLoadEvent.class, event -> { + Object playerObj = event.getPlayer().getPlayer(); + if (playerObj instanceof Player player && player.isOnline()) { + syncPlayer(player); } - } - - } catch (Exception e) { - // Silently fail - TAB integration is best-effort - } - } - - public void hideNametag(Player player) { - if (!available || player == null) return; - - try { - TabPlayer tabPlayer = tabAPI.getPlayer(player.getUniqueId()); - if (tabPlayer == null) return; + }); - hideNametag(tabPlayer); + eventBus.register(TabLoadEvent.class, event -> { + refreshManagers(); + // TAB rebuilds its managers on reload, so re-apply our per-player overrides immediately after. + Bukkit.getOnlinePlayers().forEach(this::syncPlayer); + }); } catch (Exception ignored) { } } - public void hideNametag(TabPlayer tabPlayer) { - if (!available || tabPlayer == null) return; - + private void refreshManagers() { try { - NameTagManager nameTagManager = tabAPI.getNameTagManager(); - if (nameTagManager == null) return; - - nameTagManager.hideNameTag(tabPlayer); + nameTagManager = tabAPI.getNameTagManager(); + tabListFormatManager = tabAPI.getTabListFormatManager(); } catch (Exception ignored) { } } - /** - * Sets the player's tablist name to their lobby formatting using ONLY TAB API. - * This method uses TabListFormatManager to set prefix, name, and suffix. - * Only called when tablistFormattingEnabled is true. - * - * @param player The player whose tablist name should be set to lobby formatting - */ - private void setLobbyTabListName(Player player) { - if (!available || !tablistFormattingEnabled || !PermanentConfig.NAMETAG_MANAGEMENT_ENABLED) return; - - try { - TabPlayer tabPlayer = tabAPI.getPlayer(player.getUniqueId()); - if (tabPlayer == null) return; - - TabListFormatManager tabListFormatManager = tabAPI.getTabListFormatManager(); - if (tabListFormatManager == null) return; - - dev.nandi0813.practice.manager.profile.Profile profile = - dev.nandi0813.practice.manager.profile.ProfileManager.getInstance().getProfile(player); - if (profile == null) return; - - InventoryUtil.LobbyNametag lobbyNametag = InventoryUtil.getLobbyNametag(profile, player.getName(), player); + private void syncPlayer(Player player) { + hideNametag(player); - // Convert components to legacy strings for TAB API - String prefixStr = componentToLegacy(lobbyNametag.getPrefix()); - String suffixStr = componentToLegacy(lobbyNametag.getSuffix()); - String nameStr = componentToLegacy(lobbyNametag.getName()); - - // Use TAB's TabListFormatManager to set the tab list formatting - tabListFormatManager.setPrefix(tabPlayer, prefixStr); - tabListFormatManager.setName(tabPlayer, nameStr); - tabListFormatManager.setSuffix(tabPlayer, suffixStr); - - } catch (Exception e) { - // Silently fail - this is best-effort + dev.nandi0813.practice.manager.profile.Profile profile = + dev.nandi0813.practice.manager.profile.ProfileManager.getInstance().getProfile(player); + if (profile != null) { + // This reuses the normal lobby formatting path so TAB and vanilla fallback stay consistent. + InventoryUtil.setLobbyNametag(player, profile); } } - /** - * Sets a player's tablist name using ONLY TAB API. - * This is used for lobby nametag formatting where we want to show the full formatted name in tablist. - * Only works if TAB's tablist-name-formatting feature is enabled. - * - * @param player The player - * @param listName The full formatted component to display in tablist (prefix + colored name + suffix) - */ - public void setTabListName(Player player, Component listName) { - if (!available || !tablistFormattingEnabled || !PermanentConfig.NAMETAG_MANAGEMENT_ENABLED) return; - - try { - TabPlayer tabPlayer = tabAPI.getPlayer(player.getUniqueId()); - if (tabPlayer == null) return; - - TabListFormatManager tabListFormatManager = tabAPI.getTabListFormatManager(); - if (tabListFormatManager == null) return; - - // Convert Component to legacy string for TAB API - String fullListName = componentToLegacy(listName); - String playerName = player.getName(); - - // Parse the formatted name into prefix, name, and suffix - // The listName contains: prefix + playerName + suffix - int nameIndex = fullListName.indexOf(playerName); - - if (nameIndex >= 0) { - // Found the player name in the formatted string - // Extract prefix (everything before the name, may include color codes) - String prefix = fullListName.substring(0, nameIndex); - - // Extract the name portion with its color code - String nameWithColor = playerName; - int lastColorBeforeName = prefix.lastIndexOf('§'); - if (lastColorBeforeName >= 0 && lastColorBeforeName + 1 < prefix.length()) { - // Extract the color code and apply it to the name - String colorCode = prefix.substring(lastColorBeforeName); - nameWithColor = colorCode + playerName; - // Remove the trailing color code from prefix (it's now part of the name) - prefix = prefix.substring(0, lastColorBeforeName); - } - - // Extract suffix (everything after the name) - String suffix = fullListName.substring(nameIndex + playerName.length()); - - // Set the components through TAB API - tabListFormatManager.setPrefix(tabPlayer, prefix); - tabListFormatManager.setName(tabPlayer, nameWithColor); - tabListFormatManager.setSuffix(tabPlayer, suffix); - } else { - // Fallback: couldn't parse, set the whole thing as name - tabListFormatManager.setPrefix(tabPlayer, ""); - tabListFormatManager.setName(tabPlayer, fullListName); - tabListFormatManager.setSuffix(tabPlayer, ""); - } - - } catch (Exception e) { - // Silently fail - TAB integration is best-effort + private TabPlayer getTabPlayer(Player player) { + if (!available || player == null) { + return null; } - } - - /** - * Resets a player's nametag to TAB's default using ONLY TAB API. - * This resets the nametag (above head) prefix and suffix to TAB's configured values. - * NOTE: This does NOT reset tablist formatting - tablist remains as configured by TAB. - * - * @param player The player - */ - public void resetNametag(Player player) { - if (!available) return; try { - TabPlayer tabPlayer = tabAPI.getPlayer(player.getUniqueId()); - if (tabPlayer == null) return; - - // Get TAB's NameTagManager to reset nametag values - NameTagManager nameTagManager = tabAPI.getNameTagManager(); - if (nameTagManager != null) { - // Reset nametag prefix and suffix to default (null values reset to TAB's config) - nameTagManager.setPrefix(tabPlayer, null); - nameTagManager.setSuffix(tabPlayer, null); - } - - // Also reset tablist formatting to TAB's defaults so stale values don't persist - if (tablistFormattingEnabled) { - TabListFormatManager tabListFormatManager = tabAPI.getTabListFormatManager(); - if (tabListFormatManager != null) { - tabListFormatManager.setPrefix(tabPlayer, null); - tabListFormatManager.setName(tabPlayer, null); - tabListFormatManager.setSuffix(tabPlayer, null); - } - } - - } catch (Exception e) { - // Silently fail - TAB integration is best-effort + return tabAPI.getPlayer(player.getUniqueId()); + } catch (Exception ignored) { + return null; } } - /** - * Converts an Adventure Component to a legacy color-coded string. - */ - private String componentToLegacy(Component component) { - if (component == null) return ""; - return LegacyComponentSerializer.legacySection().serialize(component); - } - - /** - * Converts a NamedTextColor to a legacy color code (§x format). - * This is needed for TAB API's setPlayerNameColor method. - * - * @param color The NamedTextColor to convert - * @return Legacy color code string (e.g., "§a" for green) - */ - private String getColorCode(NamedTextColor color) { - if (color == null) return "§7"; // Default to gray - - // Map NamedTextColor to legacy color codes - if (color == NamedTextColor.BLACK) return "§0"; - if (color == NamedTextColor.DARK_BLUE) return "§1"; - if (color == NamedTextColor.DARK_GREEN) return "§2"; - if (color == NamedTextColor.DARK_AQUA) return "§3"; - if (color == NamedTextColor.DARK_RED) return "§4"; - if (color == NamedTextColor.DARK_PURPLE) return "§5"; - if (color == NamedTextColor.GOLD) return "§6"; - if (color == NamedTextColor.GRAY) return "§7"; - if (color == NamedTextColor.DARK_GRAY) return "§8"; - if (color == NamedTextColor.BLUE) return "§9"; - if (color == NamedTextColor.GREEN) return "§a"; - if (color == NamedTextColor.AQUA) return "§b"; - if (color == NamedTextColor.RED) return "§c"; - if (color == NamedTextColor.LIGHT_PURPLE) return "§d"; - if (color == NamedTextColor.YELLOW) return "§e"; - if (color == NamedTextColor.WHITE) return "§f"; - - return "§7"; // Default to gray if unknown - } - - /** - * Applies division placeholders to a component. - * - * @param component The component to process - * @param division The division to apply - * @return Component with division placeholders replaced - */ - private Component applyDivisionPlaceholder(Component component, dev.nandi0813.practice.manager.division.Division division) { - if (component == null || division == null) return component; - - return component - .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder() - .match("%division%") - .replacement(division.getComponentFullName()) - .build()) - .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder() - .match("%division_short%") - .replacement(division.getComponentShortName()) - .build()); - } - - /** - * Removes division placeholders from a component. - * - * @param component The component to process - * @return Component with division placeholders removed - */ - private Component removeDivisionPlaceholder(Component component) { - if (component == null) return component; - - return component - .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder() - .match("%division%") - .replacement(Component.empty()) - .build()) - .replaceText(net.kyori.adventure.text.TextReplacementConfig.builder() - .match("%division_short%") - .replacement(Component.empty()) - .build()); - } - - /** - * Preserves the player's tablist name using internal Bukkit method. - * This is called when TAB's tablist-name-formatting is disabled. - * Prevents match nametag colors from bleeding into the tablist in 1.21+. - * - * @param player The player whose tablist name should be preserved - */ - private void preserveTabListNameInternal(Player player) { - try { - dev.nandi0813.practice.manager.profile.Profile profile = - dev.nandi0813.practice.manager.profile.ProfileManager.getInstance().getProfile(player); - if (profile == null) return; - - InventoryUtil.LobbyNametag lobbyNametag = InventoryUtil.getLobbyNametag(profile, player.getName(), player); - Component tabListName = lobbyNametag.getPrefix() - .append(lobbyNametag.getName()) - .append(lobbyNametag.getSuffix()); - - // Use internal Bukkit method to set the tablist name (not TAB API) - PlayerUtil.setPlayerListName(player, tabListName); - - } catch (Exception e) { - // Silently fail - this is best-effort - } + private String toLegacy(Component component) { + return component == null ? "" : LEGACY.serialize(component); } - - } +} diff --git a/core/src/main/java/dev/nandi0813/practice/manager/nametag/TeamPacketBlocker.java b/core/src/main/java/dev/nandi0813/practice/manager/nametag/TeamPacketBlocker.java index 59b8f9e7..13b2d9d0 100644 --- a/core/src/main/java/dev/nandi0813/practice/manager/nametag/TeamPacketBlocker.java +++ b/core/src/main/java/dev/nandi0813/practice/manager/nametag/TeamPacketBlocker.java @@ -2,23 +2,13 @@ import lombok.Getter; import org.bukkit.Bukkit; -import org.bukkit.plugin.Plugin; public class TeamPacketBlocker { private static TeamPacketBlocker instance; @Getter - private boolean tabPluginPresent = false; - - @Getter - private boolean tabScoreboardTeamsEnabled = false; - - @Getter - private boolean nametagSystemDisabled = false; - - @Getter - private TabIntegration tabIntegration = null; + private TabIntegration tabIntegration; private TeamPacketBlocker() { } @@ -31,41 +21,16 @@ public static TeamPacketBlocker getInstance() { } public void register() { - Plugin tabPlugin = Bukkit.getPluginManager().getPlugin("TAB"); - tabPluginPresent = tabPlugin != null && tabPlugin.isEnabled(); - - if (!tabPluginPresent) { - return; - } - - tabScoreboardTeamsEnabled = false; - nametagSystemDisabled = false; - - try { - tabIntegration = new TabIntegration(); - } catch (Throwable ignored) { - tabIntegration = null; + if (Bukkit.getPluginManager().isPluginEnabled("TAB")) { + try { + tabIntegration = new TabIntegration(); + } catch (Throwable ignored) { + tabIntegration = null; + } } } public void unregister() { tabIntegration = null; - tabPluginPresent = false; - tabScoreboardTeamsEnabled = false; - nametagSystemDisabled = false; - } - - @SuppressWarnings ( "unused" ) - public void registerOurTeam(String teamName) { - } - - @SuppressWarnings ( "unused" ) - public void unregisterOurTeam(String teamName) { - } - - @SuppressWarnings ( "unused" ) - public boolean isOurTeam(String teamName) { - return false; } - }