From 8509cca7ceeaf55ba3901306e0726bc369ff36cd Mon Sep 17 00:00:00 2001 From: GeminiAntigravityCLI Date: Thu, 20 Aug 2026 17:37:02 +0200 Subject: [PATCH] =?UTF-8?q?Mise=20=C3=A0=20jour=20compl=C3=A8te=20de=20la?= =?UTF-8?q?=20documentation=20README.md=20avec=20diagrammes=20et=20ajout?= =?UTF-8?q?=20de=20la=20licence=20MIT?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LICENSE | 21 ++++ README.md | 332 ++++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 271 insertions(+), 82 deletions(-) create mode 100644 LICENSE 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`. +[![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 - -- **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).