refactor: clean base with WorldGuard, WorldEdit, and Vault dependencies
This commit is contained in:
@@ -23,6 +23,9 @@ dependencies {
|
||||
compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.9")
|
||||
compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.3.0")
|
||||
|
||||
// Vault API (Economy & Permissions)
|
||||
compileOnly("com.github.MilkBowl:VaultAPI:1.7.1")
|
||||
|
||||
// Adventure platform for Bukkit (cross-version RGB, MiniMessage, Titles, ActionBars)
|
||||
implementation("net.kyori:adventure-platform-bukkit:4.3.4")
|
||||
implementation("net.kyori:adventure-text-minimessage:4.17.0")
|
||||
|
||||
@@ -1,23 +1,91 @@
|
||||
package fr.luc.gamingcore;
|
||||
|
||||
import com.sk89q.worldguard.WorldGuard;
|
||||
import com.sk89q.worldguard.protection.regions.RegionContainer;
|
||||
import fr.luc.gamingcore.command.CommandManager;
|
||||
import fr.luc.gamingcore.util.TextUtil;
|
||||
import net.milkbowl.vault.economy.Economy;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
public final class Main extends JavaPlugin {
|
||||
|
||||
private static Main instance;
|
||||
|
||||
private Economy economy;
|
||||
private RegionContainer regionContainer;
|
||||
private CommandManager commandManager;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
getLogger().info("GamingCore a ete active avec succes !");
|
||||
saveDefaultConfig();
|
||||
|
||||
// 1. Initialiser le préfixe de messages MiniMessage
|
||||
TextUtil.setPrefix(getConfig().getString("prefix", "<gradient:#00C9FF:#92FE9D>[Guilde]</gradient> <dark_gray>»</dark_gray> <gray>"));
|
||||
|
||||
// 2. Connexion à WorldGuard
|
||||
setupWorldGuard();
|
||||
|
||||
// 3. Connexion à Vault (Économie)
|
||||
setupEconomy();
|
||||
|
||||
// 4. Initialisation du CommandManager dynamique
|
||||
this.commandManager = new CommandManager(this);
|
||||
|
||||
getLogger().info("GamingCore (Système de Guilde) a ete active avec succes !");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (commandManager != null) {
|
||||
commandManager.shutdown();
|
||||
}
|
||||
getLogger().info("GamingCore a ete desactive !");
|
||||
}
|
||||
|
||||
private void setupWorldGuard() {
|
||||
try {
|
||||
this.regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer();
|
||||
getLogger().info("Connexion avec WorldGuard 7 etablie avec succes !");
|
||||
} catch (Throwable t) {
|
||||
getLogger().warning("Impossible d'initialiser WorldGuard : " + t.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void setupEconomy() {
|
||||
if (getServer().getPluginManager().getPlugin("Vault") == null) {
|
||||
getLogger().warning("Vault n'a pas ete detecte ! Le support economique sera desactive.");
|
||||
return;
|
||||
}
|
||||
|
||||
RegisteredServiceProvider<Economy> rsp = getServer().getServicesManager().getRegistration(Economy.class);
|
||||
if (rsp == null) {
|
||||
getLogger().warning("Aucun provider d'economie (ex: Essentials, TheNewEconomy) trouve via Vault !");
|
||||
return;
|
||||
}
|
||||
|
||||
this.economy = rsp.getProvider();
|
||||
getLogger().info("Connexion avec l'economie Vault (" + economy.getName() + ") etablie avec succes !");
|
||||
}
|
||||
|
||||
public static Main getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Economy getEconomy() {
|
||||
return economy;
|
||||
}
|
||||
|
||||
public boolean hasEconomy() {
|
||||
return economy != null;
|
||||
}
|
||||
|
||||
public RegionContainer getRegionContainer() {
|
||||
return regionContainer;
|
||||
}
|
||||
|
||||
public CommandManager getCommandManager() {
|
||||
return commandManager;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
package fr.luc.gamingcore.command;
|
||||
|
||||
import fr.luc.gamingcore.command.argument.ArgumentParseException;
|
||||
import fr.luc.gamingcore.command.argument.CommandArgument;
|
||||
import fr.luc.gamingcore.command.cooldown.CooldownManager;
|
||||
import fr.luc.gamingcore.util.TextUtil;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class AppCommand {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger("GamingCore");
|
||||
|
||||
private final String name;
|
||||
private final List<String> aliases;
|
||||
private final String description;
|
||||
private final String permission;
|
||||
private final String permissionMessage;
|
||||
private final boolean playerOnly;
|
||||
private final long cooldownMillis;
|
||||
private final String cooldownBypassPermission;
|
||||
private final List<CommandArgument<?>> arguments;
|
||||
private final Map<String, AppCommand> subCommands;
|
||||
private final CommandHandler handler;
|
||||
|
||||
public AppCommand(String name, List<String> aliases, String description, String permission,
|
||||
String permissionMessage, boolean playerOnly, long cooldownMillis,
|
||||
String cooldownBypassPermission, List<CommandArgument<?>> arguments,
|
||||
Map<String, AppCommand> subCommands, CommandHandler handler) {
|
||||
this.name = name.toLowerCase();
|
||||
this.aliases = aliases != null ? aliases : Collections.emptyList();
|
||||
this.description = description != null ? description : "";
|
||||
this.permission = permission;
|
||||
this.permissionMessage = permissionMessage != null ? permissionMessage : "<red>Tu n'as pas la permission d'exécuter cette commande.</red>";
|
||||
this.playerOnly = playerOnly;
|
||||
this.cooldownMillis = cooldownMillis;
|
||||
this.cooldownBypassPermission = cooldownBypassPermission != null ? cooldownBypassPermission : "gamingcore.cooldown.bypass";
|
||||
this.arguments = arguments != null ? arguments : Collections.emptyList();
|
||||
this.subCommands = subCommands != null ? subCommands : Collections.emptyMap();
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public List<String> getAliases() {
|
||||
return aliases;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public String getPermission() {
|
||||
return permission;
|
||||
}
|
||||
|
||||
public boolean isPlayerOnly() {
|
||||
return playerOnly;
|
||||
}
|
||||
|
||||
public long getCooldownMillis() {
|
||||
return cooldownMillis;
|
||||
}
|
||||
|
||||
public List<CommandArgument<?>> getArguments() {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
public Map<String, AppCommand> getSubCommands() {
|
||||
return subCommands;
|
||||
}
|
||||
|
||||
public AppCommand getSubCommand(String name) {
|
||||
if (name == null) return null;
|
||||
String lower = name.toLowerCase();
|
||||
AppCommand direct = subCommands.get(lower);
|
||||
if (direct != null) return direct;
|
||||
for (AppCommand sub : subCommands.values()) {
|
||||
if (sub.getAliases().stream().anyMatch(a -> a.equalsIgnoreCase(lower))) {
|
||||
return sub;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void execute(CommandSender sender, String label, String[] args, CooldownManager cooldownManager, String currentPath) {
|
||||
String fullPath = currentPath.isEmpty() ? name : currentPath + " " + name;
|
||||
|
||||
// 1. Permission check
|
||||
if (permission != null && !permission.isEmpty() && !sender.hasPermission(permission)) {
|
||||
TextUtil.sendMessage(sender, permissionMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Player-only check
|
||||
if (playerOnly && !(sender instanceof Player)) {
|
||||
sender.sendMessage("Cette commande doit être exécutée par un joueur.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Sub-command check
|
||||
if (args.length > 0) {
|
||||
AppCommand sub = getSubCommand(args[0]);
|
||||
if (sub != null) {
|
||||
String[] nextArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
sub.execute(sender, args[0], nextArgs, cooldownManager, fullPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 4. If no sub-command matched but handler is null, show help
|
||||
if (handler == null) {
|
||||
sendHelp(sender, fullPath);
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = sender instanceof Player ? (Player) sender : null;
|
||||
|
||||
// 5. Cooldown check
|
||||
if (player != null && cooldownMillis > 0 && !player.hasPermission(cooldownBypassPermission)) {
|
||||
String cooldownKey = fullPath;
|
||||
if (cooldownManager.isOnCooldown(player, cooldownKey)) {
|
||||
double remaining = cooldownManager.getRemainingSeconds(player, cooldownKey);
|
||||
TextUtil.sendMessage(player, "<red>⏳ Veuillez patienter <yellow>" + remaining + "s</yellow> avant de réutiliser cette commande.</red>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Argument parsing
|
||||
Map<String, Object> parsedArgs = new HashMap<>();
|
||||
CommandContext preliminaryContext = new CommandContext(sender, label, args, parsedArgs, fullPath);
|
||||
|
||||
int argIndex = 0;
|
||||
for (int i = 0; i < arguments.size(); i++) {
|
||||
CommandArgument<?> argument = arguments.get(i);
|
||||
|
||||
if (argIndex >= args.length) {
|
||||
if (argument.isRequired()) {
|
||||
TextUtil.sendMessage(sender, "<red>Syntaxe incorrecte : <yellow>/" + fullPath + " " + getSyntax() + "</yellow></red>");
|
||||
return;
|
||||
} else {
|
||||
parsedArgs.put(argument.getName().toLowerCase(), argument.getDefaultValue());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
String inputString;
|
||||
if (argument.isGreedy()) {
|
||||
// Consume all remaining arguments
|
||||
inputString = String.join(" ", Arrays.copyOfRange(args, argIndex, args.length));
|
||||
argIndex = args.length;
|
||||
} else {
|
||||
inputString = args[argIndex];
|
||||
argIndex++;
|
||||
}
|
||||
|
||||
try {
|
||||
Object parsed = argument.getParser().parse(preliminaryContext, inputString);
|
||||
parsedArgs.put(argument.getName().toLowerCase(), parsed);
|
||||
} catch (ArgumentParseException e) {
|
||||
TextUtil.sendMessage(sender, "<red>" + e.getMessage() + "</red>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Execution
|
||||
CommandContext finalContext = new CommandContext(sender, label, args, parsedArgs, fullPath);
|
||||
try {
|
||||
handler.execute(finalContext);
|
||||
|
||||
// Apply cooldown on success
|
||||
if (player != null && cooldownMillis > 0 && !player.hasPermission(cooldownBypassPermission)) {
|
||||
cooldownManager.applyCooldown(player, fullPath, cooldownMillis);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
TextUtil.sendMessage(sender, "<red>Une erreur est survenue lors de l'exécution de la commande.</red>");
|
||||
LOGGER.log(Level.SEVERE, "Erreur lors de l'exécution de la commande " + fullPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> tabComplete(CommandSender sender, String label, String[] args, String currentPath) {
|
||||
String fullPath = currentPath.isEmpty() ? name : currentPath + " " + name;
|
||||
|
||||
if (permission != null && !permission.isEmpty() && !sender.hasPermission(permission)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (args.length == 0) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
if (args.length == 1) {
|
||||
String prefix = args[0].toLowerCase();
|
||||
List<String> suggestions = new ArrayList<>();
|
||||
|
||||
// Suggest subcommands
|
||||
for (AppCommand sub : subCommands.values()) {
|
||||
if (sub.getPermission() == null || sub.getPermission().isEmpty() || sender.hasPermission(sub.getPermission())) {
|
||||
if (sub.getName().toLowerCase().startsWith(prefix)) {
|
||||
suggestions.add(sub.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also check first argument completions if applicable
|
||||
if (!arguments.isEmpty()) {
|
||||
CommandContext ctx = new CommandContext(sender, label, args, Collections.emptyMap(), fullPath);
|
||||
suggestions.addAll(arguments.get(0).getParser().complete(ctx, args[0]));
|
||||
}
|
||||
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
// Check if first arg matches a subcommand
|
||||
AppCommand sub = getSubCommand(args[0]);
|
||||
if (sub != null) {
|
||||
String[] nextArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||
return sub.tabComplete(sender, args[0], nextArgs, fullPath);
|
||||
}
|
||||
|
||||
// Argument completions
|
||||
int targetArgIndex = args.length - 1;
|
||||
if (targetArgIndex < arguments.size()) {
|
||||
CommandArgument<?> argument = arguments.get(targetArgIndex);
|
||||
CommandContext ctx = new CommandContext(sender, label, args, Collections.emptyMap(), fullPath);
|
||||
return argument.getParser().complete(ctx, args[targetArgIndex]);
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public String getSyntax() {
|
||||
if (arguments.isEmpty()) return "";
|
||||
return arguments.stream()
|
||||
.map(CommandArgument::getFormattedSyntax)
|
||||
.collect(Collectors.joining(" "));
|
||||
}
|
||||
|
||||
public void sendHelp(CommandSender sender, String fullPath) {
|
||||
TextUtil.sendRawMessage(sender, "<gradient:#00C9FF:#92FE9D>═══════════ [ " + fullPath.toUpperCase() + " ] ═══════════</gradient>");
|
||||
if (!description.isEmpty()) {
|
||||
TextUtil.sendRawMessage(sender, " <gray>" + description + "</gray>");
|
||||
}
|
||||
if (!subCommands.isEmpty()) {
|
||||
for (AppCommand sub : subCommands.values()) {
|
||||
if (sub.getPermission() == null || sub.getPermission().isEmpty() || sender.hasPermission(sub.getPermission())) {
|
||||
String subSyntax = sub.getSyntax();
|
||||
String syntaxPart = subSyntax.isEmpty() ? "" : " " + subSyntax;
|
||||
TextUtil.sendRawMessage(sender, " <yellow>/" + fullPath + " " + sub.getName() + syntaxPart + "</yellow> <dark_gray>-</dark_gray> <gray>" + sub.getDescription() + "</gray>");
|
||||
}
|
||||
}
|
||||
}
|
||||
TextUtil.sendRawMessage(sender, "<gradient:#00C9FF:#92FE9D>════════════════════════════════════════</gradient>");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package fr.luc.gamingcore.command;
|
||||
|
||||
import fr.luc.gamingcore.command.cooldown.CooldownManager;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BukkitCommandWrapper extends Command {
|
||||
|
||||
private final AppCommand appCommand;
|
||||
private final CooldownManager cooldownManager;
|
||||
|
||||
public BukkitCommandWrapper(AppCommand appCommand, CooldownManager cooldownManager) {
|
||||
super(appCommand.getName(), appCommand.getDescription(), "/" + appCommand.getName(), appCommand.getAliases());
|
||||
this.appCommand = appCommand;
|
||||
this.cooldownManager = cooldownManager;
|
||||
if (appCommand.getPermission() != null && !appCommand.getPermission().isEmpty()) {
|
||||
setPermission(appCommand.getPermission());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
||||
appCommand.execute(sender, commandLabel, args, cooldownManager, "");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> tabComplete(CommandSender sender, String alias, String[] args) throws IllegalArgumentException {
|
||||
return appCommand.tabComplete(sender, alias, args, "");
|
||||
}
|
||||
|
||||
public AppCommand getAppCommand() {
|
||||
return appCommand;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package fr.luc.gamingcore.command;
|
||||
|
||||
import fr.luc.gamingcore.command.argument.CommandArgument;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CommandBuilder {
|
||||
|
||||
private final String name;
|
||||
private final List<String> aliases = new ArrayList<>();
|
||||
private String description = "";
|
||||
private String permission = null;
|
||||
private String permissionMessage = null;
|
||||
private boolean playerOnly = false;
|
||||
private long cooldownMillis = 0L;
|
||||
private String cooldownBypassPermission = "gamingcore.cooldown.bypass";
|
||||
private final List<CommandArgument<?>> arguments = new ArrayList<>();
|
||||
private final Map<String, AppCommand> subCommands = new LinkedHashMap<>();
|
||||
private CommandHandler handler = null;
|
||||
|
||||
private CommandBuilder(String name) {
|
||||
this.name = Objects.requireNonNull(name, "Command name cannot be null").trim();
|
||||
}
|
||||
|
||||
public static CommandBuilder create(String name) {
|
||||
return new CommandBuilder(name);
|
||||
}
|
||||
|
||||
public CommandBuilder aliases(String... aliases) {
|
||||
if (aliases != null) {
|
||||
this.aliases.addAll(Arrays.asList(aliases));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder description(String description) {
|
||||
this.description = description != null ? description : "";
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder permission(String permission) {
|
||||
this.permission = permission;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder permissionMessage(String message) {
|
||||
this.permissionMessage = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder playerOnly() {
|
||||
this.playerOnly = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder playerOnly(boolean playerOnly) {
|
||||
this.playerOnly = playerOnly;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder cooldown(long amount, TimeUnit unit) {
|
||||
this.cooldownMillis = unit.toMillis(amount);
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder cooldownSeconds(int seconds) {
|
||||
return cooldown(seconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public CommandBuilder cooldownBypass(String permission) {
|
||||
this.cooldownBypassPermission = permission;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder argument(CommandArgument<?> argument) {
|
||||
if (argument != null) {
|
||||
this.arguments.add(argument);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder subCommand(AppCommand subCommand) {
|
||||
if (subCommand != null) {
|
||||
this.subCommands.put(subCommand.getName().toLowerCase(), subCommand);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder subCommand(CommandBuilder subBuilder) {
|
||||
if (subBuilder != null) {
|
||||
AppCommand built = subBuilder.build();
|
||||
this.subCommands.put(built.getName().toLowerCase(), built);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public CommandBuilder executes(CommandHandler handler) {
|
||||
this.handler = handler;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AppCommand build() {
|
||||
return new AppCommand(name, aliases, description, permission, permissionMessage,
|
||||
playerOnly, cooldownMillis, cooldownBypassPermission, arguments, subCommands, handler);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package fr.luc.gamingcore.command;
|
||||
|
||||
import fr.luc.gamingcore.util.TextUtil;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class CommandContext {
|
||||
|
||||
private final CommandSender sender;
|
||||
private final String label;
|
||||
private final String[] rawArgs;
|
||||
private final Map<String, Object> parsedArgs;
|
||||
private final String fullCommandPath;
|
||||
|
||||
public CommandContext(CommandSender sender, String label, String[] rawArgs, Map<String, Object> parsedArgs, String fullCommandPath) {
|
||||
this.sender = sender;
|
||||
this.label = label;
|
||||
this.rawArgs = rawArgs != null ? rawArgs : new String[0];
|
||||
this.parsedArgs = parsedArgs != null ? parsedArgs : Collections.emptyMap();
|
||||
this.fullCommandPath = fullCommandPath;
|
||||
}
|
||||
|
||||
public CommandSender getSender() {
|
||||
return sender;
|
||||
}
|
||||
|
||||
public boolean isPlayer() {
|
||||
return sender instanceof Player;
|
||||
}
|
||||
|
||||
public boolean isConsole() {
|
||||
return !isPlayer();
|
||||
}
|
||||
|
||||
public Player getPlayer() {
|
||||
return isPlayer() ? (Player) sender : null;
|
||||
}
|
||||
|
||||
public String getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public String[] getRawArgs() {
|
||||
return rawArgs;
|
||||
}
|
||||
|
||||
public String getFullCommandPath() {
|
||||
return fullCommandPath;
|
||||
}
|
||||
|
||||
public boolean has(String argName) {
|
||||
return parsedArgs.containsKey(argName.toLowerCase());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String argName, Class<T> clazz) {
|
||||
Object value = parsedArgs.get(argName.toLowerCase());
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (clazz.isInstance(value)) {
|
||||
return (T) value;
|
||||
}
|
||||
throw new ClassCastException("L'argument '" + argName + "' est de type " + value.getClass().getSimpleName() + ", attendu: " + clazz.getSimpleName());
|
||||
}
|
||||
|
||||
public Object get(String argName) {
|
||||
return parsedArgs.get(argName.toLowerCase());
|
||||
}
|
||||
|
||||
public String getString(String argName) {
|
||||
return get(argName, String.class);
|
||||
}
|
||||
|
||||
public int getInt(String argName) {
|
||||
Integer val = get(argName, Integer.class);
|
||||
return val != null ? val : 0;
|
||||
}
|
||||
|
||||
public double getDouble(String argName) {
|
||||
Double val = get(argName, Double.class);
|
||||
return val != null ? val : 0.0;
|
||||
}
|
||||
|
||||
public Player getTargetPlayer(String argName) {
|
||||
return get(argName, Player.class);
|
||||
}
|
||||
|
||||
public void reply(String message) {
|
||||
TextUtil.sendMessage(sender, message);
|
||||
}
|
||||
|
||||
public void sendRaw(String message) {
|
||||
TextUtil.sendRawMessage(sender, message);
|
||||
}
|
||||
|
||||
public void error(String errorMessage) {
|
||||
TextUtil.sendMessage(sender, "<red>" + errorMessage + "</red>");
|
||||
}
|
||||
|
||||
public void sendActionBar(String message) {
|
||||
if (isPlayer()) {
|
||||
TextUtil.sendActionBar(getPlayer(), message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package fr.luc.gamingcore.command;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface CommandHandler {
|
||||
void execute(CommandContext context) throws Exception;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package fr.luc.gamingcore.command;
|
||||
|
||||
import fr.luc.gamingcore.Main;
|
||||
import fr.luc.gamingcore.command.cooldown.CooldownManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandMap;
|
||||
import org.bukkit.command.SimpleCommandMap;
|
||||
import org.bukkit.plugin.SimplePluginManager;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class CommandManager {
|
||||
|
||||
private final Main plugin;
|
||||
private final CooldownManager cooldownManager;
|
||||
private final Map<String, BukkitCommandWrapper> registeredCommands = new ConcurrentHashMap<>();
|
||||
private CommandMap commandMap;
|
||||
|
||||
public CommandManager(Main plugin) {
|
||||
this.plugin = plugin;
|
||||
this.cooldownManager = new CooldownManager();
|
||||
this.commandMap = resolveCommandMap();
|
||||
}
|
||||
|
||||
private CommandMap resolveCommandMap() {
|
||||
// Try direct Bukkit.getCommandMap() (Paper API)
|
||||
try {
|
||||
Method method = Bukkit.getServer().getClass().getMethod("getCommandMap");
|
||||
return (CommandMap) method.invoke(Bukkit.getServer());
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
// Fallback: Reflection on SimplePluginManager
|
||||
try {
|
||||
Field field = SimplePluginManager.class.getDeclaredField("commandMap");
|
||||
field.setAccessible(true);
|
||||
return (CommandMap) field.get(Bukkit.getPluginManager());
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.SEVERE, "Impossible de recuperer le CommandMap de Bukkit !", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void register(AppCommand appCommand) {
|
||||
if (commandMap == null) {
|
||||
commandMap = resolveCommandMap();
|
||||
if (commandMap == null) {
|
||||
plugin.getLogger().severe("Impossible d'enregistrer la commande " + appCommand.getName() + " : CommandMap introuvable !");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
BukkitCommandWrapper wrapper = new BukkitCommandWrapper(appCommand, cooldownManager);
|
||||
commandMap.register(plugin.getName().toLowerCase(), wrapper);
|
||||
registeredCommands.put(appCommand.getName().toLowerCase(), wrapper);
|
||||
|
||||
plugin.getLogger().info("Commande dynamique enregistree : /" + appCommand.getName() + " (alias: " + String.join(", ", appCommand.getAliases()) + ")");
|
||||
}
|
||||
|
||||
public void register(CommandBuilder builder) {
|
||||
if (builder != null) {
|
||||
register(builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
public void unregister(String commandName) {
|
||||
if (commandName == null) return;
|
||||
BukkitCommandWrapper wrapper = registeredCommands.remove(commandName.toLowerCase());
|
||||
if (wrapper != null && commandMap instanceof SimpleCommandMap) {
|
||||
try {
|
||||
Field knownCommandsField = SimpleCommandMap.class.getDeclaredField("knownCommands");
|
||||
knownCommandsField.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Command> knownCommands = (Map<String, Command>) knownCommandsField.get(commandMap);
|
||||
knownCommands.remove(wrapper.getName().toLowerCase());
|
||||
for (String alias : wrapper.getAliases()) {
|
||||
knownCommands.remove(alias.toLowerCase());
|
||||
}
|
||||
wrapper.unregister(commandMap);
|
||||
} catch (Exception e) {
|
||||
plugin.getLogger().log(Level.WARNING, "Erreur lors du desenregistrement de la commande " + commandName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
for (String name : registeredCommands.keySet()) {
|
||||
unregister(name);
|
||||
}
|
||||
registeredCommands.clear();
|
||||
cooldownManager.cleanExpired();
|
||||
}
|
||||
|
||||
public CooldownManager getCooldownManager() {
|
||||
return cooldownManager;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package fr.luc.gamingcore.command.argument;
|
||||
|
||||
public class ArgumentParseException extends Exception {
|
||||
|
||||
public ArgumentParseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package fr.luc.gamingcore.command.argument;
|
||||
|
||||
import fr.luc.gamingcore.command.CommandContext;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ArgumentParser<T> {
|
||||
|
||||
/**
|
||||
* Parses the string input into the target typed object.
|
||||
*
|
||||
* @param ctx The current command execution context
|
||||
* @param input The raw argument string entered by the user
|
||||
* @return The parsed object
|
||||
* @throws ArgumentParseException If the input is invalid
|
||||
*/
|
||||
T parse(CommandContext ctx, String input) throws ArgumentParseException;
|
||||
|
||||
/**
|
||||
* Provides dynamic tab completions for this argument.
|
||||
*
|
||||
* @param ctx The current command context
|
||||
* @param input The current prefix typed by the user
|
||||
* @return A list of suggestions
|
||||
*/
|
||||
default List<String> complete(CommandContext ctx, String input) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package fr.luc.gamingcore.command.argument;
|
||||
|
||||
public class CommandArgument<T> {
|
||||
|
||||
private final String name;
|
||||
private final String description;
|
||||
private final ArgumentParser<T> parser;
|
||||
private final boolean required;
|
||||
private final T defaultValue;
|
||||
private final boolean greedy;
|
||||
|
||||
public CommandArgument(String name, String description, ArgumentParser<T> parser, boolean required, T defaultValue, boolean greedy) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.parser = parser;
|
||||
this.required = required;
|
||||
this.defaultValue = defaultValue;
|
||||
this.greedy = greedy;
|
||||
}
|
||||
|
||||
public static <T> CommandArgument<T> required(String name, ArgumentParser<T> parser) {
|
||||
return new CommandArgument<>(name, "", parser, true, null, false);
|
||||
}
|
||||
|
||||
public static <T> CommandArgument<T> required(String name, String description, ArgumentParser<T> parser) {
|
||||
return new CommandArgument<>(name, description, parser, true, null, false);
|
||||
}
|
||||
|
||||
public static <T> CommandArgument<T> optional(String name, ArgumentParser<T> parser) {
|
||||
return new CommandArgument<>(name, "", parser, false, null, false);
|
||||
}
|
||||
|
||||
public static <T> CommandArgument<T> optional(String name, ArgumentParser<T> parser, T defaultValue) {
|
||||
return new CommandArgument<>(name, "", parser, false, defaultValue, false);
|
||||
}
|
||||
|
||||
public static <T> CommandArgument<T> optional(String name, String description, ArgumentParser<T> parser, T defaultValue) {
|
||||
return new CommandArgument<>(name, description, parser, false, defaultValue, false);
|
||||
}
|
||||
|
||||
public static <T> CommandArgument<T> greedy(String name, String description, ArgumentParser<T> parser, boolean required, T defaultValue) {
|
||||
return new CommandArgument<>(name, description, parser, required, defaultValue, true);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public ArgumentParser<T> getParser() {
|
||||
return parser;
|
||||
}
|
||||
|
||||
public boolean isRequired() {
|
||||
return required;
|
||||
}
|
||||
|
||||
public T getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public boolean isGreedy() {
|
||||
return greedy;
|
||||
}
|
||||
|
||||
public String getFormattedSyntax() {
|
||||
return required ? "<" + name + ">" : "[" + name + "]";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package fr.luc.gamingcore.command.argument.parsers;
|
||||
|
||||
import fr.luc.gamingcore.command.CommandContext;
|
||||
import fr.luc.gamingcore.command.argument.ArgumentParser;
|
||||
import fr.luc.gamingcore.command.argument.ArgumentParseException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DoubleArgument implements ArgumentParser<Double> {
|
||||
|
||||
private final double min;
|
||||
private final double max;
|
||||
|
||||
public DoubleArgument() {
|
||||
this(Double.MIN_VALUE, Double.MAX_VALUE);
|
||||
}
|
||||
|
||||
public DoubleArgument(double min, double max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public static DoubleArgument decimal() {
|
||||
return new DoubleArgument();
|
||||
}
|
||||
|
||||
public static DoubleArgument min(double min) {
|
||||
return new DoubleArgument(min, Double.MAX_VALUE);
|
||||
}
|
||||
|
||||
public static DoubleArgument range(double min, double max) {
|
||||
return new DoubleArgument(min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double parse(CommandContext ctx, String input) throws ArgumentParseException {
|
||||
try {
|
||||
double value = Double.parseDouble(input.replace(',', '.'));
|
||||
if (value < min || value > max) {
|
||||
throw new ArgumentParseException("La valeur doit être comprise entre " + min + " et " + max + ".");
|
||||
}
|
||||
return value;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ArgumentParseException("'" + input + "' n'est pas un nombre décimal valide.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> complete(CommandContext ctx, String input) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package fr.luc.gamingcore.command.argument.parsers;
|
||||
|
||||
import fr.luc.gamingcore.command.CommandContext;
|
||||
import fr.luc.gamingcore.command.argument.ArgumentParser;
|
||||
import fr.luc.gamingcore.command.argument.ArgumentParseException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class IntegerArgument implements ArgumentParser<Integer> {
|
||||
|
||||
private final int min;
|
||||
private final int max;
|
||||
|
||||
public IntegerArgument() {
|
||||
this(Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public IntegerArgument(int min, int max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public static IntegerArgument integer() {
|
||||
return new IntegerArgument();
|
||||
}
|
||||
|
||||
public static IntegerArgument min(int min) {
|
||||
return new IntegerArgument(min, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
public static IntegerArgument range(int min, int max) {
|
||||
return new IntegerArgument(min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer parse(CommandContext ctx, String input) throws ArgumentParseException {
|
||||
try {
|
||||
int value = Integer.parseInt(input);
|
||||
if (value < min || value > max) {
|
||||
throw new ArgumentParseException("Le nombre doit être compris entre " + min + " et " + max + ".");
|
||||
}
|
||||
return value;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ArgumentParseException("'" + input + "' n'est pas un nombre entier valide.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> complete(CommandContext ctx, String input) {
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package fr.luc.gamingcore.command.argument.parsers;
|
||||
|
||||
import fr.luc.gamingcore.command.CommandContext;
|
||||
import fr.luc.gamingcore.command.argument.ArgumentParser;
|
||||
import fr.luc.gamingcore.command.argument.ArgumentParseException;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PlayerArgument implements ArgumentParser<Player> {
|
||||
|
||||
public static PlayerArgument player() {
|
||||
return new PlayerArgument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player parse(CommandContext ctx, String input) throws ArgumentParseException {
|
||||
if (input == null || input.isEmpty()) {
|
||||
throw new ArgumentParseException("Veuillez spécifier un pseudo de joueur.");
|
||||
}
|
||||
Player target = Bukkit.getPlayerExact(input);
|
||||
if (target == null) {
|
||||
target = Bukkit.getPlayer(input);
|
||||
}
|
||||
if (target == null || !target.isOnline()) {
|
||||
throw new ArgumentParseException("Le joueur '<red>" + input + "</red>' n'est pas connecté.");
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> complete(CommandContext ctx, String input) {
|
||||
String lower = input != null ? input.toLowerCase() : "";
|
||||
return Bukkit.getOnlinePlayers().stream()
|
||||
.map(Player::getName)
|
||||
.filter(name -> name.toLowerCase().startsWith(lower))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package fr.luc.gamingcore.command.argument.parsers;
|
||||
|
||||
import fr.luc.gamingcore.command.CommandContext;
|
||||
import fr.luc.gamingcore.command.argument.ArgumentParser;
|
||||
import fr.luc.gamingcore.command.argument.ArgumentParseException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StringArgument implements ArgumentParser<String> {
|
||||
|
||||
private final List<String> suggestions;
|
||||
private final Supplier<List<String>> dynamicSuggestions;
|
||||
|
||||
public StringArgument() {
|
||||
this.suggestions = null;
|
||||
this.dynamicSuggestions = null;
|
||||
}
|
||||
|
||||
public StringArgument(String... suggestions) {
|
||||
this.suggestions = Arrays.asList(suggestions);
|
||||
this.dynamicSuggestions = null;
|
||||
}
|
||||
|
||||
public StringArgument(Supplier<List<String>> dynamicSuggestions) {
|
||||
this.suggestions = null;
|
||||
this.dynamicSuggestions = dynamicSuggestions;
|
||||
}
|
||||
|
||||
public static StringArgument word() {
|
||||
return new StringArgument();
|
||||
}
|
||||
|
||||
public static StringArgument choices(String... choices) {
|
||||
return new StringArgument(choices);
|
||||
}
|
||||
|
||||
public static StringArgument dynamic(Supplier<List<String>> supplier) {
|
||||
return new StringArgument(supplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parse(CommandContext ctx, String input) throws ArgumentParseException {
|
||||
if (input == null || input.trim().isEmpty()) {
|
||||
throw new ArgumentParseException("Le texte ne peut pas être vide.");
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> complete(CommandContext ctx, String input) {
|
||||
List<String> list = suggestions;
|
||||
if (dynamicSuggestions != null) {
|
||||
list = dynamicSuggestions.get();
|
||||
}
|
||||
if (list == null || list.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
String lower = input != null ? input.toLowerCase() : "";
|
||||
return list.stream()
|
||||
.filter(s -> s.toLowerCase().startsWith(lower))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package fr.luc.gamingcore.command.cooldown;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class CooldownManager {
|
||||
|
||||
private final Map<UUID, Map<String, Long>> cooldowns = new ConcurrentHashMap<>();
|
||||
|
||||
public boolean isOnCooldown(Player player, String commandKey) {
|
||||
if (player == null || commandKey == null) return false;
|
||||
long remaining = getRemainingMillis(player, commandKey);
|
||||
return remaining > 0;
|
||||
}
|
||||
|
||||
public long getRemainingMillis(Player player, String commandKey) {
|
||||
if (player == null || commandKey == null) return 0L;
|
||||
Map<String, Long> userCooldowns = cooldowns.get(player.getUniqueId());
|
||||
if (userCooldowns == null) return 0L;
|
||||
|
||||
Long expireAt = userCooldowns.get(commandKey.toLowerCase());
|
||||
if (expireAt == null) return 0L;
|
||||
|
||||
long diff = expireAt - System.currentTimeMillis();
|
||||
if (diff <= 0) {
|
||||
userCooldowns.remove(commandKey.toLowerCase());
|
||||
return 0L;
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
|
||||
public double getRemainingSeconds(Player player, String commandKey) {
|
||||
long millis = getRemainingMillis(player, commandKey);
|
||||
if (millis <= 0) return 0.0;
|
||||
return Math.round((millis / 1000.0) * 10.0) / 10.0;
|
||||
}
|
||||
|
||||
public void applyCooldown(Player player, String commandKey, long durationMillis) {
|
||||
if (player == null || commandKey == null || durationMillis <= 0) return;
|
||||
cooldowns.computeIfAbsent(player.getUniqueId(), k -> new ConcurrentHashMap<>())
|
||||
.put(commandKey.toLowerCase(), System.currentTimeMillis() + durationMillis);
|
||||
}
|
||||
|
||||
public void clearCooldown(Player player, String commandKey) {
|
||||
if (player == null || commandKey == null) return;
|
||||
Map<String, Long> userCooldowns = cooldowns.get(player.getUniqueId());
|
||||
if (userCooldowns != null) {
|
||||
userCooldowns.remove(commandKey.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
public void cleanExpired() {
|
||||
long now = System.currentTimeMillis();
|
||||
cooldowns.forEach((uuid, map) -> {
|
||||
map.entrySet().removeIf(entry -> entry.getValue() <= now);
|
||||
});
|
||||
cooldowns.entrySet().removeIf(entry -> entry.getValue().isEmpty());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package fr.luc.gamingcore.util;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
public final class TextUtil {
|
||||
|
||||
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
|
||||
private static final LegacyComponentSerializer AMPERSAND_SERIALIZER = LegacyComponentSerializer.legacyAmpersand();
|
||||
|
||||
private static String prefix = "<gradient:#00C9FF:#92FE9D>[Guilde]</gradient> <dark_gray>»</dark_gray> <gray>";
|
||||
|
||||
private TextUtil() {}
|
||||
|
||||
public static void setPrefix(String newPrefix) {
|
||||
if (newPrefix != null) {
|
||||
prefix = newPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string containing MiniMessage tags or legacy color codes into a Component.
|
||||
*/
|
||||
public static Component parse(String input) {
|
||||
if (input == null || input.isEmpty()) {
|
||||
return Component.empty();
|
||||
}
|
||||
String formatted = input.replace('§', '&');
|
||||
if (formatted.contains("&")) {
|
||||
return AMPERSAND_SERIALIZER.deserialize(formatted);
|
||||
}
|
||||
return MINI_MESSAGE.deserialize(input);
|
||||
}
|
||||
|
||||
public static void sendMessage(CommandSender sender, String message) {
|
||||
if (sender == null || message == null) return;
|
||||
sender.sendMessage(parse(prefix + message));
|
||||
}
|
||||
|
||||
public static void sendRawMessage(CommandSender sender, String message) {
|
||||
if (sender == null || message == null) return;
|
||||
sender.sendMessage(parse(message));
|
||||
}
|
||||
|
||||
public static void sendActionBar(Player player, String message) {
|
||||
if (player == null || message == null) return;
|
||||
player.sendActionBar(parse(message));
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,13 @@ version: 1.0.0
|
||||
main: fr.luc.gamingcore.Main
|
||||
api-version: '1.16'
|
||||
author: Luc
|
||||
description: GamingCore Plugin
|
||||
description: GamingCore Plugin - Système de Guilde
|
||||
depend:
|
||||
- WorldGuard
|
||||
- Vault
|
||||
softdepend:
|
||||
- WorldEdit
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user