From 56231af30dd10c28b09f4a3ced2daf8bf0e52890 Mon Sep 17 00:00:00 2001 From: GeminiAntigravityCLI Date: Thu, 20 Aug 2026 21:28:45 +0200 Subject: [PATCH] feat: Custom guild chat format and NametagManager for above-head & tablist guild tags --- src/main/java/fr/luc/gamingcore/Main.java | 13 ++ .../gamingcore/listener/NametagListener.java | 55 +++++++ .../luc/gamingcore/manager/GuildManager.java | 48 ++++++ .../gamingcore/manager/NametagManager.java | 138 ++++++++++++++++++ src/main/resources/messages.yml | 2 +- 5 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 src/main/java/fr/luc/gamingcore/listener/NametagListener.java create mode 100644 src/main/java/fr/luc/gamingcore/manager/NametagManager.java diff --git a/src/main/java/fr/luc/gamingcore/Main.java b/src/main/java/fr/luc/gamingcore/Main.java index b028e69..f6f0b01 100644 --- a/src/main/java/fr/luc/gamingcore/Main.java +++ b/src/main/java/fr/luc/gamingcore/Main.java @@ -33,6 +33,7 @@ public final class Main extends JavaPlugin { private EconomyManager economyManager; private fr.luc.gamingcore.worldguard.WorldGuardManager worldGuardManager; private GuildManager guildManager; + private fr.luc.gamingcore.manager.NametagManager nametagManager; private BetterMcCommands betterMcCommands; /** @@ -64,6 +65,8 @@ public final class Main extends JavaPlugin { // 6. Initialiser le service métier GuildManager et charger les données this.guildManager = new GuildManager(this, guildRepository, economyManager, worldGuardManager); + this.nametagManager = new fr.luc.gamingcore.manager.NametagManager(this, guildManager); + this.guildManager.setNametagManager(nametagManager); loadConfigSettings(); this.guildManager.loadData(); @@ -75,6 +78,7 @@ public final class Main extends JavaPlugin { PluginManager pm = getServer().getPluginManager(); pm.registerEvents(new TerritoryProtectionListener(this), this); pm.registerEvents(new fr.luc.gamingcore.listener.GuildChatListener(this), this); + pm.registerEvents(new fr.luc.gamingcore.listener.NametagListener(this), this); getLogger().info("GamingCore (Système de Guilde - Cœur Métier & Commandes) a ete active avec succes !"); } @@ -265,4 +269,13 @@ public final class Main extends JavaPlugin { public BetterMcCommands getBetterMcCommands() { return betterMcCommands; } + + /** + * Obtient le gestionnaire de Nametags et TabList. + * + * @return L'instance NametagManager. + */ + public fr.luc.gamingcore.manager.NametagManager getNametagManager() { + return nametagManager; + } } diff --git a/src/main/java/fr/luc/gamingcore/listener/NametagListener.java b/src/main/java/fr/luc/gamingcore/listener/NametagListener.java new file mode 100644 index 0000000..69fbe68 --- /dev/null +++ b/src/main/java/fr/luc/gamingcore/listener/NametagListener.java @@ -0,0 +1,55 @@ +package fr.luc.gamingcore.listener; + +import fr.luc.gamingcore.Main; +import fr.luc.gamingcore.manager.NametagManager; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerJoinEvent; +import org.bukkit.event.player.PlayerQuitEvent; + +/** + * Écouteur gérant l'assignation automatique des Nametags et noms TabList à la connexion/déconnexion des joueurs. + */ +public class NametagListener implements Listener { + + private final Main plugin; + private final NametagManager nametagManager; + + /** + * Constructeur de l'écouteur de Nametags. + * + * @param plugin Instance principale du plugin. + */ + public NametagListener(Main plugin) { + this.plugin = plugin; + this.nametagManager = plugin.getNametagManager(); + } + + /** + * Met à jour le tag au-dessus de la tête et dans la TabList lors de la connexion. + * + * @param event L'événement PlayerJoinEvent. + */ + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerJoin(PlayerJoinEvent event) { + Player player = event.getPlayer(); + // Léger délai d'un tick pour laisser le client charger le scoreboard + plugin.getServer().getScheduler().runTask(plugin, () -> { + if (player.isOnline()) { + nametagManager.updatePlayer(player); + } + }); + } + + /** + * Nettoie les entrées de scoreboard à la déconnexion. + * + * @param event L'événement PlayerQuitEvent. + */ + @EventHandler(priority = EventPriority.MONITOR) + public void onPlayerQuit(PlayerQuitEvent event) { + nametagManager.removePlayer(event.getPlayer()); + } +} diff --git a/src/main/java/fr/luc/gamingcore/manager/GuildManager.java b/src/main/java/fr/luc/gamingcore/manager/GuildManager.java index 1fd46e3..c8fe09d 100644 --- a/src/main/java/fr/luc/gamingcore/manager/GuildManager.java +++ b/src/main/java/fr/luc/gamingcore/manager/GuildManager.java @@ -30,6 +30,7 @@ public class GuildManager { private final ChunkPriceCalculator priceCalculator; private final InviteManager inviteManager; private final MessageManager messageManager; + private NametagManager nametagManager; // Indexations en mémoire pour des lookups O(1) private final Map guildsById = new ConcurrentHashMap<>(); @@ -55,6 +56,10 @@ public class GuildManager { this.messageManager = plugin.getMessageManager(); } + public void setNametagManager(NametagManager nametagManager) { + this.nametagManager = nametagManager; + } + /** * Charge toutes les données depuis la base de données SQLite en mémoire. */ @@ -80,6 +85,10 @@ public class GuildManager { if (worldGuardManager != null && worldGuardManager.isEnabled()) { worldGuardManager.syncAllGuilds(new ArrayList<>(loadedGuilds)); } + + if (nametagManager != null) { + nametagManager.updateAll(); + } } /** @@ -214,6 +223,10 @@ public class GuildManager { worldGuardManager.createOrUpdateRegion(guild, loc.getWorld(), chunkX, chunkZ); } + if (nametagManager != null) { + nametagManager.updatePlayer(founder); + } + logAction(guild, founder, "CREATION", "Création de la guilde avec le chunk initial (" + chunkX + ", " + chunkZ + ")"); return GuildResult.success("La guilde " + guild.getFormattedName() + " a été créée avec succès !"); @@ -244,6 +257,18 @@ public class GuildManager { worldGuardManager.removeGuildRegions(guild); } + if (nametagManager != null) { + for (GuildMember m : guild.getMembers()) { + if (m.getOfflinePlayer() != null && m.getOfflinePlayer().isOnline()) { + Player p = m.getOfflinePlayer().getPlayer(); + if (p != null) { + nametagManager.updatePlayer(p); + } + } + } + nametagManager.removeGuildTeam(guild.getId()); + } + unindexGuild(guild); repository.deleteGuildAsync(guild.getId()); @@ -280,6 +305,10 @@ public class GuildManager { worldGuardManager.syncGuildMembers(guild); } + if (nametagManager != null) { + nametagManager.updatePlayer(player); + } + logAction(guild, player, "JOIN", player.getName() + " a rejoint la guilde (" + defaultRank.getName() + ")"); broadcastToGuild(guild, "guild.broadcast.member_joined", "{player}", player.getName()); @@ -325,6 +354,13 @@ public class GuildManager { worldGuardManager.syncGuildMembers(guild); } + if (nametagManager != null) { + Player targetPlayer = org.bukkit.Bukkit.getPlayer(targetUuid); + if (targetPlayer != null) { + nametagManager.updatePlayer(targetPlayer); + } + } + logAction(guild, actor, "KICK", targetName + " a été expulsé par " + actor.getName()); broadcastToGuild(guild, "guild.broadcast.member_kicked", "{player}", targetName, "{actor}", actor.getName()); @@ -357,6 +393,10 @@ public class GuildManager { worldGuardManager.syncGuildMembers(guild); } + if (nametagManager != null) { + nametagManager.updatePlayer(player); + } + logAction(guild, player, "LEAVE", player.getName() + " a quitté la guilde."); broadcastToGuild(guild, "guild.broadcast.member_left", "{player}", player.getName()); @@ -557,6 +597,10 @@ public class GuildManager { repository.saveGuildAsync(guild); logAction(guild, actor, "COLOR", "Couleur modifiée vers " + hexColor); + if (nametagManager != null) { + nametagManager.updateGuild(guild); + } + return GuildResult.success("La couleur de la guilde a été changée pour " + hexColor + " !"); } @@ -577,6 +621,10 @@ public class GuildManager { repository.saveGuildAsync(guild); logAction(guild, actor, "TAG", "Tag modifié vers [" + newTag.toUpperCase() + "]"); + if (nametagManager != null) { + nametagManager.updateGuild(guild); + } + return GuildResult.success("Le tag de la guilde a été changé pour " + guild.getFormattedTag() + " !"); } diff --git a/src/main/java/fr/luc/gamingcore/manager/NametagManager.java b/src/main/java/fr/luc/gamingcore/manager/NametagManager.java new file mode 100644 index 0000000..e1ff01f --- /dev/null +++ b/src/main/java/fr/luc/gamingcore/manager/NametagManager.java @@ -0,0 +1,138 @@ +package fr.luc.gamingcore.manager; + +import fr.luc.gamingcore.Main; +import fr.luc.gamingcore.model.Guild; +import fr.luc.gamingcore.model.GuildMember; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Team; + +import java.util.UUID; + +/** + * Gestionnaire responsable de l'affichage des tags et couleurs de guilde + * au-dessus de la tête des joueurs (Nametag / Scoreboard Team) et dans la liste des joueurs (TabList). + */ +public class NametagManager { + + private final Main plugin; + private final GuildManager guildManager; + private final Scoreboard scoreboard; + + /** + * Constructeur du gestionnaire de Nametags. + * + * @param plugin Instance principale du plugin. + * @param guildManager Gestionnaire métier des guildes. + */ + public NametagManager(Main plugin, GuildManager guildManager) { + this.plugin = plugin; + this.guildManager = guildManager; + this.scoreboard = Bukkit.getScoreboardManager().getMainScoreboard(); + } + + /** + * Met à jour le nametag et le nom dans le TabList pour un joueur spécifique. + * + * @param player Le joueur à mettre à jour. + */ + public void updatePlayer(Player player) { + if (player == null || !player.isOnline()) return; + + Guild guild = guildManager.getGuildByPlayer(player.getUniqueId()); + + if (guild != null) { + String teamName = getTeamName(guild.getId()); + Team team = scoreboard.getTeam(teamName); + if (team == null) { + team = scoreboard.registerNewTeam(teamName); + } + + String tagText = "[" + guild.getTag() + "] "; + Component prefixComponent = MiniMessage.miniMessage().deserialize(tagText); + team.prefix(prefixComponent); + + if (!team.hasEntry(player.getName())) { + // Retirer d'une ancienne équipe de guilde si nécessaire + Team oldTeam = scoreboard.getEntryTeam(player.getName()); + if (oldTeam != null && oldTeam.getName().startsWith("g_") && !oldTeam.getName().equals(teamName)) { + oldTeam.removeEntry(player.getName()); + } + team.addEntry(player.getName()); + } + + // TabList Name + Component tabName = MiniMessage.miniMessage().deserialize(tagText + "" + player.getName() + ""); + player.playerListName(tabName); + } else { + // Joueur sans guilde + Team currentTeam = scoreboard.getEntryTeam(player.getName()); + if (currentTeam != null && currentTeam.getName().startsWith("g_")) { + currentTeam.removeEntry(player.getName()); + } + player.playerListName(Component.text(player.getName())); + } + } + + /** + * Met à jour l'affichage de tous les membres d'une guilde donnée. + * + * @param guild La guilde dont les membres doivent être actualisés. + */ + public void updateGuild(Guild guild) { + if (guild == null) return; + for (GuildMember member : guild.getMembers()) { + if (member.getOfflinePlayer() != null && member.getOfflinePlayer().isOnline()) { + Player p = member.getOfflinePlayer().getPlayer(); + if (p != null) { + updatePlayer(p); + } + } + } + } + + /** + * Supprime l'équipe Scoreboard associée à une guilde lors de sa dissolution. + * + * @param guildId L'UUID de la guilde dissoute. + */ + public void removeGuildTeam(UUID guildId) { + if (guildId == null) return; + String teamName = getTeamName(guildId); + Team team = scoreboard.getTeam(teamName); + if (team != null) { + team.unregister(); + } + } + + /** + * Met à jour tous les joueurs actuellement connectés sur le serveur. + */ + public void updateAll() { + for (Player online : Bukkit.getOnlinePlayers()) { + updatePlayer(online); + } + } + + /** + * Nettoie les équipes lors de la déconnexion d'un joueur. + * + * @param player Le joueur déconnecté. + */ + public void removePlayer(Player player) { + if (player == null) return; + Team team = scoreboard.getEntryTeam(player.getName()); + if (team != null && team.getName().startsWith("g_")) { + team.removeEntry(player.getName()); + } + } + + private String getTeamName(UUID guildId) { + // Le nom d'équipe est préfixé par "g_" suivi des 12 premiers caractères de l'UUID pour rester sous les 16 car. + String raw = guildId.toString().replace("-", ""); + return "g_" + (raw.length() > 12 ? raw.substring(0, 12) : raw); + } +} diff --git a/src/main/resources/messages.yml b/src/main/resources/messages.yml index 2966a79..b706335 100644 --- a/src/main/resources/messages.yml +++ b/src/main/resources/messages.yml @@ -93,7 +93,7 @@ guild: pagination: "Page précédente'>[◀ Précédent] --- Page suivante'>[Suivant ▶]" chat: - format: "[Chat Guilde] {rank} {player} » {message}" + format: "[Guild Chat] {guild} - {rank} - {player} => {message}" broadcast: member_joined: "{player} a rejoint la guilde !"