Update script to write to vendor/hytale-server
This commit is contained in:
413
docs/04-command-system.md
Normal file
413
docs/04-command-system.md
Normal file
@@ -0,0 +1,413 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user