-
Notifications
You must be signed in to change notification settings - Fork 0
Subcommand Groups
The @Subcommand annotation solves a common pain point: when a command has multiple aliases (e.g. party, p, team, t), every subcommand method would normally have to repeat all of those root names in its own @Command(names = {...}) array. Changing the root name later means updating every method by hand.
@Subcommand is placed on a class and declares the root names once. Every @Command-annotated method inside that class automatically has those roots prepended to its own names — the framework synthesizes the full compound names for you at registration time.
This works identically on Bukkit, BungeeCord, and Velocity.
| Property | Type | Description |
|---|---|---|
names |
String[] |
One or more root command aliases to prepend to every method's names. |
Without @Subcommand, registering /party invite, /p invite, /team invite, /t invite — and all their siblings — looks like this:
// Before: root names are repeated on every single method
public class PartyCommands {
@Command(names = {"party invite", "p invite", "team invite", "t invite"}, playerOnly = true)
public void invite(Player sender, @Param(name = "player") Player target) { ... }
@Command(names = {"party leave", "p leave", "team leave", "t leave"}, playerOnly = true)
public void leave(Player sender) { ... }
@Command(names = {"party disband", "p disband", "team disband", "t disband"}, playerOnly = true, permission = "myplugin.party.disband")
public void disband(Player sender) { ... }
}With @Subcommand, the root names are defined once on the class:
// After: root names declared once, methods only specify the subcommand name
@Subcommand(names = {"party", "p", "team", "t"})
public class PartyCommands {
@Command(names = {"invite"}, playerOnly = true)
public void invite(Player sender, @Param(name = "player") Player target) {
// Registered as: /party invite, /p invite, /team invite, /t invite
sender.sendMessage("Invited " + target.getName() + " to your party.");
}
@Command(names = {"leave"}, playerOnly = true)
public void leave(Player sender) {
// Registered as: /party leave, /p leave, /team leave, /t leave
sender.sendMessage("You left the party.");
}
@Command(names = {"disband"}, playerOnly = true, permission = "myplugin.party.disband")
public void disband(Player sender) {
// Registered as: /party disband, /p disband, /team disband, /t disband
sender.sendMessage("Party disbanded.");
}
}Renaming party → clan now requires changing one line.
For a class annotated @Subcommand(names = {"party", "p"}) with a method annotated @Command(names = {"invite", "inv"}), the framework registers all combinations:
| Root | Method name | Registered as |
|---|---|---|
| party | invite | /party invite |
| party | inv | /party inv |
| p | invite | /p invite |
| p | inv | /p inv |
@Help works exactly as normal — just use the root name as the prefix. It will catch /party (or any alias) when no subcommand matches:
@Subcommand(names = {"party", "p", "team", "t"})
public class PartyCommands {
@Command(names = {"invite"}, playerOnly = true)
public void invite(Player sender, @Param(name = "player") Player target) { ... }
@Command(names = {"leave"}, playerOnly = true)
public void leave(Player sender) { ... }
// Shown when /party, /p, /team or /t is run with no recognizable subcommand.
@Help(names = {"party", "p", "team", "t"})
public void help(CommandSender sender) {
sender.sendMessage("/party invite <player> — Invite a player to your party.");
sender.sendMessage("/party leave — Leave your current party.");
}
}Classes without @Subcommand continue to work exactly as before — @Command(names = {...}) with full compound names is still fully supported and backward-compatible.
// Still works — no changes needed to existing code
public class FactionCommands {
@Command(names = {"f create", "faction create"}, permission = "myplugin.faction.create", playerOnly = true)
public void create(Player sender, @Param(name = "name") String name) { ... }
}