From ff810608b32f0f1973964327e827ee93e09d23cc Mon Sep 17 00:00:00 2001 From: GeminiAntigravityCLI Date: Thu, 20 Aug 2026 18:35:23 +0200 Subject: [PATCH] feat(worldguard): integrate WorldGuard 7 cuboid claims with automatic member synchronization --- src/main/java/fr/luc/gamingcore/Main.java | 25 +- .../luc/gamingcore/manager/GuildManager.java | 78 +++++- .../worldguard/WorldGuardManager.java | 241 ++++++++++++++++++ .../luc/gamingcore/model/GuildModelTest.java | 12 + 4 files changed, 342 insertions(+), 14 deletions(-) create mode 100644 src/main/java/fr/luc/gamingcore/worldguard/WorldGuardManager.java diff --git a/src/main/java/fr/luc/gamingcore/Main.java b/src/main/java/fr/luc/gamingcore/Main.java index 2cb55ac..b88a6f2 100644 --- a/src/main/java/fr/luc/gamingcore/Main.java +++ b/src/main/java/fr/luc/gamingcore/Main.java @@ -31,6 +31,7 @@ public final class Main extends JavaPlugin { private DatabaseManager databaseManager; private GuildRepository guildRepository; private EconomyManager economyManager; + private fr.luc.gamingcore.worldguard.WorldGuardManager worldGuardManager; private GuildManager guildManager; private BetterMcCommands betterMcCommands; @@ -50,24 +51,27 @@ public final class Main extends JavaPlugin { setupWorldGuard(); setupEconomy(); - // 3. Initialiser la couche de données SQLite + // 3. Initialiser le gestionnaire WorldGuard + this.worldGuardManager = new fr.luc.gamingcore.worldguard.WorldGuardManager(this); + + // 4. Initialiser la couche de données SQLite this.databaseManager = new DatabaseManager(this); this.databaseManager.initialize(); this.guildRepository = new GuildRepository(this, databaseManager); - // 4. Initialiser la couche économique + // 5. Initialiser la couche économique this.economyManager = new EconomyManager(this); - // 5. Initialiser le service métier GuildManager et charger les données - this.guildManager = new GuildManager(this, guildRepository, economyManager); + // 6. Initialiser le service métier GuildManager et charger les données + this.guildManager = new GuildManager(this, guildRepository, economyManager, worldGuardManager); loadConfigSettings(); this.guildManager.loadData(); - // 6. Initialiser le framework betterMcCommands et enregistrer les commandes + // 7. Initialiser le framework betterMcCommands et enregistrer les commandes this.betterMcCommands = BetterMcCommands.create(this); new GuildCommands(this, guildManager).register(); - // 7. Enregistrer les écouteurs d'événements + // 8. Enregistrer les écouteurs d'événements PluginManager pm = getServer().getPluginManager(); pm.registerEvents(new TerritoryProtectionListener(this), this); @@ -216,6 +220,15 @@ public final class Main extends JavaPlugin { return databaseManager; } + /** + * Obtient le gestionnaire WorldGuard. + * + * @return L'instance WorldGuardManager. + */ + public fr.luc.gamingcore.worldguard.WorldGuardManager getWorldGuardManager() { + return worldGuardManager; + } + /** * Obtient le référentiel de persistance des guildes. * diff --git a/src/main/java/fr/luc/gamingcore/manager/GuildManager.java b/src/main/java/fr/luc/gamingcore/manager/GuildManager.java index 9070238..edebe63 100644 --- a/src/main/java/fr/luc/gamingcore/manager/GuildManager.java +++ b/src/main/java/fr/luc/gamingcore/manager/GuildManager.java @@ -24,6 +24,7 @@ public class GuildManager { private final Main plugin; private final GuildRepository repository; private final EconomyManager economyManager; + private final fr.luc.gamingcore.worldguard.WorldGuardManager worldGuardManager; private final ChunkPriceCalculator priceCalculator; private final InviteManager inviteManager; @@ -34,22 +35,35 @@ public class GuildManager { private final Map chunkGuildMap = new ConcurrentHashMap<>(); /** - * Constructeur du GuildManager. + * Constructeur complet du GuildManager avec WorldGuardManager. + * + * @param plugin Instance du plugin principal. + * @param repository Référentiel de persistance des données. + * @param economyManager Gestionnaire de l'économie Vault. + * @param worldGuardManager Gestionnaire des régions WorldGuard. + */ + public GuildManager(Main plugin, GuildRepository repository, EconomyManager economyManager, fr.luc.gamingcore.worldguard.WorldGuardManager worldGuardManager) { + this.plugin = plugin; + this.repository = repository; + this.economyManager = economyManager; + this.worldGuardManager = worldGuardManager; + this.priceCalculator = new ChunkPriceCalculator(); + this.inviteManager = new InviteManager(); + } + + /** + * Constructeur de compatibilité du GuildManager. * * @param plugin Instance du plugin principal. * @param repository Référentiel de persistance des données. * @param economyManager Gestionnaire de l'économie Vault. */ public GuildManager(Main plugin, GuildRepository repository, EconomyManager economyManager) { - this.plugin = plugin; - this.repository = repository; - this.economyManager = economyManager; - this.priceCalculator = new ChunkPriceCalculator(); - this.inviteManager = new InviteManager(); + this(plugin, repository, economyManager, null); } /** - * Charge l'ensemble des données de la base en mémoire et reconstruit les index de recherche. + * Charge l'ensemble des données de la base en mémoire et reconstruit les index de recherche et régions WorldGuard. */ public void loadData() { guildsById.clear(); @@ -62,6 +76,10 @@ public class GuildManager { indexGuild(guild); } + if (worldGuardManager != null) { + worldGuardManager.syncAllGuilds(guildsById.values()); + } + plugin.getLogger().info("Charges " + guildsById.size() + " guildes (" + playerGuildMap.size() + " membres, " + chunkGuildMap.size() + " claims)."); } @@ -183,6 +201,11 @@ public class GuildManager { repository.saveMemberAsync(founder); repository.saveClaimAsync(initialClaim); + // 11. Créer la région WorldGuard + if (worldGuardManager != null) { + worldGuardManager.createOrUpdateRegion(guild, loc.getWorld(), chunkX, chunkZ); + } + return GuildResult.success("La guilde " + name + " a été créée avec succès !"); } @@ -210,6 +233,11 @@ public class GuildManager { return GuildResult.failure("Seul le Chef de la guilde peut dissoudre la guilde."); } + // Supprimer toutes les régions WorldGuard associées + if (worldGuardManager != null) { + worldGuardManager.removeGuildRegions(guild); + } + // Désindexer la guilde unindexGuild(guild); @@ -249,6 +277,11 @@ public class GuildManager { playerGuildMap.put(player.getUniqueId(), guild.getId()); repository.saveMemberAsync(member); + // Synchroniser WorldGuard + if (worldGuardManager != null) { + worldGuardManager.syncGuildMembers(guild); + } + return GuildResult.success("Le joueur " + player.getName() + " a rejoint la guilde !"); } @@ -288,6 +321,11 @@ public class GuildManager { playerGuildMap.remove(targetUuid); repository.deleteMemberAsync(targetUuid); + // Synchroniser WorldGuard + if (worldGuardManager != null) { + worldGuardManager.syncGuildMembers(guild); + } + return GuildResult.success("Le membre a été expulsé de la guilde."); } @@ -317,6 +355,11 @@ public class GuildManager { playerGuildMap.remove(player.getUniqueId()); repository.deleteMemberAsync(player.getUniqueId()); + // Synchroniser WorldGuard + if (worldGuardManager != null) { + worldGuardManager.syncGuildMembers(guild); + } + return GuildResult.success("Vous avez quitté la guilde."); } @@ -370,6 +413,11 @@ public class GuildManager { targetMember.setRankId(newRank.getId()); repository.saveMemberAsync(targetMember); + // Synchroniser WorldGuard (au cas où le chef a changé) + if (worldGuardManager != null) { + worldGuardManager.syncGuildMembers(guild); + } + return GuildResult.success("Le rang du membre a été mis à jour vers " + newRank.getName() + "."); } @@ -480,6 +528,11 @@ public class GuildManager { repository.saveGuildAsync(guild); repository.saveClaimAsync(claim); + // 7. Créer la région WorldGuard + if (worldGuardManager != null) { + worldGuardManager.createOrUpdateRegion(guild, world, chunkX, chunkZ); + } + return GuildResult.success("Chunk revendiqué avec succès pour " + economyManager.format(price) + " ! (Total: " + guild.getClaimCount() + " chunks)"); } @@ -515,7 +568,12 @@ public class GuildManager { return GuildResult.failure("Vous ne pouvez pas abandonner le dernier chunk de votre guilde."); } - // 4. Supprimer le claim + // 4. Supprimer la région WorldGuard + if (worldGuardManager != null) { + worldGuardManager.removeRegion(guild, world, chunkX, chunkZ); + } + + // 5. Supprimer le claim guild.removeClaim(worldName, chunkX, chunkZ); chunkGuildMap.remove(getChunkKey(worldName, chunkX, chunkZ)); repository.deleteClaimAsync(worldName, chunkX, chunkZ); @@ -646,6 +704,10 @@ public class GuildManager { return inviteManager; } + public fr.luc.gamingcore.worldguard.WorldGuardManager getWorldGuardManager() { + return worldGuardManager; + } + // ========================================== // MÉTHODES INTERNES D'INDEXATION // ========================================== diff --git a/src/main/java/fr/luc/gamingcore/worldguard/WorldGuardManager.java b/src/main/java/fr/luc/gamingcore/worldguard/WorldGuardManager.java new file mode 100644 index 0000000..cb18819 --- /dev/null +++ b/src/main/java/fr/luc/gamingcore/worldguard/WorldGuardManager.java @@ -0,0 +1,241 @@ +package fr.luc.gamingcore.worldguard; + +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldguard.WorldGuard; +import com.sk89q.worldguard.protection.flags.Flags; +import com.sk89q.worldguard.protection.flags.StateFlag; +import com.sk89q.worldguard.protection.managers.RegionManager; +import com.sk89q.worldguard.protection.regions.ProtectedCuboidRegion; +import com.sk89q.worldguard.protection.regions.ProtectedRegion; +import com.sk89q.worldguard.protection.regions.RegionContainer; +import fr.luc.gamingcore.Main; +import fr.luc.gamingcore.model.Guild; +import fr.luc.gamingcore.model.GuildClaim; +import fr.luc.gamingcore.model.GuildMember; +import org.bukkit.Bukkit; +import org.bukkit.World; + +import java.util.Collection; +import java.util.Objects; +import java.util.logging.Level; + +/** + * Service d'intégration avec WorldGuard 7 pour la création, la protection et la synchronisation des régions de claim. + */ +public class WorldGuardManager { + + private final Main plugin; + private final RegionContainer regionContainer; + private final boolean enabled; + + /** + * Constructeur du WorldGuardManager. + * + * @param plugin L'instance principale de GamingCore. + */ + public WorldGuardManager(Main plugin) { + this.plugin = Objects.requireNonNull(plugin, "plugin cannot be null"); + RegionContainer container = null; + boolean isEnabled = false; + + try { + if (Bukkit.getPluginManager().getPlugin("WorldGuard") != null) { + container = WorldGuard.getInstance().getPlatform().getRegionContainer(); + isEnabled = (container != null); + } + } catch (Throwable t) { + plugin.getLogger().log(Level.WARNING, "Erreur lors de l'initialisation de WorldGuardManager : " + t.getMessage()); + } + + this.regionContainer = container; + this.enabled = isEnabled; + } + + /** + * Indique si l'intégration WorldGuard est active. + * + * @return true si WorldGuard est chargé et fonctionnel. + */ + public boolean isEnabled() { + return enabled && regionContainer != null; + } + + /** + * Génère l'identifiant unique WorldGuard d'une région de claim pour un chunk donné. + * + * @param guild La guilde propriétaire. + * @param chunkX Coordonnée X du chunk. + * @param chunkZ Coordonnée Z du chunk. + * @return L'identifiant normalisé de la région (ex: "guild_a1b2c3d4_12_-4"). + */ + public String getRegionId(Guild guild, int chunkX, int chunkZ) { + String cleanId = guild.getId().toString().replace("-", "").substring(0, 12); + return "guild_" + cleanId + "_" + chunkX + "_" + chunkZ; + } + + /** + * Obtient le gestionnaire de régions WorldGuard pour un monde Bukkit donné. + * + * @param world Le monde Bukkit. + * @return L'instance RegionManager ou null si indisponible. + */ + public RegionManager getRegionManager(World world) { + if (!isEnabled() || world == null) return null; + return regionContainer.get(BukkitAdapter.adapt(world)); + } + + /** + * Crée ou met à jour la région WorldGuard associée à un claim territorial de guilde. + * Les membres de la guilde sont enregistrés en tant que membres de la région (permettant la pose/casse de blocs et l'ouverture de coffres). + * + * @param guild La guilde propriétaire. + * @param world Le monde Bukkit dans lequel se situe le claim. + * @param chunkX Coordonnée X du chunk. + * @param chunkZ Coordonnée Z du chunk. + * @return La région protégée créée ou mise à jour, ou null en cas d'échec. + */ + public ProtectedRegion createOrUpdateRegion(Guild guild, World world, int chunkX, int chunkZ) { + if (!isEnabled() || guild == null || world == null) return null; + + RegionManager regionManager = getRegionManager(world); + if (regionManager == null) return null; + + String regionId = getRegionId(guild, chunkX, chunkZ); + ProtectedRegion region = regionManager.getRegion(regionId); + + int minX = chunkX * 16; + int minZ = chunkZ * 16; + int maxX = minX + 15; + int maxZ = minZ + 15; + int minY = world.getMinHeight(); + int maxY = world.getMaxHeight() - 1; + + BlockVector3 min = BlockVector3.at(minX, minY, minZ); + BlockVector3 max = BlockVector3.at(maxX, maxY, maxZ); + + if (region == null) { + region = new ProtectedCuboidRegion(regionId, min, max); + region.setPriority(plugin.getConfig().getInt("worldguard.region_priority", 10)); + + // Flags WorldGuard + region.setFlag(Flags.PASSTHROUGH, StateFlag.State.DENY); + region.setFlag(Flags.TNT, StateFlag.State.DENY); + region.setFlag(Flags.CREEPER_EXPLOSION, StateFlag.State.DENY); + region.setFlag(Flags.PVP, StateFlag.State.ALLOW); + + regionManager.addRegion(region); + } + + // Synchroniser les membres et propriétaires + syncRegionMembers(guild, region); + + return region; + } + + /** + * Supprime la région WorldGuard correspondant à un chunk précis. + * + * @param guild La guilde propriétaire. + * @param world Le monde du chunk. + * @param chunkX Coordonnée X. + * @param chunkZ Coordonnée Z. + * @return true si la région a été supprimée. + */ + public boolean removeRegion(Guild guild, World world, int chunkX, int chunkZ) { + if (!isEnabled() || guild == null || world == null) return false; + + RegionManager regionManager = getRegionManager(world); + if (regionManager == null) return false; + + String regionId = getRegionId(guild, chunkX, chunkZ); + return regionManager.removeRegion(regionId) != null; + } + + /** + * Supprime toutes les régions WorldGuard appartenant à une guilde dans tous les mondes (utilisé lors du disband). + * + * @param guild La guilde dissoute. + */ + public void removeGuildRegions(Guild guild) { + if (!isEnabled() || guild == null) return; + + for (GuildClaim claim : guild.getClaims()) { + World world = Bukkit.getWorld(claim.getWorldName()); + if (world != null) { + removeRegion(guild, world, claim.getChunkX(), claim.getChunkZ()); + } + } + } + + /** + * Synchronise la liste des membres et propriétaires sur toutes les régions WorldGuard d'une guilde. + * Doit être appelée à chaque ajout, retrait ou modification de membre. + * + * @param guild La guilde à synchroniser. + */ + public void syncGuildMembers(Guild guild) { + if (!isEnabled() || guild == null) return; + + for (GuildClaim claim : guild.getClaims()) { + World world = Bukkit.getWorld(claim.getWorldName()); + if (world == null) continue; + + RegionManager regionManager = getRegionManager(world); + if (regionManager == null) continue; + + String regionId = getRegionId(guild, claim.getChunkX(), claim.getChunkZ()); + ProtectedRegion region = regionManager.getRegion(regionId); + + if (region == null) { + // Si la région n'existe pas encore, la créer + createOrUpdateRegion(guild, world, claim.getChunkX(), claim.getChunkZ()); + } else { + syncRegionMembers(guild, region); + } + } + } + + /** + * Synchronise tous les claims et membres de l'intégralité des guildes dans WorldGuard au démarrage du serveur. + * + * @param guilds Collection des guildes chargées en mémoire. + */ + public void syncAllGuilds(Collection guilds) { + if (!isEnabled() || guilds == null) return; + + int totalRegions = 0; + for (Guild guild : guilds) { + for (GuildClaim claim : guild.getClaims()) { + World world = Bukkit.getWorld(claim.getWorldName()); + if (world != null) { + createOrUpdateRegion(guild, world, claim.getChunkX(), claim.getChunkZ()); + totalRegions++; + } + } + } + + plugin.getLogger().info("WorldGuard: Synchronisation de " + totalRegions + " regions territoriales de guilde terminee avec succes !"); + } + + /** + * Met à jour les domaines de membres et d'owners d'une région WorldGuard à partir de la guilde. + * + * @param guild La guilde source. + * @param region La région WorldGuard cible. + */ + private void syncRegionMembers(Guild guild, ProtectedRegion region) { + // 1. Mise à jour des propriétaires (Chef de guilde) + region.getOwners().clear(); + GuildMember leader = guild.getLeaderMember(); + if (leader != null) { + region.getOwners().addPlayer(leader.getPlayerUuid()); + } + + // 2. Mise à jour des membres (Tous les membres de la guilde) + region.getMembers().clear(); + for (GuildMember member : guild.getMembers()) { + region.getMembers().addPlayer(member.getPlayerUuid()); + } + } +} diff --git a/src/test/java/fr/luc/gamingcore/model/GuildModelTest.java b/src/test/java/fr/luc/gamingcore/model/GuildModelTest.java index 935ab19..07c981c 100644 --- a/src/test/java/fr/luc/gamingcore/model/GuildModelTest.java +++ b/src/test/java/fr/luc/gamingcore/model/GuildModelTest.java @@ -57,4 +57,16 @@ public class GuildModelTest { assertFalse(guild.hasPermission(memberUuid, GuildPermission.CLAIM)); assertFalse(guild.hasPermission(memberUuid, GuildPermission.BANK_WITHDRAW)); } + + @Test + public void testGuildColorAndTagFormatting() { + assertEquals("#00C9FF", guild.getColor()); + assertEquals("[VAL]", guild.getFormattedTag()); + assertEquals("Valhalla", guild.getFormattedName()); + + guild.setColor("#FF5555"); + guild.setTag("VALH"); + assertEquals("#FF5555", guild.getColor()); + assertEquals("[VALH]", guild.getFormattedTag()); + } }