15 KiB
15 KiB
⚡ betterMcCommands
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
- 🌟 Fonctionnalités
- 🏗️ Architecture & Flux d'Exécution
- 📦 Installation & Configuration
- 🚀 Guide de Démarrage Rapide
- 💡 Exemples de Commandes
- 🧩 Catalogue des Arguments Typés
- 🎯 Système d'Événements & Cycle de Vie
- 📂 Organisation des Packages
🌟 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 laCommandMapdu 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
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)
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)
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)
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("<red>Vous ne pouvez pas exécuter cette commande en combat !</red>");
}
})
// Exécution de la commande racine
.executes(context -> {
context.reply("<gradient:#00d2ff:#3a7bd5><bold>=== betterMcCommands ===</bold></gradient>");
context.reply("<gray>Utilisez <yellow>/demo give</yellow> pour donner des ressources.</gray>");
})
// 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 <gold>" + quantite + "</gold> items à <aqua>" + target.getName() + "</aqua> !");
})
)
.register();
2. Sous-Commandes Imbriquées
Créez des arbres hiérarchiques profonds de façon fluide :
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) :
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)
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)
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("<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
public void onCooldown(CommandCooldownEvent event) {
event.setCustomMessage("<red>⏳ Patientez <gold>" + event.getRemainingCooldown().toSeconds() + "s</gold> avant de réutiliser cette commande.</red>");
}
}
📂 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.)
├── event/ (Bus d'événements & Lifecycle)
│ ├── CommandEvent.java
│ ├── CancellableCommandEvent.java
│ ├── CommandPreExecuteEvent.java
│ ├── CommandPostExecuteEvent.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.