Commands
Create custom commands for your Hytale plugin using the server command system found in the decompiled sources.
Command Registration
Commands extend AbstractCommand (most commonly CommandBase for sync commands) and are registered in your plugin’s setup() method.
public class PingCommand extends CommandBase {
public PingCommand() {
// Second parameter is a description/translation key.
super("ping", "myplugin.commands.ping.desc");
}
@Override
protected void executeSync(@Nonnull CommandContext context) {
context.sendMessage(Message.raw("Pong!"));
}
}
@Override
protected void setup() {
getCommandRegistry().registerCommand(new PingCommand());
}getCommandRegistry().registerCommand(...) wires the command owner to your plugin. Use CommandManager.get().registerSystemCommand(...) only for system-level commands.
Arguments
Commands define arguments with withRequiredArg, withOptionalArg, withDefaultArg, and withFlagArg. Argument values are read from CommandContext.
public class EchoCommand extends CommandBase {
private final RequiredArg<String> textArg =
this.withRequiredArg("text", "myplugin.commands.echo.text", ArgTypes.STRING);
private final DefaultArg<Integer> timesArg =
this.withDefaultArg("times", "myplugin.commands.echo.times", ArgTypes.INTEGER, 1, "1");
public EchoCommand() {
super("echo", "myplugin.commands.echo.desc");
}
@Override
protected void executeSync(@Nonnull CommandContext context) {
String text = textArg.get(context);
int times = timesArg.get(context);
for (int i = 0; i < times; i++) {
context.sendMessage(Message.raw(text));
}
}
}Use ArgTypes to pick the built-in argument parsers (strings, numbers, players, worlds, assets, and more).
Sender Handling
CommandContext provides the sender and helper methods for sender checks.
if (!context.isPlayer()) {
context.sendMessage(Message.raw("Players only."));
return;
}
Player player = context.senderAs(Player.class);Sub-Commands
Use AbstractCommandCollection for subcommands.
public class WarpCommand extends AbstractCommandCollection {
public WarpCommand() {
super("warp", "myplugin.commands.warp.desc");
this.addSubCommand(new WarpSetCommand());
this.addSubCommand(new WarpGoCommand());
}
}
public class WarpSetCommand extends CommandBase {
public WarpSetCommand() {
super("set", "myplugin.commands.warp.set.desc");
}
@Override
protected void executeSync(@Nonnull CommandContext context) {
context.sendMessage(Message.raw("Warp set."));
}
}Async Commands
For long-running work, extend AbstractAsyncCommand and return a CompletableFuture.
public class ExportCommand extends AbstractAsyncCommand {
public ExportCommand() {
super("export", "myplugin.commands.export.desc");
}
@Override
protected CompletableFuture<Void> executeAsync(@Nonnull CommandContext context) {
return runAsync(context, () -> {
// Heavy work (file I/O, DB, etc.)
context.sendMessage(Message.raw("Export complete."));
}, MyPlugin.getExecutor());
}
}Built-in Commands
System commands are registered in CommandManager.registerCommands() and by builtin plugins via registerSystemCommand(...). Refer to the decompiled sources for the authoritative list in your build.
Common Issues
| Issue | Solution |
|---|---|
| Command not appearing in help | Ensure setup() registers the command with getCommandRegistry() |
SenderTypeException | Guard with context.isPlayer() or catch the exception |
| Optional arg is null | Use withDefaultArg(...) or check for null |
| Suggestions not shown | Use Argument.suggest(...) or a supported ArgTypes parser |
Next Steps
- Permissions - Access control patterns
- Configuration - Store command settings
- Complete Examples - Full plugin examples with commands