feat(messages): add messages.yml, MessageManager, validation rules and double-confirmation for guild creation & disband

This commit is contained in:
2026-08-20 18:03:17 +02:00
parent 1f0d469dd3
commit 62b239b9cd
5 changed files with 451 additions and 124 deletions
+36 -8
View File
@@ -2,11 +2,15 @@ package fr.luc.gamingcore;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.protection.regions.RegionContainer;
import fr.luc.bettermccommands.BetterMcCommands;
import fr.luc.gamingcore.command.GuildCommands;
import fr.luc.gamingcore.database.DatabaseManager;
import fr.luc.gamingcore.database.GuildRepository;
import fr.luc.gamingcore.economy.ChunkPriceCalculator;
import fr.luc.gamingcore.economy.EconomyManager;
import fr.luc.gamingcore.listener.TerritoryProtectionListener;
import fr.luc.gamingcore.manager.GuildManager;
import fr.luc.gamingcore.util.MessageManager;
import fr.luc.gamingcore.util.TextUtil;
import net.milkbowl.vault.economy.Economy;
import org.bukkit.plugin.PluginManager;
@@ -23,11 +27,12 @@ public final class Main extends JavaPlugin {
private Economy economy;
private RegionContainer regionContainer;
private MessageManager messageManager;
private DatabaseManager databaseManager;
private GuildRepository guildRepository;
private EconomyManager economyManager;
private GuildManager guildManager;
private fr.luc.bettermccommands.BetterMcCommands betterMcCommands;
private BetterMcCommands betterMcCommands;
/**
* Méthode appelée lors de l'activation du plugin par le serveur Minecraft.
@@ -37,8 +42,9 @@ public final class Main extends JavaPlugin {
instance = this;
saveDefaultConfig();
// 1. Initialiser le préfixe de texte MiniMessage
TextUtil.setPrefix(getConfig().getString("prefix", "<gradient:#00C9FF:#92FE9D>[Guilde]</gradient> <dark_gray>»</dark_gray> <gray>"));
// 1. Initialiser le gestionnaire de messages (messages.yml)
this.messageManager = new MessageManager(this);
this.messageManager.reload();
// 2. Initialiser les hooks externes
setupWorldGuard();
@@ -58,12 +64,12 @@ public final class Main extends JavaPlugin {
this.guildManager.loadData();
// 6. Initialiser le framework betterMcCommands et enregistrer les commandes
this.betterMcCommands = fr.luc.bettermccommands.BetterMcCommands.create(this);
new fr.luc.gamingcore.command.GuildCommands(this, guildManager).register();
this.betterMcCommands = BetterMcCommands.create(this);
new GuildCommands(this, guildManager).register();
// 7. Enregistrer les écouteurs d'événements
PluginManager pm = getServer().getPluginManager();
pm.registerEvents(new TerritoryProtectionListener(guildManager), this);
pm.registerEvents(new TerritoryProtectionListener(this), this);
getLogger().info("GamingCore (Système de Guilde - Cœur Métier & Commandes) a ete active avec succes !");
}
@@ -86,6 +92,19 @@ public final class Main extends JavaPlugin {
getLogger().info("GamingCore a ete desactive !");
}
/**
* Recharge à chaud tous les fichiers de configuration (config.yml et messages.yml)
* et réapplique les paramètres économiques aux calculateurs.
*/
public synchronized void reloadPlugin() {
reloadConfig();
if (messageManager != null) {
messageManager.reload();
}
loadConfigSettings();
getLogger().info("Configuration et messages recharges avec succes !");
}
/**
* Charge et applique les paramètres de configuration dans les différents gestionnaires.
*/
@@ -101,7 +120,7 @@ public final class Main extends JavaPlugin {
double delta = getConfig().getDouble("economy.chunk_pricing.delta", 1.15);
long recentWindowMillis = getConfig().getLong("economy.chunk_pricing.recent_window_hours", 24) * 3600_000L;
fr.luc.gamingcore.economy.ChunkPriceCalculator calc = guildManager.getPriceCalculator();
ChunkPriceCalculator calc = guildManager.getPriceCalculator();
calc.setBasePrice(basePrice);
calc.setGrowthRate(growthRate);
calc.setAlpha(alpha);
@@ -152,6 +171,15 @@ public final class Main extends JavaPlugin {
return instance;
}
/**
* Obtient le gestionnaire de messages.
*
* @return L'instance MessageManager.
*/
public MessageManager getMessageManager() {
return messageManager;
}
/**
* Obtient le fournisseur d'économie Vault.
*
@@ -220,7 +248,7 @@ public final class Main extends JavaPlugin {
*
* @return L'instance BetterMcCommands.
*/
public fr.luc.bettermccommands.BetterMcCommands getBetterMcCommands() {
public BetterMcCommands getBetterMcCommands() {
return betterMcCommands;
}
}
@@ -7,23 +7,30 @@ import fr.luc.gamingcore.manager.GuildManager;
import fr.luc.gamingcore.manager.GuildResult;
import fr.luc.gamingcore.manager.InviteManager;
import fr.luc.gamingcore.model.*;
import fr.luc.gamingcore.util.MessageManager;
import fr.luc.gamingcore.util.TextUtil;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import java.time.Duration;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* Enregistrement et définition des commandes de guilde via le framework fluent **betterMcCommands**.
* Enregistrement et définition de la suite complète de commandes `/guild`
* utilisant les validateurs métier et les confirmations de **betterMcCommands**.
*/
public class GuildCommands {
private static final Pattern GUILD_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]{3,16}$");
private final Main plugin;
private final GuildManager guildManager;
private final InviteManager inviteManager;
private final MessageManager msg;
/**
* Constructeur des commandes de guilde.
@@ -35,6 +42,7 @@ public class GuildCommands {
this.plugin = plugin;
this.guildManager = guildManager;
this.inviteManager = guildManager.getInviteManager();
this.msg = plugin.getMessageManager();
}
/**
@@ -66,6 +74,9 @@ public class GuildCommands {
context.reply("<yellow>/guild withdraw <montant></yellow> <gray>- Retirer de la banque</gray>");
context.reply("<yellow>/guild sethome</yellow> <gray>- Définir le point de ralliement</gray>");
context.reply("<yellow>/guild home</yellow> <gray>- Se téléporter au home de guilde</gray>");
if (context.getSender().hasPermission("guild.admin.reload")) {
context.reply("<red>/guild reload</red> <gray>- Recharger les configurations et messages</gray>");
}
})
// -------------------------------------------------------------
@@ -76,15 +87,44 @@ public class GuildCommands {
.playerOnly()
.argument(Arguments.string("nom").description("Nom de la guilde (3-16 car)"))
.argument(Arguments.string("tag").optional().description("Tag court (ex: LEG)"))
// Validations déclaratives
.validate(context -> !guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.already_in_guild"))
.validate(context -> GUILD_NAME_PATTERN.matcher(context.getString("nom")).matches(),
msg.get("guild.invalid_name"))
.validate(context -> guildManager.getGuildByName(context.getString("nom")) == null,
msg.get("guild.name_taken", "{guild}", ""))
.validate(context -> {
Chunk chunk = context.getPlayer().getLocation().getChunk();
return !guildManager.isChunkClaimed(chunk.getWorld().getName(), chunk.getX(), chunk.getZ());
}, msg.get("guild.creation.chunk_already_claimed"))
.validate(context -> guildManager.getEconomyManager().has(context.getPlayer(), creationCost),
msg.get("guild.creation.insufficient_funds", "{cost}", guildManager.getEconomyManager().format(creationCost)))
// Confirmation avec détails du claim et du coût
.requireConfirmation(Duration.ofSeconds(20))
.executes(context -> {
Player player = context.getPlayer();
String name = context.getString("nom");
Optional<String> tagOpt = context.getOptional("tag", String.class);
String tag = tagOpt.orElse(name.length() <= 4 ? name.toUpperCase() : name.substring(0, 4).toUpperCase());
Location loc = player.getLocation();
Chunk chunk = loc.getChunk();
GuildResult result = guildManager.createGuild(player, name, tag, creationCost);
if (result.isSuccess()) {
context.replySuccess(result.getMessage());
String successMsg = msg.get("guild.creation.success",
"{guild}", name,
"{tag}", tag,
"{cost}", guildManager.getEconomyManager().format(creationCost),
"{world}", loc.getWorld().getName(),
"{chunk_x}", String.valueOf(chunk.getX()),
"{chunk_z}", String.valueOf(chunk.getZ())
);
context.reply(successMsg);
} else {
context.replyError(result.getMessage());
}
@@ -98,17 +138,25 @@ public class GuildCommands {
.description("Dissoudre définitivement votre guilde")
.aliases("delete", "supprimer")
.playerOnly()
.validate(context -> guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.not_in_guild"))
.validate(context -> {
Guild g = guildManager.getGuildByPlayer(context.getPlayer().getUniqueId());
if (g == null) return false;
GuildRank leaderRank = g.getLeaderRank();
GuildMember member = g.getMember(context.getPlayer().getUniqueId());
return member != null && leaderRank != null && member.getRankId().equalsIgnoreCase(leaderRank.getId());
}, msg.get("guild.disband.not_leader"))
.requireConfirmation(Duration.ofSeconds(15))
.executes(context -> {
Player player = context.getPlayer();
Guild guild = guildManager.getGuildByPlayer(player.getUniqueId());
if (guild == null) {
context.replyError("Vous n'appartenez à aucune guilde.");
return;
}
if (guild == null) return;
String guildName = guild.getName();
GuildResult result = guildManager.disbandGuild(player, guild);
if (result.isSuccess()) {
context.replySuccess(result.getMessage());
context.reply(msg.get("guild.disband.success", "{guild}", guildName));
} else {
context.replyError(result.getMessage());
}
@@ -122,69 +170,64 @@ public class GuildCommands {
.description("Inviter un joueur connecté à rejoindre votre guilde")
.playerOnly()
.argument(Arguments.player("joueur").description("Joueur cible"))
.validate(context -> guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.not_in_guild"))
.validate(context -> {
Guild g = guildManager.getGuildByPlayer(context.getPlayer().getUniqueId());
return g != null && g.hasPermission(context.getPlayer().getUniqueId(), GuildPermission.INVITE);
}, msg.get("guild.invite.no_permission"))
.validate(context -> !context.getTargetPlayer("joueur").getUniqueId().equals(context.getPlayer().getUniqueId()),
msg.get("guild.invite.cannot_invite_self"))
.validate(context -> !guildManager.isInGuild(context.getTargetPlayer("joueur").getUniqueId()),
msg.get("guild.invite.target_already_in_guild", "{player}", ""))
.executes(context -> {
Player sender = context.getPlayer();
Guild guild = guildManager.getGuildByPlayer(sender.getUniqueId());
if (guild == null) {
context.replyError("Vous n'appartenez à aucune guilde.");
return;
}
if (!guild.hasPermission(sender.getUniqueId(), GuildPermission.INVITE)) {
context.replyError("Votre rang ne vous autorise pas à inviter des joueurs.");
return;
}
Player target = context.getTargetPlayer("joueur");
if (target.getUniqueId().equals(sender.getUniqueId())) {
context.replyError("Vous ne pouvez pas vous inviter vous-même.");
return;
}
if (guildManager.isInGuild(target.getUniqueId())) {
context.replyError("Ce joueur appartient déjà à une guilde.");
return;
}
GuildInvite invite = inviteManager.sendInvite(guild, sender, target);
context.replySuccess("Invitation envoyée à <white>" + target.getName() + "</white> (Expire dans " + invite.getRemainingSeconds() + "s).");
TextUtil.sendMessage(target, "<gradient:#00C9FF:#92FE9D>[Guilde]</gradient> Vous avez été invité à rejoindre la guilde <white><bold>" + guild.getName() + "</bold></white> par <yellow>" + sender.getName() + "</yellow> !");
TextUtil.sendMessage(target, "<gray>Tapez <yellow>/guild join " + guild.getName() + "</yellow> pour accepter.</gray>");
context.reply(msg.get("guild.invite.sent",
"{player}", target.getName(),
"{seconds}", String.valueOf(invite.getRemainingSeconds())
));
TextUtil.sendMessage(target, msg.get("guild.invite.received",
"{guild}", guild.getName(),
"{sender}", sender.getName()
));
})
)
// -------------------------------------------------------------
// Sous-commande : /guild join <guilde> (ou /guild accept)
// Sous-commande : /guild join <guilde>
// -------------------------------------------------------------
.subcommand(BetterMcCommands.subBuilder("join")
.description("Accepter une invitation et rejoindre une guilde")
.aliases("accept", "rejoindre")
.playerOnly()
.argument(Arguments.string("guilde").description("Nom de la guilde"))
.validate(context -> !guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.already_in_guild"))
.executes(context -> {
Player player = context.getPlayer();
if (guildManager.isInGuild(player.getUniqueId())) {
context.replyError("Vous appartenez déjà à une guilde.");
return;
}
String guildName = context.getString("guilde");
Guild targetGuild = guildManager.getGuildByName(guildName);
if (targetGuild == null) {
context.replyError("La guilde '" + guildName + "' n'existe pas.");
context.reply(msg.get("guild.guild_not_found", "{guild}", guildName));
return;
}
GuildInvite invite = inviteManager.getValidInvite(player, targetGuild.getId());
if (invite == null) {
context.replyError("Vous n'avez aucune invitation valide en attente pour cette guilde.");
context.reply(msg.get("guild.invite.no_pending_invite", "{guild}", targetGuild.getName()));
return;
}
inviteManager.removeInvite(player, targetGuild.getId());
GuildResult result = guildManager.addMember(targetGuild, player);
if (result.isSuccess()) {
context.replySuccess(result.getMessage());
context.reply(msg.get("guild.invite.accepted", "{player}", player.getName()));
} else {
context.replyError(result.getMessage());
}
@@ -198,17 +241,17 @@ public class GuildCommands {
.description("Quitter votre guilde actuelle")
.aliases("quitter")
.playerOnly()
.validate(context -> guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.not_in_guild"))
.executes(context -> {
Player player = context.getPlayer();
Guild guild = guildManager.getGuildByPlayer(player.getUniqueId());
if (guild == null) {
context.replyError("Vous n'appartenez à aucune guilde.");
return;
}
if (guild == null) return;
String guildName = guild.getName();
GuildResult result = guildManager.leaveGuild(player, guild);
if (result.isSuccess()) {
context.replySuccess(result.getMessage());
context.reply(msg.get("guild.leave.success", "{guild}", guildName));
} else {
context.replyError(result.getMessage());
}
@@ -222,18 +265,18 @@ public class GuildCommands {
.description("Expulser un membre de votre guilde")
.playerOnly()
.argument(Arguments.player("joueur").description("Joueur à expulser"))
.validate(context -> guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.not_in_guild"))
.executes(context -> {
Player player = context.getPlayer();
Guild guild = guildManager.getGuildByPlayer(player.getUniqueId());
if (guild == null) {
context.replyError("Vous n'appartenez à aucune guilde.");
return;
}
if (guild == null) return;
Player target = context.getTargetPlayer("joueur");
GuildResult result = guildManager.kickMember(player, guild, target.getUniqueId());
if (result.isSuccess()) {
context.replySuccess(result.getMessage());
context.reply(msg.get("guild.kick.success", "{player}", target.getName()));
TextUtil.sendMessage(target, msg.get("guild.kick.target_notify", "{guild}", guild.getName()));
} else {
context.replyError(result.getMessage());
}
@@ -246,19 +289,30 @@ public class GuildCommands {
.subcommand(BetterMcCommands.subBuilder("claim")
.description("Revendiquer le chunk actuel pour votre guilde")
.playerOnly()
.validate(context -> guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.not_in_guild"))
.validate(context -> {
Guild g = guildManager.getGuildByPlayer(context.getPlayer().getUniqueId());
return g != null && g.hasPermission(context.getPlayer().getUniqueId(), GuildPermission.CLAIM);
}, msg.get("guild.claim.no_permission"))
.validate(context -> {
Chunk chunk = context.getPlayer().getLocation().getChunk();
return !guildManager.isChunkClaimed(chunk.getWorld().getName(), chunk.getX(), chunk.getZ());
}, msg.get("guild.claim.already_claimed"))
.executes(context -> {
Player player = context.getPlayer();
Guild guild = guildManager.getGuildByPlayer(player.getUniqueId());
if (guild == null) {
context.replyError("Vous n'appartenez à aucune guilde.");
return;
}
Location loc = player.getLocation();
Chunk chunk = loc.getChunk();
double price = guildManager.getPriceCalculator().calculateNextChunkPrice(guild);
GuildResult result = guildManager.claimChunk(player, guild, loc.getWorld(), chunk.getX(), chunk.getZ());
if (result.isSuccess()) {
context.replySuccess(result.getMessage());
context.reply(msg.get("guild.claim.success",
"{cost}", guildManager.getEconomyManager().format(price),
"{count}", String.valueOf(guild.getClaimCount())
));
} else {
context.replyError(result.getMessage());
}
@@ -271,19 +325,21 @@ public class GuildCommands {
.subcommand(BetterMcCommands.subBuilder("unclaim")
.description("Libérer le chunk actuel")
.playerOnly()
.validate(context -> guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.not_in_guild"))
.validate(context -> {
Guild g = guildManager.getGuildByPlayer(context.getPlayer().getUniqueId());
return g != null && g.hasPermission(context.getPlayer().getUniqueId(), GuildPermission.UNCLAIM);
}, msg.get("guild.claim.unclaim_no_permission"))
.executes(context -> {
Player player = context.getPlayer();
Guild guild = guildManager.getGuildByPlayer(player.getUniqueId());
if (guild == null) {
context.replyError("Vous n'appartenez à aucune guilde.");
return;
}
Location loc = player.getLocation();
Chunk chunk = loc.getChunk();
GuildResult result = guildManager.unclaimChunk(player, guild, loc.getWorld(), chunk.getX(), chunk.getZ());
if (result.isSuccess()) {
context.replySuccess(result.getMessage());
context.reply(msg.get("guild.claim.unclaim_success"));
} else {
context.replyError(result.getMessage());
}
@@ -298,18 +354,19 @@ public class GuildCommands {
.aliases("deposer", "don")
.playerOnly()
.argument(Arguments.decimal("montant", 1.0, 1000000000.0).description("Somme à déposer"))
.validate(context -> guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.not_in_guild"))
.executes(context -> {
Player player = context.getPlayer();
Guild guild = guildManager.getGuildByPlayer(player.getUniqueId());
if (guild == null) {
context.replyError("Vous n'appartenez à aucune guilde.");
return;
}
double amount = context.getDouble("montant");
GuildResult result = guildManager.depositMoney(player, guild, amount);
if (result.isSuccess()) {
context.replySuccess(result.getMessage());
context.reply(msg.get("guild.bank.deposit_success",
"{amount}", guildManager.getEconomyManager().format(amount),
"{balance}", guildManager.getEconomyManager().format(guild.getBalance())
));
} else {
context.replyError(result.getMessage());
}
@@ -324,18 +381,18 @@ public class GuildCommands {
.aliases("retirer")
.playerOnly()
.argument(Arguments.decimal("montant", 1.0, 1000000000.0).description("Somme à retirer"))
.validate(context -> guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.not_in_guild"))
.executes(context -> {
Player player = context.getPlayer();
Guild guild = guildManager.getGuildByPlayer(player.getUniqueId());
if (guild == null) {
context.replyError("Vous n'appartenez à aucune guilde.");
return;
}
double amount = context.getDouble("montant");
GuildResult result = guildManager.withdrawMoney(player, guild, amount);
if (result.isSuccess()) {
context.replySuccess(result.getMessage());
context.reply(msg.get("guild.bank.withdraw_success",
"{amount}", guildManager.getEconomyManager().format(amount)
));
} else {
context.replyError(result.getMessage());
}
@@ -348,29 +405,26 @@ public class GuildCommands {
.subcommand(BetterMcCommands.subBuilder("sethome")
.description("Définir le home de guilde à votre position")
.playerOnly()
.validate(context -> guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.not_in_guild"))
.validate(context -> {
Guild g = guildManager.getGuildByPlayer(context.getPlayer().getUniqueId());
return g != null && g.hasPermission(context.getPlayer().getUniqueId(), GuildPermission.SET_HOME);
}, msg.get("guild.home.set_no_permission"))
.executes(context -> {
Player player = context.getPlayer();
Guild guild = guildManager.getGuildByPlayer(player.getUniqueId());
if (guild == null) {
context.replyError("Vous n'appartenez à aucune guilde.");
return;
}
if (!guild.hasPermission(player.getUniqueId(), GuildPermission.SET_HOME)) {
context.replyError("Votre rang ne vous autorise pas à modifier le home de guilde.");
return;
}
Location loc = player.getLocation();
Chunk chunk = loc.getChunk();
if (!guild.isClaimed(loc.getWorld().getName(), chunk.getX(), chunk.getZ())) {
context.replyError("Le point de ralliement doit impérativement se situer sur un territoire possédé par la guilde !");
context.reply(msg.get("guild.home.set_must_be_in_claim"));
return;
}
guild.setHome(loc);
plugin.getGuildRepository().saveGuildAsync(guild);
context.replySuccess("Le home de guilde a été mis à jour avec succès !");
context.reply(msg.get("guild.home.set_success"));
})
)
@@ -380,28 +434,25 @@ public class GuildCommands {
.subcommand(BetterMcCommands.subBuilder("home")
.description("Se téléporter au point de ralliement de la guilde")
.playerOnly()
.validate(context -> guildManager.isInGuild(context.getPlayer().getUniqueId()),
msg.get("guild.not_in_guild"))
.validate(context -> {
Guild g = guildManager.getGuildByPlayer(context.getPlayer().getUniqueId());
return g != null && g.hasPermission(context.getPlayer().getUniqueId(), GuildPermission.HOME);
}, msg.get("guild.home.teleport_no_permission"))
.executes(context -> {
Player player = context.getPlayer();
Guild guild = guildManager.getGuildByPlayer(player.getUniqueId());
if (guild == null) {
context.replyError("Vous n'appartenez à aucune guilde.");
return;
}
if (!guild.hasPermission(player.getUniqueId(), GuildPermission.HOME)) {
context.replyError("Votre rang ne vous autorise pas à vous téléporter au home de guilde.");
return;
}
Location homeLoc = guild.getHomeLocation();
if (homeLoc == null) {
context.replyError("Aucun home n'est défini pour votre guilde.");
context.reply(msg.get("guild.home.not_set"));
return;
}
player.teleportAsync(homeLoc).thenAccept(success -> {
if (success) {
TextUtil.sendActionBar(player, "<green>Téléportation au home de guilde réussie !</green>");
msg.sendActionBar(player, "guild.home.teleport_success");
}
});
})
@@ -421,12 +472,12 @@ public class GuildCommands {
} else if (context.isPlayer()) {
targetGuild = guildManager.getGuildByPlayer(context.getPlayer().getUniqueId());
} else {
context.replyError("Veuillez préciser le nom de la guilde depuis la console.");
context.replyError("Veuillez préciser le nom de la guilde.");
return;
}
if (targetGuild == null) {
context.replyError("Guilde introuvable.");
context.reply(msg.get("guild.guild_not_found", "{guild}", nameOpt.orElse("")));
return;
}
@@ -462,7 +513,7 @@ public class GuildCommands {
}
if (targetGuild == null) {
context.replyError("Guilde introuvable.");
context.reply(msg.get("guild.guild_not_found", "{guild}", nameOpt.orElse("")));
return;
}
@@ -480,6 +531,18 @@ public class GuildCommands {
})
)
// -------------------------------------------------------------
// Sous-commande : /guild reload
// -------------------------------------------------------------
.subcommand(BetterMcCommands.subBuilder("reload")
.description("Recharger les fichiers de configuration et de messages")
.permission("guild.admin.reload")
.executes(context -> {
plugin.reloadPlugin();
context.reply(msg.get("general.reloaded"));
})
)
.register();
}
}
@@ -1,16 +1,14 @@
package fr.luc.gamingcore.listener;
import fr.luc.gamingcore.Main;
import fr.luc.gamingcore.manager.GuildManager;
import fr.luc.gamingcore.model.Guild;
import fr.luc.gamingcore.model.GuildPermission;
import fr.luc.gamingcore.util.TextUtil;
import org.bukkit.Chunk;
import fr.luc.gamingcore.util.MessageManager;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import java.util.Objects;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
@@ -22,20 +20,26 @@ import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import java.util.Objects;
/**
* Écouteur d'événements garantissant la protection territoriale des chunks de guilde contre le grief et les intrusions.
*/
public class TerritoryProtectionListener implements Listener {
private final Main plugin;
private final GuildManager guildManager;
private final MessageManager messageManager;
/**
* Constructeur de l'écouteur de protection territoriale.
*
* @param guildManager Le gestionnaire de guildes.
* @param plugin Instance principale du plugin.
*/
public TerritoryProtectionListener(GuildManager guildManager) {
this.guildManager = guildManager;
public TerritoryProtectionListener(Main plugin) {
this.plugin = plugin;
this.guildManager = plugin.getGuildManager();
this.messageManager = plugin.getMessageManager();
}
/**
@@ -59,14 +63,14 @@ public class TerritoryProtectionListener implements Listener {
Guild playerGuild = guildManager.getGuildByPlayer(player.getUniqueId());
if (playerGuild == null || !playerGuild.getId().equals(guildAtBlock.getId())) {
event.setCancelled(true);
TextUtil.sendActionBar(player, "<red>⛔ Ce territoire appartient à la guilde <yellow>" + guildAtBlock.getName() + "</yellow> !</red>");
messageManager.sendActionBar(player, "protection.cannot_break", "{guild}", guildAtBlock.getName(), "{tag}", guildAtBlock.getTag());
return;
}
// Le joueur est dans sa propre guilde : vérifier la permission BREAK
if (!guildAtBlock.hasPermission(player.getUniqueId(), GuildPermission.BREAK)) {
event.setCancelled(true);
TextUtil.sendActionBar(player, "<red>Votre rang de guilde ne vous autorise pas à casser des blocs.</red>");
messageManager.sendActionBar(player, "protection.rank_cannot_break");
}
}
@@ -91,14 +95,14 @@ public class TerritoryProtectionListener implements Listener {
Guild playerGuild = guildManager.getGuildByPlayer(player.getUniqueId());
if (playerGuild == null || !playerGuild.getId().equals(guildAtBlock.getId())) {
event.setCancelled(true);
TextUtil.sendActionBar(player, "<red>⛔ Ce territoire appartient à la guilde <yellow>" + guildAtBlock.getName() + "</yellow> !</red>");
messageManager.sendActionBar(player, "protection.cannot_build", "{guild}", guildAtBlock.getName(), "{tag}", guildAtBlock.getTag());
return;
}
// Vérifier la permission BUILD
if (!guildAtBlock.hasPermission(player.getUniqueId(), GuildPermission.BUILD)) {
event.setCancelled(true);
TextUtil.sendActionBar(player, "<red>Votre rang de guilde ne vous autorise pas à poser des blocs.</red>");
messageManager.sendActionBar(player, "protection.rank_cannot_build");
}
}
@@ -134,7 +138,7 @@ public class TerritoryProtectionListener implements Listener {
if (!isSameGuild) {
if (isContainer || isMechanism) {
event.setCancelled(true);
TextUtil.sendActionBar(player, "<red>⛔ Ce territoire appartient à la guilde <yellow>" + guildAtBlock.getName() + "</yellow> !</red>");
messageManager.sendActionBar(player, "protection.cannot_interact", "{guild}", guildAtBlock.getName(), "{tag}", guildAtBlock.getTag());
}
return;
}
@@ -142,10 +146,10 @@ public class TerritoryProtectionListener implements Listener {
// Vérification des permissions spécifiques de rang
if (isContainer && !guildAtBlock.hasPermission(player.getUniqueId(), GuildPermission.CONTAINER)) {
event.setCancelled(true);
TextUtil.sendActionBar(player, "<red>Votre rang ne vous autorise pas à ouvrir les conteneurs de guilde.</red>");
messageManager.sendActionBar(player, "protection.rank_cannot_container");
} else if (isMechanism && !guildAtBlock.hasPermission(player.getUniqueId(), GuildPermission.INTERACT)) {
event.setCancelled(true);
TextUtil.sendActionBar(player, "<red>Votre rang ne vous autorise pas à interagir avec ces mécanismes.</red>");
messageManager.sendActionBar(player, "protection.rank_cannot_interact");
}
}
@@ -156,6 +160,9 @@ public class TerritoryProtectionListener implements Listener {
*/
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onEntityExplode(EntityExplodeEvent event) {
if (!plugin.getConfig().getBoolean("protection.prevent_explosions", true)) {
return;
}
event.blockList().removeIf(block -> guildManager.isChunkClaimed(
block.getWorld().getName(),
block.getX() >> 4,
@@ -170,6 +177,9 @@ public class TerritoryProtectionListener implements Listener {
*/
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onBlockExplode(BlockExplodeEvent event) {
if (!plugin.getConfig().getBoolean("protection.prevent_explosions", true)) {
return;
}
event.blockList().removeIf(block -> guildManager.isChunkClaimed(
block.getWorld().getName(),
block.getX() >> 4,
@@ -184,6 +194,10 @@ public class TerritoryProtectionListener implements Listener {
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent event) {
if (!plugin.getConfig().getBoolean("protection.show_actionbar_on_move", true)) {
return;
}
Location from = event.getFrom();
Location to = event.getTo();
if (to == null) return;
@@ -209,10 +223,13 @@ public class TerritoryProtectionListener implements Listener {
if (currentGuild != null) {
Guild playerGuild = guildManager.getGuildByPlayer(player.getUniqueId());
boolean isOwn = playerGuild != null && playerGuild.getId().equals(currentGuild.getId());
String color = isOwn ? "<green>" : "<yellow>";
TextUtil.sendActionBar(player, "<gray>Entrée dans le territoire : </gray>" + color + "<bold>" + currentGuild.getName() + "</bold></color> <dark_gray>(" + currentGuild.getTag() + ")</dark_gray>");
if (isOwn) {
messageManager.sendActionBar(player, "protection.territory_enter_own", "{guild}", currentGuild.getName(), "{tag}", currentGuild.getTag());
} else {
messageManager.sendActionBar(player, "protection.territory_enter_other", "{guild}", currentGuild.getName(), "{tag}", currentGuild.getTag());
}
} else if (previousGuild != null) {
TextUtil.sendActionBar(player, "<gray>Vous entrez en <green>Zone Libre</green> (Wilderness).</gray>");
messageManager.sendActionBar(player, "protection.territory_leave");
}
}
@@ -0,0 +1,130 @@
package fr.luc.gamingcore.util;
import fr.luc.gamingcore.Main;
import org.bukkit.command.CommandSender;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
/**
* Gestionnaire centralisé des messages configurables et rechargeables depuis `messages.yml`.
*/
public class MessageManager {
private final Main plugin;
private final File messagesFile;
private FileConfiguration messagesConfig;
private final Map<String, String> cachedMessages = new ConcurrentHashMap<>();
private String prefix = "<gradient:#00C9FF:#92FE9D>[Guilde]</gradient> <dark_gray>»</dark_gray> <gray>";
/**
* Constructeur du MessageManager.
*
* @param plugin Instance principale du plugin.
*/
public MessageManager(Main plugin) {
this.plugin = plugin;
this.messagesFile = new File(plugin.getDataFolder(), "messages.yml");
}
/**
* Charge ou recharge le fichier `messages.yml` depuis le disque.
*/
public synchronized void reload() {
if (!messagesFile.exists()) {
plugin.saveResource("messages.yml", false);
}
this.messagesConfig = YamlConfiguration.loadConfiguration(messagesFile);
this.cachedMessages.clear();
// Charger les valeurs par défaut depuis le jar si des clés manquent
InputStream defStream = plugin.getResource("messages.yml");
if (defStream != null) {
YamlConfiguration defConfig = YamlConfiguration.loadConfiguration(new InputStreamReader(defStream, StandardCharsets.UTF_8));
messagesConfig.setDefaults(defConfig);
}
this.prefix = messagesConfig.getString("prefix", prefix);
TextUtil.setPrefix(this.prefix);
// Mettre en cache toutes les chaînes trouvées
for (String key : messagesConfig.getKeys(true)) {
if (messagesConfig.isString(key)) {
cachedMessages.put(key, Objects.requireNonNull(messagesConfig.getString(key)));
}
}
plugin.getLogger().info("Fichier messages.yml charge avec succes (" + cachedMessages.size() + " messages en cache).");
}
/**
* Récupère un message par sa clé et applique le remplacement des placeholders.
*
* @param key La clé du message dans messages.yml.
* @param placeholders Paires clé/valeur pour le remplacement (ex: "{guild}", "Valhalla", "{cost}", "1 000 $").
* @return Le message formaté avec placeholders appliqués.
*/
public String get(String key, String... placeholders) {
String msg = cachedMessages.get(key);
if (msg == null) {
msg = "<red>[Message manquant: " + key + "]</red>";
}
if (placeholders != null && placeholders.length >= 2) {
for (int i = 0; i < placeholders.length - 1; i += 2) {
String phKey = placeholders[i];
String phVal = placeholders[i + 1];
if (phKey != null && phVal != null) {
msg = msg.replace(phKey, phVal);
}
}
}
return msg;
}
/**
* Envoie un message formaté avec MiniMessage à un émetteur de commande.
*
* @param sender Le destinataire du message.
* @param key La clé du message.
* @param placeholders Paires de placeholders.
*/
public void send(CommandSender sender, String key, String... placeholders) {
if (sender == null) return;
String formatted = get(key, placeholders);
TextUtil.sendMessage(sender, formatted);
}
/**
* Envoie une notification dans l'Action Bar d'un joueur.
*
* @param player Le joueur destinataire.
* @param key La clé du message.
* @param placeholders Paires de placeholders.
*/
public void sendActionBar(Player player, String key, String... placeholders) {
if (player == null) return;
String formatted = get(key, placeholders);
TextUtil.sendActionBar(player, formatted);
}
/**
* Obtient le préfixe global actuel.
*
* @return La chaîne MiniMessage du préfixe.
*/
public String getPrefix() {
return prefix;
}
}
+89
View File
@@ -0,0 +1,89 @@
# ==========================================================
# GAMINGCORE - FICHIER DE MESSAGES
# Format : Kyori Adventure MiniMessage (Gradients, Hex, etc.)
# ==========================================================
prefix: "<gradient:#00C9FF:#92FE9D>[Guilde]</gradient> <dark_gray>»</dark_gray> <gray>"
general:
only_players: "<red>Cette commande est réservée aux joueurs en jeu.</red>"
no_permission: "<red>Vous n'avez pas la permission requise.</red>"
player_not_found: "<red>Joueur introuvable ou hors ligne.</red>"
reloaded: "<green>La configuration et les messages ont été rechargés avec succès !</green>"
guild:
already_in_guild: "<red>Vous appartenez déjà à une guilde.</red>"
not_in_guild: "<red>Vous n'appartenez à aucune guilde.</red>"
not_member: "<red>Ce joueur n'est pas membre de votre guilde.</red>"
guild_not_found: "<red>La guilde <yellow>{guild}</yellow> est introuvable.</red>"
invalid_name: "<red>Le nom de guilde doit comporter entre 3 et 16 caractères alphanumériques.</red>"
name_taken: "<red>Une guilde portant le nom <yellow>{guild}</yellow> existe déjà.</red>"
creation:
insufficient_funds: "<red>Fonds insuffisants pour créer une guilde. Requis : <yellow>{cost}</yellow>.</red>"
chunk_already_claimed: "<red>Le chunk où vous vous trouvez est déjà revendiqué par une guilde.</red>"
confirm_prompt: "<gradient:#00C9FF:#92FE9D>[Guilde]</gradient> <yellow>Création de la guilde</yellow> <white><bold>{guild}</bold></white> <dark_gray>[{tag}]</dark_gray> :\n<gray>• Coût : <yellow>{cost}</yellow>\n• Territoire initial : <aqua>{world} ({chunk_x}, {chunk_z})</aqua>\n<gold>⚠ Exécutez à nouveau la commande pour confirmer la création !</gold>"
success: "<green>Félicitations ! La guilde <white><bold>{guild}</bold></white> <dark_gray>[{tag}]</dark_gray> a été fondée !</green>\n<gray>• Coût prélevé : <yellow>{cost}</yellow>\n• Chunk initial revendiqué : <aqua>{world} [{chunk_x}, {chunk_z}]</aqua>\n• Point de ralliement (Home) défini à votre position.</gray>"
disband:
not_leader: "<red>Seul le Chef de la guilde peut dissoudre la guilde.</red>"
confirm_prompt: "<red><bold>⚠ ATTENTION :</bold> Vous allez dissoudre définitivement la guilde <white>{guild}</white> et libérer tous ses territoires.\n<gold>Exécutez à nouveau <yellow>/guild disband</yellow> pour confirmer.</gold></red>"
success: "<yellow>La guilde <white><bold>{guild}</bold></white> a été dissoute avec succès.</yellow>"
leave:
leader_cannot_leave: "<red>En tant que Chef, vous ne pouvez pas quitter la guilde. Transférez le lead ou dissolvez la guilde.</red>"
success: "<yellow>Vous avez quitté la guilde <white>{guild}</white>.</yellow>"
kick:
cannot_kick_self: "<red>Vous ne pouvez pas vous expulser vous-même. Utilisez <yellow>/guild leave</yellow>.</red>"
insufficient_rank: "<red>Vous ne pouvez pas expulser un membre ayant un rang supérieur ou égal au vôtre.</red>"
no_permission: "<red>Votre rang ne vous autorise pas à expulser des membres.</red>"
success: "<yellow><white>{player}</white> a été expulsé de la guilde.</yellow>"
target_notify: "<red>Vous avez été expulsé de la guilde <yellow>{guild}</yellow>.</red>"
invite:
no_permission: "<red>Votre rang ne vous autorise pas à envoyer des invitations.</red>"
cannot_invite_self: "<red>Vous ne pouvez pas vous inviter vous-même.</red>"
target_already_in_guild: "<red><white>{player}</white> appartient déjà à une guilde.</red>"
sent: "<green>Invitation envoyée à <white>{player}</white> (Expire dans <yellow>{seconds}s</yellow>).</green>"
received: "<gradient:#00C9FF:#92FE9D>[Guilde]</gradient> Vous avez été invité à rejoindre la guilde <white><bold>{guild}</bold></white> par <yellow>{sender}</yellow> !\n<gray>Tapez <yellow>/guild join {guild}</yellow> pour accepter.</gray>"
no_pending_invite: "<red>Vous n'avez aucune invitation valide en attente pour la guilde <yellow>{guild}</yellow>.</red>"
accepted: "<green>Le joueur <white>{player}</white> a rejoint la guilde !</green>"
claim:
no_permission: "<red>Votre rang ne vous autorise pas à revendiquer des territoires.</red>"
already_claimed: "<red>Ce chunk est déjà revendiqué.</red>"
insufficient_bank: "<red>La banque de guilde n'a pas assez d'argent (Requis : <yellow>{cost}</yellow>, Solde : <green>{balance}</green>).</red>"
success: "<green>Chunk revendiqué avec succès pour <yellow>{cost}</yellow> ! (Total : <aqua>{count}</aqua> chunks)</green>"
unclaim_no_permission: "<red>Votre rang ne vous autorise pas à abandonner des territoires.</red>"
unclaim_not_owned: "<red>Ce chunk n'appartient pas à votre guilde.</red>"
unclaim_last_chunk: "<red>Vous ne pouvez pas abandonner le dernier territoire de votre guilde.</red>"
unclaim_success: "<yellow>Le chunk a été libéré.</yellow>"
bank:
deposit_no_permission: "<red>Votre rang ne vous autorise pas à déposer dans la banque.</red>"
deposit_insufficient_player: "<red>Vous n'avez pas assez d'argent sur votre compte personnel.</red>"
deposit_success: "<green>Vous avez déposé <yellow>{amount}</yellow> dans la banque de guilde ! (Nouveau solde : <green>{balance}</green>)</green>"
withdraw_no_permission: "<red>Votre rang ne vous autorise pas à retirer de l'argent de la banque.</red>"
withdraw_insufficient_bank: "<red>Le solde de la banque de guilde est insuffisant (Solde actuel : <yellow>{balance}</yellow>).</red>"
withdraw_success: "<green>Vous avez retiré <yellow>{amount}</yellow> de la banque de guilde.</green>"
home:
set_no_permission: "<red>Votre rang ne vous autorise pas à modifier le home de guilde.</red>"
set_must_be_in_claim: "<red>Le point de ralliement doit obligatoirement être situé sur un chunk de la guilde !</red>"
set_success: "<green>Le point de ralliement (Home) a été mis à jour avec succès !</green>"
teleport_no_permission: "<red>Votre rang ne vous autorise pas à vous téléporter au home de guilde.</red>"
not_set: "<red>Aucun home n'est défini pour votre guilde.</red>"
teleport_success: "<green>Téléportation au point de ralliement de guilde réussie !</green>"
protection:
territory_enter_own: "<gray>Territoire de guilde : </gray><green><bold>{guild}</bold></green> <dark_gray>[{tag}]</dark_gray>"
territory_enter_other: "<gray>Territoire étranger : </gray><yellow><bold>{guild}</bold></yellow> <dark_gray>[{tag}]</dark_gray>"
territory_leave: "<gray>Vous entrez en <green>Zone Libre</green> (Wilderness).</gray>"
cannot_break: "<red>⛔ Ce territoire appartient à la guilde <yellow>{guild}</yellow> !</red>"
rank_cannot_break: "<red>Votre rang de guilde ne vous autorise pas à casser des blocs ici.</red>"
cannot_build: "<red>⛔ Ce territoire appartient à la guilde <yellow>{guild}</yellow> !</red>"
rank_cannot_build: "<red>Votre rang de guilde ne vous autorise pas à poser des blocs ici.</red>"
cannot_interact: "<red>⛔ Ce territoire appartient à la guilde <yellow>{guild}</yellow> !</red>"
rank_cannot_container: "<red>Votre rang ne vous autorise pas à ouvrir les conteneurs de guilde.</red>"
rank_cannot_interact: "<red>Votre rang ne vous autorise pas à utiliser ces mécanismes.</red>"