Skip to main content

Overview

SimpleSubCommand represents a subcommand within a SimpleCommandGroup. For example, in /arena join, “join” would be a SimpleSubCommand belonging to the “arena” command group. SimpleSubCommand extends SimpleCommand, so it inherits all the convenience methods, checks, and messaging utilities.

Class hierarchy

org.bukkit.command.Command
  └── org.mineacademy.fo.command.SimpleCommand
        └── org.mineacademy.fo.command.SimpleSubCommand

Creating a subcommand

Constructors

SimpleSubCommand(String sublabel)
constructor
Creates a new subcommand for the main plugin command group.The main command group must be defined in your plugin’s main class.Separate sublabels with | or / to add aliases.Example: "join|j" creates /arena join that can also be called with /arena j.
SimpleSubCommand(SimpleCommandGroup parent, String sublabel)
constructor
Creates a new subcommand for a specific command group.

Basic example

public class ArenaJoinCommand extends SimpleSubCommand {
    
    public ArenaJoinCommand() {
        super("join|j"); // Can use /arena join or /arena j
        
        setDescription("Join an arena");
        setUsage("<arena_name>");
        setMinArguments(1);
    }
    
    @Override
    protected void onCommand() {
        checkConsole(); // Players only
        
        Player player = getPlayer();
        String arenaName = args[0];
        
        // Join arena logic...
        tellSuccess("Joined arena: " + arenaName);
    }
}

Core methods

onCommand()

onCommand()
abstract void
Required implementation. Executed when the subcommand is run.Access sender and args fields directly. Args do not include the sublabel.
@Override
protected void onCommand() {
    // For /arena create MyArena
    // args[0] = "MyArena"
    // The "create" sublabel is not in args
    
    checkConsole();
    String arenaName = args[0];
    
    // Create arena...
    tellSuccess("Created arena: " + arenaName);
}

showInHelp()

showInHelp()
boolean
Determines if this subcommand should appear in the help menu.Default: true
@Override
protected boolean showInHelp() {
    // Hide admin commands from normal help menu
    return sender.hasPermission("arena.admin");
}

Permissions

Subcommand permissions are automatically enhanced based on the parent command:

Default permission format

If this subcommand belongs to your plugin’s main command group:
{plugin}.command.{sublabel}
Example: For /chatcontrol mutechatcontrol.command.mute

Custom permissions

public ArenaAdminCommand() {
    super("admin");
    
    // Override default permission
    setPermission("arena.admin.manage");
}

Sublabels and aliases

getSublabels()

getSublabels()
String[]
Gets all registered sublabels (including aliases).

getSublabel()

getSublabel()
String
Gets the current sublabel used when the command was executed.This is updated dynamically to reflect which alias was used.
public class TeleportCommand extends SimpleSubCommand {
    
    public TeleportCommand() {
        super("teleport|tp|warp");
    }
    
    @Override
    protected void onCommand() {
        // getSublabel() returns "teleport", "tp", or "warp"
        // depending on what the player typed
        tell("You used: /arena " + getSublabel());
    }
}

Placeholder replacement

SimpleSubCommand adds the {sublabel} placeholder to all messages:

replacePlaceholders(String message)

replacePlaceholders(String message)
String
Replaces placeholders in messages.Available placeholders:
  • {sublabel} - The current sublabel
  • All placeholders from SimpleCommand
@Override
protected void onCommand() {
    tell("Usage: /{label} {sublabel} <player>");
    // Output: "Usage: /arena join <player>"
}

Inherited features

SimpleSubCommand inherits all features from SimpleCommand:
  • checkConsole() - Require player sender
  • checkPerm(String) - Check permissions
  • checkArgs(int, String) - Validate argument count
  • checkBoolean(boolean, String) - Assert conditions
  • checkNotNull(Object, String) - Null checks

Complete example

public class ArenaJoinCommand extends SimpleSubCommand {
    
    public ArenaJoinCommand() {
        super("join|j|enter");
        
        setDescription("Join an arena to start battling");
        setUsage("<arena_name> [team]");
        setMinArguments(1);
        setCooldown(3, TimeUnit.SECONDS);
    }
    
    @Override
    protected void onCommand() {
        checkConsole(); // Players only
        
        Player player = getPlayer();
        String arenaName = args[0];
        
        // Find arena
        Arena arena = ArenaManager.findArena(arenaName);
        checkNotNull(arena, "Arena '{0}' not found!".replace("{0}", arenaName));
        
        // Check if arena is full
        checkBoolean(!arena.isFull(), "Arena is full!");
        
        // Optional team parameter
        String team = args.length > 1 ? args[1] : null;
        
        if (team != null) {
            ArenaTeam arenaTeam = findEnum(ArenaTeam.class, team, 
                "Invalid team {enum}! Available: {available}");
            arena.addPlayer(player, arenaTeam);
        } else {
            arena.addPlayer(player);
        }
        
        tellSuccess("Joined arena: " + arena.getName());
        
        if (team != null)
            tellInfo("Team: " + team);
    }
    
    @Override
    protected List<String> tabComplete() {
        // Complete arena names
        if (args.length == 1)
            return completeLastWord(ArenaManager.getArenaNames());
        
        // Complete team names
        if (args.length == 2)
            return completeLastWord(ArenaTeam.values());
        
        return NO_COMPLETE;
    }
    
    @Override
    protected String[] getMultilineUsageMessage() {
        return new String[] {
            "&6Join an arena:",
            "&e  /{label} {sublabel} <arena> &7- Join any team",
            "&e  /{label} {sublabel} <arena> <team> &7- Join specific team",
            "",
            "&7Examples:",
            "&f  /{label} {sublabel} PvPArena",
            "&f  /{label} {sublabel} PvPArena red"
        };
    }
}

Advanced example with multiple aliases

public class ArenaTeleportCommand extends SimpleSubCommand {
    
    public ArenaTeleportCommand() {
        super("teleport|tp|warp|goto");
        
        setDescription("Teleport to an arena lobby");
        setUsage("<arena_name>");
        setPermission("arena.teleport");
        setMinArguments(1);
    }
    
    @Override
    protected void onCommand() {
        checkConsole();
        
        Player player = getPlayer();
        String arenaName = args[0];
        
        Arena arena = ArenaManager.findArena(arenaName);
        checkNotNull(arena, "Arena not found!");
        
        Location lobby = arena.getLobbyLocation();
        checkNotNull(lobby, "Arena has no lobby set!");
        
        player.teleport(lobby);
        
        // Use {sublabel} to show which alias they used
        tellSuccess("Teleported via /{label} {sublabel}");
    }
    
    @Override
    protected List<String> tabComplete() {
        if (args.length == 1)
            return completeLastWord(ArenaManager.getArenaNames());
        
        return NO_COMPLETE;
    }
    
    @Override
    protected boolean showInHelp() {
        // Only show to players with permission
        return hasPerm("arena.teleport");
    }
}

Registration

Subcommands are registered through their parent SimpleCommandGroup:
public class ArenaCommandGroup extends SimpleCommandGroup {
    
    public ArenaCommandGroup() {
        super("arena");
    }
    
    @Override
    protected void registerSubcommands() {
        // Register each subcommand
        registerSubcommand(new ArenaJoinCommand());
        registerSubcommand(new ArenaLeaveCommand());
        registerSubcommand(new ArenaListCommand());
    }
}
Subcommands cannot be registered directly - they must be registered through a SimpleCommandGroup.

Best practices

  • One subcommand per class
  • Group related subcommands in the same package
  • Use descriptive class names: ArenaJoinCommand, not JoinCmd
  • Make classes final when using auto-registration

See also

Build docs developers (and LLMs) love