# ⚡ betterMcCommands [![Java](https://img.shields.io/badge/Java-17%2B%20%2F%2021-orange.svg)](https://www.oracle.com/java/) [![Paper](https://img.shields.io/badge/Paper-1.20.4%2B-blue.svg)](https://papermc.io/) [![Kyori Adventure](https://img.shields.io/badge/Adventure-MiniMessage-purple.svg)](https://docs.advntr.dev/) [![License](https://img.shields.io/badge/License-MIT-green.svg)](#) Une bibliothèque Java moderne, fluide et événementielle permettant de créer, structurer et gérer des commandes Minecraft de façon hautement dynamique sans aucune déclaration préalable dans `plugin.yml`. --- ## 📑 Sommaire - [📚 Guides Détaillés (Dossier `/docs`)](#-guides-détaillés-dossier-docs) - [🌟 Fonctionnalités](#-fonctionnalités) - [🏗️ Architecture & Flux d'Exécution](#️-architecture--flux-dexécution) - [📦 Installation & Configuration](#-installation--configuration) - [🚀 Guide de Démarrage Rapide](#-guide-de-démarrage-rapide) - [💡 Exemples de Commandes](#-exemples-de-commandes) - [1. Commande de Démonstration Complète (`/commande-demo`)](#1-commande-de-démonstration-complète-commande-demo) - [2. Sous-Commandes Imbriquées](#2-sous-commandes-imbriquées) - [3. Arguments Gourmands (Greedy String)](#3-arguments-gourmands-greedy-string) - [4. Système de Validation Métier (Pré-requis)](#4-système-de-validation-métier-pré-requis) - [5. Confirmation Préalable & Double Validation (`.requireConfirmation`)](#5-confirmation-préalable--double-validation-requireconfirmation) - [6. Répétition & Cadence de Commandes (`CommandRepeater`)](#6-répétition--cadence-de-commandes-commandrepeater) - [7. Temps de Recharge (Cooldowns)](#7-temps-de-recharge-cooldowns) - [🧩 Catalogue des Arguments Typés](#-catalogue-des-arguments-typés) - [🎯 Système d'Événements & Cycle de Vie](#-système-dévénements--cycle-de-vie) - [Liste des Événements Disponibles](#liste-des-événements-disponibles) - [Écouteurs par Annotations (`@CommandEventHandler`)](#écouteurs-par-annotations-commandeventhandler) - [📂 Organisation des Packages](#-organisation-des-packages) --- ## 📚 Guides Détaillés (Dossier `/docs`) Pour aller plus loin, consultez la documentation modulaire : * 📘 [**01. Guide de Démarrage Rapide**](docs/01_GETTING_STARTED.md) : Installation, structure de plugin et initialisation. * 🏗️ [**02. Fluent Builder DSL**](docs/02_COMMAND_BUILDER_DSL.md) : Déclaration de commandes racines, sous-commandes imbriquées, restrictions et alias. * 🧩 [**03. Arguments Typés & Auto-complétion**](docs/03_TYPED_ARGUMENTS.md) : Catalogue exhaustif des arguments, parsers personnalisés et Tab-Complete. * 🎯 [**04. Système d'Événements & Cycle de Vie**](docs/04_LIFECYCLE_EVENTS.md) : Hooks d'exécution, écouteurs `@CommandEventHandler` et gestion des erreurs. * 🛡️ [**05. Validation & Double Confirmation**](docs/05_VALIDATION_AND_CONFIRMATION.md) : Validateurs de pré-requis et confirmations interactives. * ⏳ [**06. Cooldowns & Répétition de Commandes**](docs/06_COOLDOWNS_AND_REPEAT.md) : Temps de recharge, bypass et exécution répétée (`CommandRepeater`). * ⚙️ [**07. Plateforme & Injection CommandMap**](docs/07_PLATFORM_AND_INJECTION.md) : Fonctionnement sous le capot, réflexion et hot-reload. --- ## 🌟 Fonctionnalités - **DSL Fluide & Déclaratif** : Déclaration intuitive basée sur le pattern Builder chainable (`BetterMcCommands.builder("ma-commande")`). - **Zéro `plugin.yml`** : Injection directe par réflexion dans la `CommandMap` du serveur Minecraft avec support du dé-enregistrement à chaud. - **Validation Métier Directe & Extensible** : - Validateurs au niveau commande (`.validate(ctx -> condition, "Erreur...")`) - Validateurs au niveau argument (`argument.validate(val -> condition, "Erreur...")`) - **Confirmation Préalable (Double validation)** : Sécurisation des commandes critiques (`/disband`, `/delete-data`) avec expiration (`.requireConfirmation(Duration.ofSeconds(15))`). - **Répétition & Tâches Cadencées (`CommandRepeater`)** : Répétition automatique d'une action à intervalles réguliers avec suivi de progression et annulation. - **Système d'Arguments Typés & Auto-complétion** : - Conversion et validation automatiques avec messages d'erreurs clairs. - Auto-complétion dynamique (Tab-Complete) intelligente selon le type de chaque argument. - **Moteur d'Événements Intégré & Annulable** : - Interception des commandes au niveau local (sur la commande) ou global via un bus d'événements. - Annulation de l'exécution (`PreExecuteEvent`) pour intégrer facilement des vérifications (combat tag, économie, inventaire). - **Temps de recharge (Cooldowns) natifs** : Cooldowns par joueur avec support des permissions de bypass. - **Formatage Moderne Kyori Adventure / MiniMessage** : Couleurs HEX, dégradés (``), composants enrichis et fallback automatique. --- ## 🏗️ Architecture & Flux d'Exécution ```mermaid flowchart TD Player["Joueur ou Console"] -->|Invoque /demo give Luc 10| Wrapper["PaperCommandWrapper (Bukkit)"] Wrapper --> Dispatcher["CommandDispatcher (Moteur de routage)"] Dispatcher --> STypeCheck{"Vérification Type Émetteur"} STypeCheck -- Non conforme --> EvtPerm1["Dispatch PermissionDeniedEvent"] STypeCheck -- Conforme --> PermCheck{"Vérification Permissions"} PermCheck -- Manquante --> EvtPerm2["Dispatch PermissionDeniedEvent"] PermCheck -- Valide --> CoolCheck{"Vérification Cooldown"} CoolCheck -- Actif --> EvtCool["Dispatch CommandCooldownEvent"] CoolCheck -- Inactif --> ArgParse{"Parsing & Validation des Arguments"} ArgParse -- Erreur format / invalide --> EvtSyntax["Dispatch CommandSyntaxErrorEvent"] ArgParse -- Succès --> ConfCheck{"Confirmation Requise ?"} ConfCheck -- Non confirmée --> EvtConf["Dispatch ConfirmationRequiredEvent & Invite à retaper"] ConfCheck -- Confirmée ou Non requise --> ValCheck{"Validateurs (CommandValidator)"} ValCheck -- Invalide --> ValErr["Envoi Message d'Erreur & Arrêt"] ValCheck -- Valide --> EvtPre{"Dispatch CommandPreExecuteEvent"} EvtPre -- Annulé (setCancelled) --> Cancelled["Arrêt de l'exécution"] EvtPre -- Non annulé --> Exec["Exécution du CommandExecutor métier"] Exec --> ApplyCooldown["Application du Cooldown"] Exec --> EvtPost["Dispatch CommandPostExecuteEvent (Métriques/Logs)"] ``` --- ## 📦 Installation & Configuration ### Gradle (Kotlin DSL) ```kotlin repositories { mavenCentral() maven("https://repo.papermc.io/repository/maven-public/") } dependencies { implementation("fr.luc:betterMcCommands:1.0.0-SNAPSHOT") } ``` --- ## 🚀 Guide de Démarrage Rapide ### 1. Initialisation dans la classe principale (`JavaPlugin`) ```java package fr.luc.monplugin; import fr.luc.bettermccommands.BetterMcCommands; import org.bukkit.plugin.java.JavaPlugin; public class MonPlugin extends JavaPlugin { private BetterMcCommands commands; @Override public void onEnable() { // 1. Initialisation du gestionnaire pour ce plugin this.commands = BetterMcCommands.create(this); // 2. Enregistrement d'écouteurs d'événements globaux (optionnel) this.commands.registerListeners(new MonEcouteurDeCommandes()); // 3. Enregistrement de vos commandes creerCommandes(); } private void creerCommandes() { BetterMcCommands.builder("ping") .description("Répond pong !") .executes(context -> context.replySuccess("Pong !")) .register(); } @Override public void onDisable() { // Désenregistrement à chaud et nettoyage propre if (commands != null) { commands.unregisterAll(); } } } ``` --- ## 💡 Exemples de Commandes ### 1. Commande de Démonstration Complète (`/commande-demo`) ```java BetterMcCommands.builder("commande-demo") .description("Commande principale de démonstration") .aliases("demo", "cdemo") .permission("bettermc.demo") // Hook local avant exécution (annulable) .onPreExecute(event -> { if (event.isPlayer() && isInCombat(event.getPlayer())) { event.setCancelled(true); event.reply("Vous ne pouvez pas exécuter cette commande en combat !"); } }) // Exécution de la commande racine .executes(context -> { context.reply("=== betterMcCommands ==="); context.reply("Utilisez /demo give pour donner des ressources."); }) // Sous-commande avec arguments typés et valeur par défaut .subcommand(BetterMcCommands.subBuilder("give") .description("Donne des ressources à un joueur cible") .permission("bettermc.demo.give") .argument(Arguments.player("cible").description("Joueur recevant les items")) .argument(Arguments.integer("quantite", 1, 64).defaultValue(1).description("Nombre d'items (1-64)")) .executes(context -> { Player target = context.getTargetPlayer("cible"); int quantite = context.getInt("quantite"); context.replySuccess("Attribution de " + quantite + " items à " + target.getName() + " !"); }) ) .register(); ``` --- ### 2. Sous-Commandes Imbriquées Créez des arbres hiérarchiques profonds de façon fluide : ```java BetterMcCommands.builder("guilde") .description("Gestion de votre guilde") .subcommand(BetterMcCommands.subBuilder("admin") .permission("guilde.admin") .subcommand(BetterMcCommands.subBuilder("rank") .subcommand(BetterMcCommands.subBuilder("set") .argument(Arguments.player("joueur")) .argument(Arguments.enumOf("grade", MonGradeEnum.class)) .executes(context -> { Player p = context.getTargetPlayer("joueur"); MonGradeEnum grade = context.get("grade", MonGradeEnum.class); context.replySuccess("Grade de " + p.getName() + " mis à jour vers " + grade.name()); }) ) ) ) .register(); ``` --- ### 3. Arguments Gourmands (Greedy String) Permet de capturer tout le reste de la ligne sans nécessiter de guillemets (idéal pour les annonces, motifs de sanctions, broadcasts) : ```java BetterMcCommands.builder("broadcast") .aliases("bc", "annonce") .permission("bettermc.broadcast") .argument(Arguments.greedyString("message").description("Le texte complet à diffuser")) .executes(context -> { String message = context.getString("message"); context.replySuccess("Diffusion : " + message + ""); }) .register(); ``` --- ### 4. Système de Validation Métier (Pré-requis) Vous pouvez attacher des validateurs à vos commandes et arguments : ```java // Validation au niveau de la commande BetterMcCommands.builder("level-reward") .playerOnly() .validate(ctx -> ctx.getPlayer().getLevel() >= 10, "Vous devez être niveau 10 minimum !") .executes(ctx -> { ctx.replySuccess("Récompense de niveau réclamée !"); }) .register(); // Validation au niveau d'un argument BetterMcCommands.builder("setprice") .argument(Arguments.decimal("prix").validate(prix -> prix > 0, "Le prix doit être strictement positif !")) .executes(ctx -> { double prix = ctx.getDouble("prix"); ctx.replySuccess("Prix configuré à " + prix + " €."); }) .register(); ``` --- ### 5. Confirmation Préalable & Double Validation (`.requireConfirmation`) Sécurisez les commandes destructrices ou sensibles. Le joueur doit ré-exécuter la commande dans le temps imparti pour confirmer l'action : ```java BetterMcCommands.builder("disband") .description("Dissout votre guilde") .playerOnly() .requireConfirmation(Duration.ofSeconds(15), "ATTENTION : Cette action est irréversible ! Retapez /disband dans les 15s pour confirmer.") .executes(context -> { context.replySuccess("Votre guilde a été dissoute."); }) .register(); ``` --- ### 6. Répétition & Cadence de Commandes (`CommandRepeater`) Permet de répéter une action à intervalle régulier (ex: compte à rebours, diffusion répétée, téléportation avec pulsation) : ```java BetterMcCommands.builder("countdown") .playerOnly() .executes(context -> { // Répète 5 fois toutes les 1 seconde CommandRepeater.repeat(monPlugin, context, 5, Duration.ofSeconds(1), progress -> { int restants = progress.getTotalRuns() - progress.getCurrentRun() + 1; context.reply("Lancement dans " + restants + "..."); }, () -> { // Action finale context.replySuccess("PARTEZ !"); } ); }) .register(); ``` --- ### 7. Temps de Recharge (Cooldowns) ```java BetterMcCommands.builder("kit") .description("Récupère le kit quotidien") .playerOnly() .cooldown(Duration.ofHours(24)) .cooldownBypass("bettermc.bypass.kit") .executes(context -> { context.replySuccess("Vous avez reçu votre kit quotidien !"); }) .register(); ``` --- ## 🧩 Catalogue des Arguments Typés | Méthode Factory | Type Java de retour | Description & Caractéristiques | |---|---|---| | `Arguments.string("nom")` | `String` | Chaîne de caractères simple (un mot ou délimité). | | `Arguments.greedyString("message")` | `String` | Consomme tous les arguments restants jusqu'à la fin de la ligne. | | `Arguments.integer("nb")` | `Integer` | Nombre entier sans restriction de bornes. | | `Arguments.integer("nb", min, max)` | `Integer` | Nombre entier borné avec validation automatique. | | `Arguments.decimal("prix")` | `Double` | Nombre décimal avec support des points (`.`) et virgules (`,`). | | `Arguments.bool("actif")` | `Boolean` | Booléen (`true`/`false`, `oui`/`non`, `1`/`0`, `on`/`off`). | | `Arguments.player("cible")` | `org.bukkit.entity.Player` | Joueur connecté avec auto-complétion des pseudos en ligne. | | `Arguments.offlinePlayer("joueur")` | `org.bukkit.OfflinePlayer` | Joueur hors-ligne ou en ligne ciblé par pseudo ou UUID. | | `Arguments.world("monde")` | `org.bukkit.World` | Monde Bukkit chargé avec suggestions des mondes existants. | | `Arguments.location("coords")` | `org.bukkit.Location` | Coordonnées `x,y,z` avec support des coordonnées relatives (`~`). | | `Arguments.duration("temps")` | `java.time.Duration` | Durées textuelles (ex: `30s`, `15m`, `2h`, `1d`, `7d`). | | `Arguments.enumOf("val", Enum.class)`| `T extends Enum` | Enum Java avec auto-complétion de toutes les constantes. | | `Arguments.custom("cle", parseur)` | `T` | Type personnalisé implémentant `ArgumentType`. | --- ## 🎯 Système d'Événements & Cycle de Vie ### Liste des Événements Disponibles | Événement | Description | Annulable ? | |---|---|:---:| | `CommandPreExecuteEvent` | Déclenché juste avant l'exécution métier. Permet d'annuler ou modifier l'action. | ✅ Oui | | `CommandPostExecuteEvent` | Déclenché après l'exécution. Contient la durée d'exécution et le statut final. | ❌ Non | | `CommandConfirmationRequiredEvent` | Déclenché lors d'une demande de confirmation préalable pour personnaliser l'invite. | ✅ Oui | | `CommandPermissionDeniedEvent` | Déclenché si la permission est manquante ou si l'émetteur n'est pas autorisé. | ✅ Oui | | `CommandSyntaxErrorEvent` | Déclenché lors d'une erreur de syntaxe ou argument manquant. | ✅ Oui | | `CommandCooldownEvent` | Déclenché lorsqu'un joueur tente d'exécuter une commande sous cooldown actif. | ✅ Oui | | `CommandTabCompleteEvent` | Déclenché lors du calcul des suggestions pour filtrer ou injecter des valeurs. | ✅ Oui | --- ### Écouteurs par Annotations (`@CommandEventHandler`) ```java package fr.luc.monplugin.listener; import fr.luc.bettermccommands.event.*; import fr.luc.bettermccommands.event.annotation.CommandEventHandler; public class MonEcouteurDeCommandes { // Intercepte toutes les commandes exécutées @CommandEventHandler(priority = 5) public void onPreExecute(CommandPreExecuteEvent event) { System.out.println("[Audit] Commande /" + event.getNode().getFullName() + " par " + event.getSender().getName()); } // Intercepte uniquement la commande 'commande-demo' @CommandEventHandler(command = "commande-demo") public void onDemoPostExecute(CommandPostExecuteEvent event) { if (event.isSuccessful()) { System.out.println("[Perf] Exécutée en " + event.getExecutionDuration().toMillis() + "ms."); } } // Personnalise les messages de permission refusée @CommandEventHandler public void onPermissionDenied(CommandPermissionDeniedEvent event) { event.setCustomErrorMessage("Accès Refusé : Vous devez posséder la permission [" + event.getRequiredPermission() + "]."); } // Personnalise les alertes de cooldown @CommandEventHandler public void onCooldown(CommandCooldownEvent event) { event.setCustomMessage("⏳ Patientez " + event.getRemainingCooldown().toSeconds() + "s avant de réutiliser cette commande."); } } ``` --- ## 📂 Organisation des Packages ``` fr.luc.bettermccommands/ ├── BetterMcCommands.java (Manager principal & Façade d'accès) ├── api/ (Interfaces et contrats publics) │ ├── Command.java (Commande racine) │ ├── CommandNode.java (Nœud d'arborescence) │ ├── CommandContext.java (Contexte d'exécution et réponses MiniMessage) │ ├── CommandExecutor.java (Action métier exécutable) │ ├── CommandSenderType.java (ALL, PLAYER_ONLY, CONSOLE_ONLY) │ ├── CommandResult.java (SUCCESS, FAILED, CANCELLED, COOLDOWN, etc.) │ └── suggestion/ (Suggestions de complétion) ├── builder/ (DSL Fluent Builder) │ ├── AbstractCommandBuilder.java │ ├── CommandBuilder.java │ └── SubCommandBuilder.java ├── argument/ (Moteur d'arguments typés) │ ├── CommandArgument.java │ ├── ArgumentType.java │ ├── Arguments.java (Factory d'arguments) │ └── type/ (String, Integer, Player, Duration, Enum, etc.) ├── validation/ (Moteur de validation de commande et d'argument) │ ├── CommandValidator.java │ ├── ArgumentValidator.java │ └── ValidationResult.java ├── confirmation/ (Système de confirmation / Double validation) │ ├── ConfirmationManager.java │ └── PendingConfirmation.java ├── repeat/ (Tâches répétées & cadence de commandes) │ ├── CommandRepeater.java │ └── CommandRepeatProgress.java ├── event/ (Bus d'événements & Lifecycle) │ ├── CommandEvent.java │ ├── CancellableCommandEvent.java │ ├── CommandPreExecuteEvent.java │ ├── CommandPostExecuteEvent.java │ ├── CommandConfirmationRequiredEvent.java │ ├── CommandPermissionDeniedEvent.java │ ├── CommandSyntaxErrorEvent.java │ ├── CommandCooldownEvent.java │ ├── CommandTabCompleteEvent.java │ ├── CommandEventManager.java │ └── annotation/ │ └── CommandEventHandler.java ├── cooldown/ (Gestionnaire de Cooldowns par joueur) │ └── CooldownManager.java ├── platform/ (Ponts plateformes & Injection CommandMap) │ ├── CommandDispatcher.java │ └── paper/ │ ├── PaperCommandMapInjector.java │ └── PaperCommandWrapper.java └── demo/ (Plugin de démonstration complet) ├── DemoPlugin.java └── commands/ ├── DemoBaseCommand.java └── DemoListener.java ``` --- ## 📄 Licence Ce projet est sous licence [MIT](LICENSE).