# Command System The Hytale command system allows plugins to register custom commands that can be executed by players and the console. ## Overview Commands are defined by extending `AbstractCommand` and registered through the plugin's `CommandRegistry`. The system supports: - Required and optional arguments - Subcommands - Command variants (alternative syntax) - Automatic permission generation - Tab completion - Flag arguments ## Creating a Basic Command ```java public class HelloCommand extends AbstractCommand { public HelloCommand() { super("hello", "Says hello to a player"); // Add command aliases addAliases("hi", "greet"); // Require permission (auto-generated if not specified) requirePermission("myplugin.command.hello"); // Add a required argument withRequiredArg("player", "The player to greet"); } @Override public void execute(CommandContext context, Arguments args) { String playerName = args.getString("player"); context.sendMessage("Hello, " + playerName + "!"); } } ``` ## Registering Commands Register commands in your plugin's `setup()` method: ```java @Override protected void setup() { getCommandRegistry().registerCommand(new HelloCommand()); getCommandRegistry().registerCommand(new TeleportCommand()); } ``` ## Command Arguments ### Required Arguments Required arguments must be provided by the user: ```java public MyCommand() { super("mycommand", "Description"); // Single required argument withRequiredArg("target", "The target player"); // Multiple required arguments withRequiredArg("x", "X coordinate"); withRequiredArg("y", "Y coordinate"); withRequiredArg("z", "Z coordinate"); } @Override public void execute(CommandContext context, Arguments args) { String target = args.getString("target"); int x = args.getInt("x"); int y = args.getInt("y"); int z = args.getInt("z"); } ``` ### Optional Arguments Optional arguments have default values: ```java public MyCommand() { super("mycommand", "Description"); withRequiredArg("player", "Target player"); withOptionalArg("message", "Optional message"); } @Override public void execute(CommandContext context, Arguments args) { String player = args.getString("player"); String message = args.getStringOrDefault("message", "Default message"); } ``` ### Default Arguments Arguments with default values that are filled in if not provided: ```java public MyCommand() { super("mycommand", "Description"); withDefaultArg("count", "Number of items", "1"); } ``` ### Flag Arguments Boolean flags that can be toggled: ```java public MyCommand() { super("mycommand", "Description"); withFlagArg("silent", "Execute silently"); withFlagArg("force", "Force execution"); } @Override public void execute(CommandContext context, Arguments args) { boolean silent = args.hasFlag("silent"); boolean force = args.hasFlag("force"); } ``` Usage: `/mycommand --silent --force` ### List Arguments Arguments that accept multiple values: ```java public MyCommand() { super("mycommand", "Description"); withListRequiredArg("players", "List of players"); } @Override public void execute(CommandContext context, Arguments args) { List players = args.getStringList("players"); } ``` Usage: `/mycommand player1 player2 player3` ## Subcommands Create hierarchical command structures: ```java public class AdminCommand extends AbstractCommand { public AdminCommand() { super("admin", "Administration commands"); // Add subcommands addSubCommand(new AdminKickCommand()); addSubCommand(new AdminBanCommand()); addSubCommand(new AdminMuteCommand()); } @Override public void execute(CommandContext context, Arguments args) { // Called when no subcommand is specified context.sendMessage("Usage: /admin "); } } public class AdminKickCommand extends AbstractCommand { public AdminKickCommand() { super("kick", "Kick a player"); withRequiredArg("player", "Player to kick"); withOptionalArg("reason", "Kick reason"); } @Override public void execute(CommandContext context, Arguments args) { String player = args.getString("player"); String reason = args.getStringOrDefault("reason", "No reason specified"); // Kick logic } } ``` Usage: `/admin kick PlayerName "Breaking rules"` ## Command Variants Define alternative syntax for the same command: ```java public class TeleportCommand extends AbstractCommand { public TeleportCommand() { super("teleport", "Teleport to a location"); addAliases("tp"); // Variant 1: teleport to player withRequiredArg("target", "Target player"); // Variant 2: teleport to coordinates addUsageVariant(new TeleportCoordsVariant()); } @Override public void execute(CommandContext context, Arguments args) { String target = args.getString("target"); // Teleport to player } } public class TeleportCoordsVariant extends AbstractCommand { public TeleportCoordsVariant() { super("teleport", "Teleport to coordinates"); withRequiredArg("x", "X coordinate"); withRequiredArg("y", "Y coordinate"); withRequiredArg("z", "Z coordinate"); } @Override public void execute(CommandContext context, Arguments args) { double x = args.getDouble("x"); double y = args.getDouble("y"); double z = args.getDouble("z"); // Teleport to coordinates } } ``` ## Command Context The `CommandContext` provides information about the command execution: ```java @Override public void execute(CommandContext context, Arguments args) { // Get the command sender CommandSender sender = context.getSender(); // Check if sender is a player if (sender instanceof Player player) { // Player-specific logic World world = player.getWorld(); } // Send messages context.sendMessage("Success!"); context.sendError("Something went wrong!"); // Check permissions if (sender.hasPermission("myplugin.admin")) { // Admin logic } } ``` ## Permissions ### Automatic Permission Generation Commands automatically receive permissions based on the plugin's base permission: ``` {plugin.basePermission}.command.{commandName} ``` For example, if your plugin is `com.example:MyPlugin` and command is `spawn`: - Permission: `com.example.myplugin.command.spawn` ### Custom Permissions Override the automatic permission: ```java public MyCommand() { super("mycommand", "Description"); requirePermission("custom.permission.name"); } ``` ### Permission Checks in Execution ```java @Override public void execute(CommandContext context, Arguments args) { CommandSender sender = context.getSender(); if (!sender.hasPermission("myplugin.admin")) { context.sendError("You don't have permission!"); return; } // Continue execution } ``` ## Command Sender Types Commands can be executed by different sender types: | Sender Type | Description | |----------------|-------------------------------| | `Player` | In-game player | | `Console` | Server console | | `CommandBlock` | Command block (if applicable) | ```java @Override public void execute(CommandContext context, Arguments args) { CommandSender sender = context.getSender(); if (sender instanceof Player player) { // Player-specific logic } else if (sender instanceof Console) { // Console-specific logic } else { context.sendError("This command can only be run by players!"); } } ``` ## Complete Example ```java public class GameModeCommand extends AbstractCommand { public GameModeCommand() { super("gamemode", "Change your game mode"); addAliases("gm"); // Required: game mode withRequiredArg("mode", "Game mode (survival, creative, adventure, spectator)"); // Optional: target player (admin only) withOptionalArg("player", "Target player (requires admin permission)"); requirePermission("myplugin.command.gamemode"); } @Override public void execute(CommandContext context, Arguments args) { CommandSender sender = context.getSender(); String modeStr = args.getString("mode"); // Parse game mode GameMode mode = parseGameMode(modeStr); if (mode == null) { context.sendError("Invalid game mode: " + modeStr); return; } // Determine target player Player target; if (args.has("player")) { // Check admin permission for targeting others if (!sender.hasPermission("myplugin.command.gamemode.others")) { context.sendError("You don't have permission to change others' game mode!"); return; } String playerName = args.getString("player"); target = findPlayer(playerName); if (target == null) { context.sendError("Player not found: " + playerName); return; } } else { // Self - must be a player if (!(sender instanceof Player)) { context.sendError("Console must specify a target player!"); return; } target = (Player) sender; } // Apply game mode target.setGameMode(mode); context.sendMessage("Set " + target.getName() + "'s game mode to " + mode.name()); } private GameMode parseGameMode(String str) { return switch (str.toLowerCase()) { case "survival", "s", "0" -> GameMode.SURVIVAL; case "creative", "c", "1" -> GameMode.CREATIVE; case "adventure", "a", "2" -> GameMode.ADVENTURE; case "spectator", "sp", "3" -> GameMode.SPECTATOR; default -> null; }; } private Player findPlayer(String name) { return Universe.get().getPlayer(name); } } ``` ## Best Practices 1. **Provide clear descriptions** - Help users understand what the command does 2. **Use meaningful aliases** - Common abbreviations improve usability 3. **Validate input** - Check argument values before using them 4. **Handle errors gracefully** - Provide helpful error messages 5. **Check permissions appropriately** - Use automatic permissions or explicit checks 6. **Support tab completion** - Implement tab completion for better UX 7. **Document usage** - Include usage examples in the description 8. **Use subcommands for complex functionality** - Organize related commands together 9. **Consider console execution** - Check if the command can run from console