11 KiB
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
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:
@Override
protected void setup() {
getCommandRegistry().registerCommand(new HelloCommand());
getCommandRegistry().registerCommand(new TeleportCommand());
}
Command Arguments
Required Arguments
Required arguments must be provided by the user:
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:
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:
public MyCommand() {
super("mycommand", "Description");
withDefaultArg("count", "Number of items", "1");
}
Flag Arguments
Boolean flags that can be toggled:
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:
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:
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:
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:
@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:
public MyCommand() {
super("mycommand", "Description");
requirePermission("custom.permission.name");
}
Permission Checks in Execution
@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) |
@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
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
- Provide clear descriptions - Help users understand what the command does
- Use meaningful aliases - Common abbreviations improve usability
- Validate input - Check argument values before using them
- Handle errors gracefully - Provide helpful error messages
- Check permissions appropriately - Use automatic permissions or explicit checks
- Support tab completion - Implement tab completion for better UX
- Document usage - Include usage examples in the description
- Use subcommands for complex functionality - Organize related commands together
- Consider console execution - Check if the command can run from console