Sub-commands
Nesting commands, how aliases and requirements behave down the tree, and where sub-commands collide with arguments.
/guild create <name> rather than a separate /guildcreate. A sub-command is a
literal token that branches the command tree.
The type is the same one
BaseCommand<T> extends SubCommand<T>. Everything on this page — arguments,
flags, requirements, nesting — works identically on both. The only things
BaseCommand adds are the two properties that exist because it is what the
server registers:
Only on BaseCommand | Why |
|---|---|
setDescription(String) | Shown in /help; a nested node has no help entry |
setReloadable(boolean) | unregisterReloadableCommands() operates on roots |
So a sub-command is declared exactly like a command, minus those two:
public class GuildCreateSubCommand extends SubCommand<MyPlugin> {
public GuildCreateSubCommand(MyPlugin plugin) {
super(plugin, "create", "new"); // name + aliases, as usual
this.setPermission("myplugin.guild.create");
this.addRequiredArgument("name", StringArgumentType.word());
}
@Override
protected CommandResultType perform(CommandDispatch<MyPlugin> dispatch) {
String name = dispatch.getArgument("name", String.class);
...
return CommandResultType.SUCCESS;
}
}Attach it to the parent:
public class GuildCommand extends BaseCommand<MyPlugin> {
public GuildCommand(MyPlugin plugin) {
super(plugin, "guild", "g");
this.setDescription("Guild management");
this.addSubCommand(new GuildCreateSubCommand(plugin)); // /guild create <name>
this.addSubCommand(new GuildInviteSubCommand(plugin)); // /guild invite <player>
}
@Override
protected CommandResultType perform(CommandDispatch<MyPlugin> dispatch) {
// Runs for bare `/guild`. The parent stays executable on its own.
dispatch.getSender().sendMessage("/guild create | invite");
return CommandResultType.SUCCESS;
}
}Only the root is tracked with the manager. Sub-commands come along with it.
The parent stays executable
Adding sub-commands does not consume the parent. perform on the root still
runs for /guild with nothing after it, which is the natural place for a usage
message.
Nesting is unlimited
addSubCommand takes a SubCommand<T>, and SubCommand has addSubCommand,
so /guild admin purge <name> is three levels of the same construction. Nothing
in the library caps the depth.
Without subclassing
SubCommand.builder(...) mirrors BaseCommand.builder(...):
SubCommand<MyPlugin> info = SubCommand.builder(this, "info")
.alias("i")
.permission("myplugin.guild.info")
.executes(dispatch -> {
dispatch.getSender().sendMessage("Guild info");
return CommandResultType.SUCCESS;
})
.build();Mixing styles is fine — a subclassed parent can take a built sub-command and the other way round.
Aliases: one level only
A sub-command's aliases are built only under the command's real name, not under the parent's aliases.
Given /guild with alias /g, and a create sub-command with alias new:
| Input | Works |
|---|---|
/guild create | yes |
/guild new | yes |
/g create | yes |
/g new | no |
The alias tree is deliberately not a full cross-product — every combination is
real Brigadier nodes, sent to every client. If /g new matters, give the
sub-command a name people will type and skip the alias.
Requirements apply to the whole branch
Each node carries its own requirement predicate, and Brigadier checks every node along a path. The effect is that a parent's requirement gates everything beneath it, and a child's requirement stacks on top:
// on GuildCommand
this.setPermission("myplugin.guild");
// on GuildCreateSubCommand
this.setPermission("myplugin.guild.create");/guild create now needs both nodes. There is no inheritance to configure
and no way to loosen a parent's requirement from a child — see
Requirements & permissions.
Sub-commands and arguments compete
A literal and an argument in the same position are ambiguous, and Brigadier resolves literals first:
this.addSubCommand(new GuildListSubCommand(plugin)); // literal `list`
this.addOptionalArgument("name", StringArgumentType.word());/guild list runs the sub-command; the argument never sees the value list.
Everything else still reaches the argument. This is only a problem when a legal
argument value happens to equal a sub-command name — the same class of trap as
a value that looks like a flag.
Flags come after the whole path
Flags declared on a node attach to that node's executable positions, and flag nodes branch only into other flag nodes:
/guild create MyGuild --silent parses
/guild --silent create MyGuild does notFlags are attached to the executable positions of the node that declares them,
so they do not leak down the tree: a flag on guild is not available on
guild create. Declare each flag on the node that reads it, and keep the count
down — the flag tree cost is
paid once per executable position of that node, and every optional argument adds
another such position.
Next
- Requirements & permissions — gating a branch
- Arguments — required, optional, and reading them back
Last updated on