Mise à jour complète de la documentation README.md avec diagrammes et ajout de la licence MIT

This commit is contained in:
2026-08-20 17:37:02 +02:00
parent 637aa7b3ec
commit 8509cca7ce
2 changed files with 271 additions and 82 deletions
+21
View File
@@ -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.
+249 -81
View File
@@ -1,31 +1,86 @@
# ⚡ betterMcCommands # ⚡ 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`. [![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`.
--- ---
## 🌟 Fonctionnalités Clés ## 📑 Sommaire
- [🌟 Fonctionnalités](#-fonctionnalités)
- **DSL Fluide & Déclaratif** : Création chainable de commandes racines et sous-commandes imbriquées avec `BetterMcCommands.builder("ma-commande")`. - [🏗️ Architecture & Flux d'Exécution](#-architecture--flux-dexécution)
- **Zéro `plugin.yml`** : Injection et dé-enregistrement à chaud à l'exécution dans la `CommandMap` du serveur. - [📦 Installation & Configuration](#-installation--configuration)
- **Arguments Typés & Auto-complétion** : - [🚀 Guide de Démarrage Rapide](#-guide-de-démarrage-rapide)
- Types primitifs : `string`, `word`, `greedyString`, `integer`, `decimal`, `bool`, `enumOf`. - [💡 Exemples de Commandes](#-exemples-de-commandes)
- Types Minecraft : `player`, `offlinePlayer`, `world`, `location`, `duration`. - [1. Commande de Démonstration Complète (`/commande-demo`)](#1-commande-de-démonstration-complète-commande-demo)
- Arguments optionnels, valeurs par défaut et suggestions (Tab-Complete) dynamiques. - [2. Sous-Commandes Imbriquées](#2-sous-commandes-imbriquées)
- **Moteur d'Événements & Lifecycle** : - [3. Arguments Gourmands (Greedy String)](#3-arguments-gourmands-greedy-string)
- Hooks par commande (`.onPreExecute()`, `.onPostExecute()`, `.onPermissionDenied()`, `.onSyntaxError()`, `.onCooldown()`, `.onTabComplete()`). - [4. Temps de Recharge (Cooldowns)](#4-temps-de-recharge-cooldowns)
- Bus d'événements global supportant les lambdas ou les classes d'écoute avec `@CommandEventHandler`. - [🧩 Catalogue des Arguments Typés](#-catalogue-des-arguments-typés)
- Événements annulables (`PreExecuteEvent`) pour vérifier des conditions de jeu (cooldowns, état de combat, économie). - [🎯 Système d'Événements & Cycle de Vie](#-système-dévénements--cycle-de-vie)
- **Temps de recharge (Cooldowns) natifs** : Configuration directe avec permission de bypass optionnelle. - [Liste des Événements Disponibles](#liste-des-événements-disponibles)
- **Formatage Moderne** : Support direct des composants Kyori Adventure et des balises MiniMessage (`<gradient>`, `<green>`, etc.). - [É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 (`<gradient>`), 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 ```kotlin
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
}
dependencies { dependencies {
implementation("fr.luc:betterMcCommands:1.0.0-SNAPSHOT") implementation("fr.luc:betterMcCommands:1.0.0-SNAPSHOT")
} }
@@ -35,30 +90,42 @@ dependencies {
## 🚀 Guide de Démarrage Rapide ## 🚀 Guide de Démarrage Rapide
### 1. Initialisation dans votre Plugin ### 1. Initialisation dans la classe principale (`JavaPlugin`)
```java ```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 @Override
public void onEnable() { public void onEnable() {
// Initialisation du gestionnaire pour ce plugin // 1. Initialisation du gestionnaire pour ce plugin
this.commandsManager = BetterMcCommands.create(this); this.commands = BetterMcCommands.create(this);
// Enregistrement de vos classes d'écouteurs d'événements // 2. Enregistrement d'écouteurs d'événements globaux (optionnel)
this.commandsManager.registerListeners(new MyCommandEventsListener()); this.commands.registerListeners(new MonEcouteurDeCommandes());
// Enregistrement d'une commande // 3. Enregistrement de vos commandes
DemoBaseCommand.create().register(); creerCommandes();
}
private void creerCommandes() {
BetterMcCommands.builder("ping")
.description("Répond pong !")
.executes(context -> context.replySuccess("Pong !"))
.register();
} }
@Override @Override
public void onDisable() { public void onDisable() {
// Désenregistrement à chaud et nettoyage propre // Désenregistrement à chaud et nettoyage propre
if (commandsManager != null) { if (commands != null) {
commandsManager.unregisterAll(); commands.unregisterAll();
} }
} }
} }
@@ -68,34 +135,34 @@ public class MyPlugin extends JavaPlugin {
## 💡 Exemples de Commandes ## 💡 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 ```java
BetterMcCommands.builder("commande-demo") BetterMcCommands.builder("commande-demo")
.description("Commande de démonstration") .description("Commande principale de démonstration")
.aliases("demo", "cdemo") .aliases("demo", "cdemo")
.permission("bettermc.demo") .permission("bettermc.demo")
// Événement exécuté avant la commande (annulable) // Hook local avant exécution (annulable)
.onPreExecute(event -> { .onPreExecute(event -> {
if (event.isPlayer() && isInCombat(event.getPlayer())) { if (event.isPlayer() && isInCombat(event.getPlayer())) {
event.setCancelled(true); event.setCancelled(true);
event.reply("<red>Impossible d'exécuter cette commande en combat !</red>"); event.reply("<red>Vous ne pouvez pas exécuter cette commande en combat !</red>");
} }
}) })
// Exécution de la commande racine : /commande-demo // Exécution de la commande racine
.executes(context -> { .executes(context -> {
context.reply("<gradient:#00d2ff:#3a7bd5><bold>=== Démo betterMcCommands ===</bold></gradient>"); context.reply("<gradient:#00d2ff:#3a7bd5><bold>=== betterMcCommands ===</bold></gradient>");
context.reply("<gray>Utilisez <yellow>/demo give</yellow> pour donner des ressources.</gray>"); context.reply("<gray>Utilisez <yellow>/demo give</yellow> pour donner des ressources.</gray>");
}) })
// Sous-commande : /demo give <cible> [quantite] // Sous-commande avec arguments typés et valeur par défaut
.subcommand(BetterMcCommands.subBuilder("give") .subcommand(BetterMcCommands.subBuilder("give")
.description("Donne des ressources à un joueur") .description("Donne des ressources à un joueur cible")
.permission("bettermc.demo.give") .permission("bettermc.demo.give")
.argument(Arguments.player("cible").description("Joueur recevant les items")) .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 -> { .executes(context -> {
Player target = context.getTargetPlayer("cible"); Player target = context.getTargetPlayer("cible");
int quantite = context.getInt("quantite"); int quantite = context.getInt("quantite");
@@ -104,53 +171,137 @@ BetterMcCommands.builder("commande-demo")
}) })
) )
// Sous-commande avec argument gourmand (greedy) : /demo broadcast <message...>
.subcommand(BetterMcCommands.subBuilder("broadcast")
.description("Diffuse une annonce")
.argument(Arguments.greedyString("message"))
.executes(context -> {
String msg = context.getString("message");
context.replySuccess("Diffusion : <yellow>" + msg + "</yellow>");
})
)
// 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(); .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 ```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 : <yellow>" + message + "</yellow>");
})
.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<T>` | Enum Java avec auto-complétion de toutes les constantes. |
| `Arguments.custom("cle", parseur)` | `T` | Type personnalisé implémentant `ArgumentType<T>`. |
---
## 🎯 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) { 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") @CommandEventHandler(command = "commande-demo")
public void onDemoPostExecute(CommandPostExecuteEvent event) { 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 @CommandEventHandler
public void onPermissionDenied(CommandPermissionDeniedEvent event) { public void onPermissionDenied(CommandPermissionDeniedEvent event) {
event.setCustomErrorMessage("<dark_red>Accès refusé ! Permission requise : " + event.getRequiredPermission() + "</dark_red>"); event.setCustomErrorMessage("<dark_red><bold>Accès Refusé</bold></dark_red> : Vous devez posséder la permission <gray>[" + event.getRequiredPermission() + "]</gray>.");
} }
// Personnalise les alertes de cooldown
@CommandEventHandler @CommandEventHandler
public void onCooldown(CommandCooldownEvent event) { public void onCooldown(CommandCooldownEvent event) {
event.setCustomMessage("<red>⏳ Patientez <gold>" + event.getRemainingCooldown().toSeconds() + "s</gold> avant de réutiliser cette commande.</red>"); event.setCustomMessage("<red>⏳ Patientez <gold>" + event.getRemainingCooldown().toSeconds() + "s</gold> avant de réutiliser cette commande.</red>");
@@ -160,28 +311,28 @@ public class MyCommandEventsListener {
--- ---
## 📂 Architecture des Packages ## 📂 Organisation des Packages
``` ```
fr.luc.bettermccommands/ fr.luc.bettermccommands/
├── BetterMcCommands.java (Manager principal & Façade) ├── BetterMcCommands.java (Manager principal & Façade d'accès)
├── api/ (Interfaces et modèles publics) ├── api/ (Interfaces et contrats publics)
│ ├── Command.java │ ├── Command.java (Commande racine)
│ ├── CommandNode.java │ ├── CommandNode.java (Nœud d'arborescence)
│ ├── CommandContext.java │ ├── CommandContext.java (Contexte d'exécution et réponses MiniMessage)
│ ├── CommandExecutor.java │ ├── CommandExecutor.java (Action métier exécutable)
│ ├── CommandSenderType.java │ ├── CommandSenderType.java (ALL, PLAYER_ONLY, CONSOLE_ONLY)
│ ├── CommandResult.java │ ├── CommandResult.java (SUCCESS, FAILED, CANCELLED, COOLDOWN, etc.)
│ └── suggestion/ │ └── suggestion/ (Suggestions de complétion)
├── builder/ (Fluent DSL Builder) ├── builder/ (DSL Fluent Builder)
│ ├── AbstractCommandBuilder.java │ ├── AbstractCommandBuilder.java
│ ├── CommandBuilder.java │ ├── CommandBuilder.java
│ └── SubCommandBuilder.java │ └── SubCommandBuilder.java
├── argument/ (Arguments typés & Auto-complétion) ├── argument/ (Moteur d'arguments typés)
│ ├── CommandArgument.java │ ├── CommandArgument.java
│ ├── ArgumentType.java │ ├── ArgumentType.java
│ ├── Arguments.java │ ├── Arguments.java (Factory d'arguments)
│ └── type/ │ └── type/ (String, Integer, Player, Duration, Enum, etc.)
├── event/ (Bus d'événements & Lifecycle) ├── event/ (Bus d'événements & Lifecycle)
│ ├── CommandEvent.java │ ├── CommandEvent.java
│ ├── CancellableCommandEvent.java │ ├── CancellableCommandEvent.java
@@ -191,8 +342,25 @@ fr.luc.bettermccommands/
│ ├── CommandSyntaxErrorEvent.java │ ├── CommandSyntaxErrorEvent.java
│ ├── CommandCooldownEvent.java │ ├── CommandCooldownEvent.java
│ ├── CommandTabCompleteEvent.java │ ├── CommandTabCompleteEvent.java
│ ├── CommandEventManager.java
│ └── annotation/ │ └── annotation/
├── cooldown/ (Gestion des cooldowns par joueur) │ └── CommandEventHandler.java
├── platform/ (Injection runtime CommandMap Paper/Spigot) ├── cooldown/ (Gestionnaire de Cooldowns par joueur)
└── demo/ (Plugin de démonstration et exemples) │ └── 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).