Initialisation complète de la bibliothèque betterMcCommands (DSL, arguments typés, cycle de vie des événements, injection dynamique CommandMap)

This commit is contained in:
2026-08-20 17:26:36 +02:00
parent c3aa06ab3b
commit 637aa7b3ec
59 changed files with 5252 additions and 1 deletions
@@ -0,0 +1,321 @@
package fr.luc.bettermccommands.api;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.bukkit.entity.Player;
import java.time.Duration;
import java.util.*;
/**
* Contexte complet d'exécution d'une commande.
* Contient l'émetteur, les arguments analysés et typés, les métadonnées et des utilitaires de réponse.
*/
public class CommandContext {
private static final MiniMessage MINI_MESSAGE = MiniMessage.miniMessage();
private final CommandSender sender;
private final String label;
private final String[] rawArgs;
private final Map<String, Object> arguments;
private final Map<String, Object> metadata;
/**
* Construit un nouveau contexte de commande.
*
* @param sender L'émetteur de la commande.
* @param label Le nom ou alias utilisé pour invoquer la commande.
* @param rawArgs Les arguments bruts sous forme de tableau de chaînes.
* @param arguments La table des arguments résolus et typés.
*/
public CommandContext(CommandSender sender, String label, String[] rawArgs, Map<String, Object> arguments) {
this.sender = Objects.requireNonNull(sender, "sender cannot be null");
this.label = label != null ? label : "";
this.rawArgs = rawArgs != null ? rawArgs : new String[0];
this.arguments = arguments != null ? new HashMap<>(arguments) : new HashMap<>();
this.metadata = new HashMap<>();
}
/**
* @return L'émetteur de la commande (Player, ConsoleCommandSender, etc.).
*/
public CommandSender getSender() {
return sender;
}
/**
* @return true si l'émetteur est un {@link Player}, sinon false.
*/
public boolean isPlayer() {
return sender instanceof Player;
}
/**
* @return true si l'émetteur est la console du serveur.
*/
public boolean isConsole() {
return sender instanceof ConsoleCommandSender;
}
/**
* Récupère le joueur émetteur de la commande.
*
* @return L'instance de {@link Player}.
* @throws IllegalStateException si l'émetteur n'est pas un joueur.
*/
public Player getPlayer() {
if (!isPlayer()) {
throw new IllegalStateException("CommandSender is not a Player: " + sender.getClass().getSimpleName());
}
return (Player) sender;
}
/**
* Récupère la console émettrice de la commande.
*
* @return L'instance de {@link ConsoleCommandSender}.
* @throws IllegalStateException si l'émetteur n'est pas la console.
*/
public ConsoleCommandSender getConsole() {
if (!isConsole()) {
throw new IllegalStateException("CommandSender is not a ConsoleCommandSender: " + sender.getClass().getSimpleName());
}
return (ConsoleCommandSender) sender;
}
/**
* @return Le label ou alias utilisé lors de l'exécution.
*/
public String getLabel() {
return label;
}
/**
* @return Le tableau des arguments bruts passés lors de l'appel.
*/
public String[] getRawArgs() {
return rawArgs.clone();
}
/**
* @return Une vue non modifiable des arguments analysés.
*/
public Map<String, Object> getArguments() {
return Collections.unmodifiableMap(arguments);
}
/**
* Vérifie si un argument du nom donné a été renseigné et analysé.
*
* @param name Le nom de l'argument.
* @return true si l'argument est présent, sinon false.
*/
public boolean hasArgument(String name) {
return arguments.containsKey(name);
}
/**
* Récupère un argument typé par son nom.
*
* @param name Le nom de l'argument.
* @param type La classe attendue pour l'argument.
* @param <T> Le type générique.
* @return L'instance de l'argument analysé.
* @throws IllegalArgumentException si l'argument est manquant ou n'est pas du type attendu.
*/
@SuppressWarnings("unchecked")
public <T> T get(String name, Class<T> type) {
Object val = arguments.get(name);
if (val == null) {
throw new IllegalArgumentException("Argument '" + name + "' is missing from context.");
}
if (!type.isInstance(val)) {
throw new IllegalArgumentException("Argument '" + name + "' is of type " +
val.getClass().getSimpleName() + ", expected " + type.getSimpleName());
}
return (T) val;
}
/**
* Récupère un argument de manière optionnelle.
*
* @param name Le nom de l'argument.
* @param type La classe attendue pour l'argument.
* @param <T> Le type générique.
* @return Un {@link Optional} contenant la valeur si présente.
*/
@SuppressWarnings("unchecked")
public <T> Optional<T> getOptional(String name, Class<T> type) {
Object val = arguments.get(name);
if (val != null && type.isInstance(val)) {
return Optional.of((T) val);
}
return Optional.empty();
}
/**
* Récupère un argument sous forme de chaîne de caractères.
*
* @param name Le nom de l'argument.
* @return La chaîne analysée.
*/
public String getString(String name) {
return get(name, String.class);
}
/**
* Récupère un argument entier.
*
* @param name Le nom de l'argument.
* @return L'entier analysé.
*/
public int getInt(String name) {
return get(name, Integer.class);
}
/**
* Récupère un argument double.
*
* @param name Le nom de l'argument.
* @return Le double analysé.
*/
public double getDouble(String name) {
return get(name, Double.class);
}
/**
* Récupère un argument booléen.
*
* @param name Le nom de l'argument.
* @return Le booléen analysé.
*/
public boolean getBoolean(String name) {
return get(name, Boolean.class);
}
/**
* Récupère un joueur cible ciblé par l'argument.
*
* @param name Le nom de l'argument.
* @return Le joueur cible.
*/
public Player getTargetPlayer(String name) {
return get(name, Player.class);
}
/**
* Récupère un monde ciblé par l'argument.
*
* @param name Le nom de l'argument.
* @return Le monde cible.
*/
public World getWorld(String name) {
return get(name, World.class);
}
/**
* Récupère une durée ciblée par l'argument.
*
* @param name Le nom de l'argument.
* @return La durée analysée.
*/
public Duration getDuration(String name) {
return get(name, Duration.class);
}
/**
* Définit une métadonnée personnalisée dans le contexte d'exécution.
*
* @param key La clé de la métadonnée.
* @param value La valeur associée.
*/
public void setMetadata(String key, Object value) {
this.metadata.put(key, value);
}
/**
* Récupère une métadonnée du contexte.
*
* @param key La clé.
* @param type Le type attendu.
* @param <T> Le type générique.
* @return Un {@link Optional} contenant la métadonnée si présente.
*/
@SuppressWarnings("unchecked")
public <T> Optional<T> getMetadata(String key, Class<T> type) {
Object val = metadata.get(key);
if (val != null && type.isInstance(val)) {
return Optional.of((T) val);
}
return Optional.empty();
}
/**
* Envoie un message formaté avec MiniMessage à l'émetteur de la commande.
*
* @param miniMessageText Le texte formaté avec tags MiniMessage (ex: "<green>Succès !</green>").
*/
public void reply(String miniMessageText) {
if (miniMessageText == null || miniMessageText.isEmpty()) {
return;
}
try {
if (sender instanceof Audience) {
((Audience) sender).sendMessage(MINI_MESSAGE.deserialize(miniMessageText));
} else {
sender.sendMessage(ChatColor.translateAlternateColorCodes('&', miniMessageText));
}
} catch (Throwable t) {
sender.sendMessage(miniMessageText);
}
}
/**
* Envoie un composant Kyori Adventure directement à l'émetteur.
*
* @param component Le composant texte Adventure.
*/
public void reply(Component component) {
if (component == null) {
return;
}
if (sender instanceof Audience) {
((Audience) sender).sendMessage(component);
} else {
sender.sendMessage(component.toString());
}
}
/**
* Envoie un message de succès (préfixé en vert).
*
* @param message Le message de succès.
*/
public void replySuccess(String message) {
reply("<dark_gray>[<green>✔</green>]</dark_gray> <green>" + message + "</green>");
}
/**
* Envoie un message d'erreur (préfixé en rouge).
*
* @param message Le message d'erreur.
*/
public void replyError(String message) {
reply("<dark_gray>[<red>✖</red>]</dark_gray> <red>" + message + "</red>");
}
/**
* Envoie un message informatif (préfixé en bleu/aqua).
*
* @param message Le message d'information.
*/
public void replyInfo(String message) {
reply("<dark_gray>[<aqua></aqua>]</dark_gray> <gray>" + message + "</gray>");
}
}