# 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<String> 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 <kick|ban|mute>");
    }
}

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
