--- title: Subcommands type: docs weight: 3 --- Subcommands allow you to create hierarchical command structures, organizing related functionality under a single parent command. ## Creating Subcommands ### Basic Structure ```java public class AdminCommand extends AbstractCommand { public AdminCommand() { super("admin", "Administration commands"); // Add subcommands addSubCommand(new KickSubCommand()); addSubCommand(new BanSubCommand()); addSubCommand(new MuteSubCommand()); } @Override protected CompletableFuture execute(CommandContext context) { // This runs when no subcommand is specified context.sender().sendMessage(Message.raw("Usage: /admin ")); return null; } } ``` ### Subcommand Implementation ```java public class KickSubCommand extends AbstractCommand { private final RequiredArg playerArg; private final OptionalArg reasonArg; public KickSubCommand() { super("kick", "Kick a player from the server"); playerArg = withRequiredArg("player", "Player to kick", ArgTypes.PLAYER_REF); reasonArg = withOptionalArg("reason", "Kick reason", ArgTypes.STRING); } @Override protected CompletableFuture execute(CommandContext context) { PlayerRef target = context.get(playerArg); String reason = context.get(reasonArg); // Check if player is online via ECS reference Ref ref = target.getReference(); if (ref != null && ref.isValid()) { // Disconnect via PacketHandler String kickReason = reason != null ? reason : "Kicked by admin"; target.getPacketHandler().disconnect(kickReason); context.sendMessage(Message.raw("Kicked " + target.getUsername())); } return null; } } ``` Usage: `/admin kick player123 --reason "Breaking rules"` ## Command Collections For commands that only contain subcommands (no direct execution), extend `AbstractCommandCollection`: ```java public class ManageCommand extends AbstractCommandCollection { public ManageCommand() { super("manage", "Management commands"); addSubCommand(new ManageUsersCommand()); addSubCommand(new ManageWorldsCommand()); addSubCommand(new ManagePluginsCommand()); } } ``` With `AbstractCommandCollection`, running `/manage` without a subcommand will show available subcommands automatically. ## Nested Subcommands Subcommands can have their own subcommands: ```java public class ManageUsersCommand extends AbstractCommand { public ManageUsersCommand() { super("users", "User management"); addSubCommand(new ListUsersCommand()); // /manage users list addSubCommand(new AddUserCommand()); // /manage users add addSubCommand(new RemoveUserCommand()); // /manage users remove } @Override protected CompletableFuture execute(CommandContext context) { // Show usage for /manage users return null; } } ``` ## Subcommand Aliases Subcommands can have aliases just like regular commands: ```java public class TeleportCommand extends AbstractCommand { public TeleportCommand() { super("teleport", "Teleport commands"); addAliases("tp"); addSubCommand(new TeleportHereCommand()); addSubCommand(new TeleportAllCommand()); } } public class TeleportHereCommand extends AbstractCommand { public TeleportHereCommand() { super("here", "Teleport player to you"); addAliases("h", "tome"); } // ... } ``` Now players can use: - `/teleport here player1` - `/tp here player1` - `/tp h player1` - `/tp tome player1` ## Command Variants Variants allow the same command to accept different argument patterns: ```java public class TpCommand extends AbstractCommand { private final RequiredArg targetArg; public TpCommand() { super("tp", "Teleport command"); // Main variant: /tp targetArg = withRequiredArg("target", "Player to teleport to", ArgTypes.PLAYER_REF); // Add variant: /tp addUsageVariant(new TpToPlayerVariant()); // Add variant: /tp addUsageVariant(new TpToPositionVariant()); } @Override protected CompletableFuture execute(CommandContext context) { // Teleport sender to target player PlayerRef target = context.get(targetArg); // ... return null; } } ``` ### Variant Implementation ```java public class TpToPlayerVariant extends AbstractCommand { private final RequiredArg playerArg; private final RequiredArg destinationArg; public TpToPlayerVariant() { // No name for variants - use description only super("Teleport one player to another"); playerArg = withRequiredArg("player", "Player to teleport", ArgTypes.PLAYER_REF); destinationArg = withRequiredArg("destination", "Destination player", ArgTypes.PLAYER_REF); } @Override protected CompletableFuture execute(CommandContext context) { PlayerRef player = context.get(playerArg); PlayerRef destination = context.get(destinationArg); // Teleport player to destination return null; } } ``` {{< callout type="info" >}} Variants are distinguished by the number of required parameters. Each variant must have a different number of required arguments. {{< /callout >}} ## Permission Inheritance Subcommand permissions are automatically built from the parent: ``` /admin -> myplugin.command.admin /admin kick -> myplugin.command.admin.kick /admin ban -> myplugin.command.admin.ban ``` You can also set custom permissions: ```java public KickSubCommand() { super("kick", "Kick a player"); requirePermission("myplugin.admin.kick"); } ```