Skip to content

Subcommand Groups

mario edited this page Mar 10, 2026 · 1 revision

@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.


@Subcommand Annotation Reference

Property Type Description
names String[] One or more root command aliases to prepend to every method's names.

Example

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 partyclan now requires changing one line.


How names are resolved

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

Combining with @Help

@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.");
    }
}

Without @Subcommand (still supported)

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) { ... }
}

Clone this wiki locally