414 lines
11 KiB
Markdown
414 lines
11 KiB
Markdown
# 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
|