diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..16b160c
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Luc Rival / GeminiAntigravityCLI
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 3f702e1..3755c82 100644
--- a/README.md
+++ b/README.md
@@ -1,31 +1,86 @@
# ⚡ betterMcCommands
-Une bibliothèque Java moderne, fluide et événementielle pour créer et gérer des commandes Minecraft de façon hautement dynamique sans aucune configuration dans `plugin.yml`.
+[](https://www.oracle.com/java/)
+[](https://papermc.io/)
+[](https://docs.advntr.dev/)
+[](#)
+
+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`.
---
-## 🌟 Fonctionnalités Clés
-
-- **DSL Fluide & Déclaratif** : Création chainable de commandes racines et sous-commandes imbriquées avec `BetterMcCommands.builder("ma-commande")`.
-- **Zéro `plugin.yml`** : Injection et dé-enregistrement à chaud à l'exécution dans la `CommandMap` du serveur.
-- **Arguments Typés & Auto-complétion** :
- - Types primitifs : `string`, `word`, `greedyString`, `integer`, `decimal`, `bool`, `enumOf`.
- - Types Minecraft : `player`, `offlinePlayer`, `world`, `location`, `duration`.
- - Arguments optionnels, valeurs par défaut et suggestions (Tab-Complete) dynamiques.
-- **Moteur d'Événements & Lifecycle** :
- - Hooks par commande (`.onPreExecute()`, `.onPostExecute()`, `.onPermissionDenied()`, `.onSyntaxError()`, `.onCooldown()`, `.onTabComplete()`).
- - Bus d'événements global supportant les lambdas ou les classes d'écoute avec `@CommandEventHandler`.
- - Événements annulables (`PreExecuteEvent`) pour vérifier des conditions de jeu (cooldowns, état de combat, économie).
-- **Temps de recharge (Cooldowns) natifs** : Configuration directe avec permission de bypass optionnelle.
-- **Formatage Moderne** : Support direct des composants Kyori Adventure et des balises MiniMessage (``, ``, etc.).
+## 📑 Sommaire
+- [🌟 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. Temps de Recharge (Cooldowns)](#4-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)
---
-## 📦 Installation & Configuration Gradle
+## 🌟 Fonctionnalités
-Ajoutez la dépendance dans votre `build.gradle.kts` :
+- **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.
+- **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 des Arguments Typés"}
+ ArgParse -- Erreur format / manquant --> EvtSyntax["Dispatch CommandSyntaxErrorEvent"]
+
+ ArgParse -- Succès --> 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")
}
@@ -35,30 +90,42 @@ dependencies {
## 🚀 Guide de Démarrage Rapide
-### 1. Initialisation dans votre Plugin
+### 1. Initialisation dans la classe principale (`JavaPlugin`)
```java
-public class MyPlugin extends JavaPlugin {
+package fr.luc.monplugin;
- private BetterMcCommands commandsManager;
+import fr.luc.bettermccommands.BetterMcCommands;
+import org.bukkit.plugin.java.JavaPlugin;
+
+public class MonPlugin extends JavaPlugin {
+
+ private BetterMcCommands commands;
@Override
public void onEnable() {
- // Initialisation du gestionnaire pour ce plugin
- this.commandsManager = BetterMcCommands.create(this);
+ // 1. Initialisation du gestionnaire pour ce plugin
+ this.commands = BetterMcCommands.create(this);
- // Enregistrement de vos classes d'écouteurs d'événements
- this.commandsManager.registerListeners(new MyCommandEventsListener());
+ // 2. Enregistrement d'écouteurs d'événements globaux (optionnel)
+ this.commands.registerListeners(new MonEcouteurDeCommandes());
- // Enregistrement d'une commande
- DemoBaseCommand.create().register();
+ // 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 (commandsManager != null) {
- commandsManager.unregisterAll();
+ if (commands != null) {
+ commands.unregisterAll();
}
}
}
@@ -68,34 +135,34 @@ public class MyPlugin extends JavaPlugin {
## 💡 Exemples de Commandes
-### Déclaration d'une commande racine avec sous-commandes et arguments typés
+### 1. Commande de Démonstration Complète (`/commande-demo`)
```java
BetterMcCommands.builder("commande-demo")
- .description("Commande de démonstration")
+ .description("Commande principale de démonstration")
.aliases("demo", "cdemo")
.permission("bettermc.demo")
-
- // Événement exécuté avant la commande (annulable)
+
+ // Hook local avant exécution (annulable)
.onPreExecute(event -> {
if (event.isPlayer() && isInCombat(event.getPlayer())) {
event.setCancelled(true);
- event.reply("Impossible d'exécuter cette commande en combat !");
+ event.reply("Vous ne pouvez pas exécuter cette commande en combat !");
}
})
- // Exécution de la commande racine : /commande-demo
+ // Exécution de la commande racine
.executes(context -> {
- context.reply("=== Démo betterMcCommands ===");
+ context.reply("=== betterMcCommands ===");
context.reply("Utilisez /demo give pour donner des ressources.");
})
- // Sous-commande : /demo give [quantite]
+ // Sous-commande avec arguments typés et valeur par défaut
.subcommand(BetterMcCommands.subBuilder("give")
- .description("Donne des ressources à un joueur")
+ .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))
+ .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");
@@ -104,53 +171,137 @@ BetterMcCommands.builder("commande-demo")
})
)
- // Sous-commande avec argument gourmand (greedy) : /demo broadcast
- .subcommand(BetterMcCommands.subBuilder("broadcast")
- .description("Diffuse une annonce")
- .argument(Arguments.greedyString("message"))
- .executes(context -> {
- String msg = context.getString("message");
- context.replySuccess("Diffusion : " + msg + "");
- })
- )
-
- // Sous-commande avec Cooldown : /demo kit (recharge de 60 secondes)
- .subcommand(BetterMcCommands.subBuilder("kit")
- .cooldown(Duration.ofSeconds(60))
- .cooldownBypass("bettermc.bypass.kit")
- .playerOnly()
- .executes(context -> {
- context.replySuccess("Kit reçu !");
- })
- )
-
.register();
```
---
-## 🎯 Système d'Événements & Lifecycle
+### 2. Sous-Commandes Imbriquées
-Vous pouvez intercepter les événements du cycle de vie des commandes soit directement sur le builder (`.onPreExecute(...)`, `.onPostExecute(...)`), soit dans une classe dédiée avec `@CommandEventHandler` :
+Créez des arbres hiérarchiques profonds de façon fluide :
```java
-public class MyCommandEventsListener {
+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();
+```
- @CommandEventHandler(priority = 10)
+---
+
+### 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. 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 |
+| `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("Commande demandée : /" + event.getNode().getFullName() + " par " + event.getSender().getName());
+ 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) {
- System.out.println("Commande exécutée en " + event.getExecutionDuration().toMillis() + "ms.");
+ 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é ! Permission requise : " + event.getRequiredPermission() + "");
+ 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.");
@@ -160,28 +311,28 @@ public class MyCommandEventsListener {
---
-## 📂 Architecture des Packages
+## 📂 Organisation des Packages
```
fr.luc.bettermccommands/
- ├── BetterMcCommands.java (Manager principal & Façade)
- ├── api/ (Interfaces et modèles publics)
- │ ├── Command.java
- │ ├── CommandNode.java
- │ ├── CommandContext.java
- │ ├── CommandExecutor.java
- │ ├── CommandSenderType.java
- │ ├── CommandResult.java
- │ └── suggestion/
- ├── builder/ (Fluent DSL Builder)
+ ├── 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/ (Arguments typés & Auto-complétion)
+ ├── argument/ (Moteur d'arguments typés)
│ ├── CommandArgument.java
│ ├── ArgumentType.java
- │ ├── Arguments.java
- │ └── type/
+ │ ├── Arguments.java (Factory d'arguments)
+ │ └── type/ (String, Integer, Player, Duration, Enum, etc.)
├── event/ (Bus d'événements & Lifecycle)
│ ├── CommandEvent.java
│ ├── CancellableCommandEvent.java
@@ -191,8 +342,25 @@ fr.luc.bettermccommands/
│ ├── CommandSyntaxErrorEvent.java
│ ├── CommandCooldownEvent.java
│ ├── CommandTabCompleteEvent.java
+ │ ├── CommandEventManager.java
│ └── annotation/
- ├── cooldown/ (Gestion des cooldowns par joueur)
- ├── platform/ (Injection runtime CommandMap Paper/Spigot)
- └── demo/ (Plugin de démonstration et exemples)
+ │ └── 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).