hytale-commands
Reference guide for building and registering custom commands in Hytale plugins.
Install
mkdir -p .claude/skills/hytale-commands && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/10498" && unzip -o skill.zip -d .claude/skills/hytale-commands && rm skill.zipInstalls to .claude/skills/hytale-commands
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Documents Hytale's command system for creating custom commands in plugins. Covers AbstractAsyncCommand, AbstractPlayerCommand, AbstractTargetPlayerCommand, AbstractTargetEntityCommand, AbstractCommandCollection, arguments (RequiredArg, OptionalArg, DefaultArg, FlagArg), ArgTypes, argument validators, custom validators, permissions, command variants, aliases, subcommands, and registration. Use when creating commands, adding arguments, validating input, requiring permissions, building command trees, or registering commands. Triggers - command, custom command, AbstractPlayerCommand, AbstractAsyncCommand, AbstractTargetPlayerCommand, AbstractTargetEntityCommand, AbstractCommandCollection, CommandContext, RequiredArg, OptionalArg, DefaultArg, FlagArg, ArgTypes, Validator, requirePermission, addUsageVariant, addAliases, addSubCommand, registerCommand, CommandRegistry.Key capabilities
- →Create custom commands
- →Add command arguments
- →Validate input
- →Register commands
How it works
Provides a framework for extending Hytale's command system with custom logic, arguments, and permissions.
Inputs & outputs
When to use hytale-commands
- →Create a new server command
- →Add arguments to a command
- →Register a command in a plugin
About this skill
Hytale Command System
Comprehensive reference for creating custom commands in Hytale plugins, including command types, arguments, validators, permissions, variants, subcommands, registration, and the full ArgTypes reference.
Source: https://hytalemodding.dev/en/docs/guides/plugin/creating-commands, https://hytalemodding.dev/en/docs/server/argtypes Related skills: For permissions in detail, see
hytale-permissions. For player stats used in commands, seehytale-player-stats.
Quick Reference
| Task | Approach |
|---|---|
| Basic async command | Extend AbstractAsyncCommand, override executeAsync() |
| Player-bound command | Extend AbstractPlayerCommand, override execute() |
| Target another player | Extend AbstractTargetPlayerCommand (adds --player arg) |
| Target looked-at entity | Extend AbstractTargetEntityCommand (uses raycast) |
| Add required argument | this.withRequiredArg("name", "desc", ArgTypes.STRING) |
| Add optional argument | this.withOptionalArg("name", "desc", ArgTypes.STRING) |
| Add default argument | this.withDefaultArg("name", "desc", ArgTypes.FLOAT, 100f, "default desc") |
| Add flag argument | this.withFlagArg("name", "desc") |
| Browse all supported argument types | Use the ArgTypes server reference for the authoritative list |
| Get argument value | myArg.get(commandContext) |
| Require permission | requirePermission(HytalePermissions.fromCommand("name")) |
| Make command public | Override canGeneratePermission() to return false |
| Add variant | addUsageVariant(new OtherCommand()) |
| Add alias | addAliases("alias1", "alias2") |
| Group subcommands | Extend AbstractCommandCollection, call addSubCommand(...) |
| Register command | getCommandRegistry().registerCommand(new MyCommand()) in setup() |
Command Types
AbstractAsyncCommand
Runs on a background thread. Cannot safely access Store or Ref without getting the world first. Best for world-independent commands (e.g., displaying rules).
public class ServerRulesCommand extends AbstractAsyncCommand {
public ServerRulesCommand() {
super("rules", "Lists the servers rules");
}
@Override
protected CompletableFuture<Void> executeAsync(@Nonnull CommandContext context) {
context.sendMessage(Message.raw("The only rule is there are no rules."));
return CompletableFuture.completedFuture(null);
}
}
Warning:
AbstractAsyncCommandruns asynchronously - it cannot edit Stores or Refs without first getting the desired world. For most commands, prefer the other command types.
AbstractPlayerCommand
Tied to the executing player and their world. Runs on the world thread - safe to access Store and Ref directly. Most common command type.
public class ExampleCommand extends AbstractPlayerCommand {
public ExampleCommand() {
super("test", "Super test command!");
}
@Override
protected void execute(@Nonnull CommandContext commandContext,
@Nonnull Store<EntityStore> store,
@Nonnull Ref<EntityStore> ref,
@Nonnull PlayerRef playerRef,
@Nonnull World world) {
Player player = store.getComponent(ref, Player.getComponentType());
UUIDComponent component = store.getComponent(ref, UUIDComponent.getComponentType());
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
player.sendMessage(Message.raw("Transform : " + transform.getPosition()));
}
}
Note: Long-running operations (like IO) in
AbstractPlayerCommandwill block the world thread and cause lag. UseAbstractAsyncCommandfor heavy IO.
AbstractTargetPlayerCommand
Like AbstractPlayerCommand but adds a --player <value> argument to target a different player. Thread-safe - override execute(), not executeAsync().
AbstractTargetEntityCommand
Uses a raycast to target the entity the player is looking at. Runs on the world thread of the targeted entity.
@Override
protected void execute(CommandContext context,
Store<EntityStore> store,
Ref<EntityStore> ref,
World world) {
// ref is the targeted entity's reference
EntityStatMap stats = store.getComponent(ref, EntityStatMap.getComponentType());
if (stats == null) {
context.sendMessage(Message.raw("This entity has no stats!"));
return;
}
int healthIdx = DefaultEntityStatTypes.getHealth();
EntityStatValue health = stats.get(healthIdx);
if (health == null) {
context.sendMessage(Message.raw("This entity has no health!"));
return;
}
stats.addValue(healthIdx, 100);
}
Arguments
Arguments are added in the command's constructor. The value is retrieved during execute by passing commandContext to the argument's get() method.
Argument Types
| Method | Behavior | Usage |
|---|---|---|
withRequiredArg(name, desc, type) | Must be provided; parsed left-to-right positionally | RequiredArg<T> |
withOptionalArg(name, desc, type) | Returns null if not provided; uses --key value syntax | OptionalArg<T> |
withDefaultArg(name, desc, type, default, defaultDesc) | Returns default if not provided | DefaultArg<T> |
withFlagArg(name, desc) | Boolean switch; true if present, false if not; uses --name | FlagArg |
ArgTypes
Use the ArgTypes server reference as the authoritative list when you need an exact constant name or a less common parser. The most common types used in plugins are:
ArgTypes.STRINGArgTypes.INTEGERArgTypes.BOOLEANArgTypes.FLOATArgTypes.DOUBLEArgTypes.UUIDArgTypes.PLAYER_REF
The full ArgTypes page also covers world, coordinate, asset, range, color, block, and game-mode parsers. Check it before inventing a custom parser for something the server already supports.
Full Arguments Example
// Usage: /healplayer --health 50 --message "Feels Good" --debug
public class HealPlayerCommand extends AbstractTargetPlayerCommand {
private final DefaultArg<Float> healthArg;
private final OptionalArg<String> messageArg;
private final FlagArg debugArg;
public HealPlayerCommand() {
super("healplayer", "Healing a player for an <input> amount of HP (default: 100)");
this.healthArg = this.withDefaultArg("health", "Amount to heal player",
ArgTypes.FLOAT, (float) 100, "Desc of Default: 100");
this.messageArg = this.withOptionalArg("message",
"Message to print while healing", ArgTypes.STRING);
this.debugArg = this.withFlagArg("debug", "Add debug logs");
}
@Override
protected void execute(@Nonnull CommandContext commandContext,
@Nullable Ref<EntityStore> ref,
@Nonnull Ref<EntityStore> ref1,
@Nonnull PlayerRef playerRef,
@Nonnull World world,
@Nonnull Store<EntityStore> store) {
if (this.debugArg.get(commandContext)) {
commandContext.sendMessage(Message.raw("We are debugging"));
}
EntityStatMap stats = store.getComponent(ref, EntityStatMap.getComponentType());
int healthIdx = DefaultEntityStatTypes.getHealth();
stats.addStatValue(healthIdx, healthArg.get(commandContext));
}
}
Argument Validators
Add validators to arguments using .addValidator(). Built-in validators are in the Validators class:
OptionalArg<Integer> healAmount = withOptionalArg("amount", "Heal Amount", ArgTypes.INTEGER)
.addValidator(Validators.greaterThan(0))
.addValidator(Validators.lessThan(1000));
Custom Validators
Implement com.hypixel.hytale.codec.validation.Validator<T>:
import com.hypixel.hytale.codec.schema.SchemaContext;
import com.hypixel.hytale.codec.schema.config.Schema;
import com.hypixel.hytale.codec.validation.ValidationResults;
import com.hypixel.hytale.codec.validation.Validator;
public class MyCustomValidator implements Validator<String> {
@Nonnull
private final String bannedValue;
public MyCustomValidator(@Nonnull String bannedValue) {
this.bannedValue = bannedValue;
}
@Override
public void accept(@Nullable String input, @Nonnull ValidationResults results) {
if (this.bannedValue.equalsIgnoreCase(input)) {
results.fail("The given value has been banned.");
}
}
@Override
public void updateSchema(SchemaContext context, @Nonnull Schema target) {
// Optional: update schema for dynamic validation
throw new UnsupportedOperationException("Not implemented yet.");
}
}
Usage:
String bannedRole = "badword";
OptionalArg<String> roleArg = withOptionalArg("role", "Role to assign", ArgTypes.STRING)
.addValidator(new MyCustomValidator(bannedRole));
Permissions
Add permission requirements in the constructor:
public HealPlayerCommand() {
super("healplayer", "heal a player a given amount of HP");
// Single permission
requirePermission(HytalePermissions.fromCommand("rules"));
// Multiple required permissions (AND)
requirePermission(HytalePermissions.fromCommand("usercommands"));
// OR block - needs one from a list
requirePermission(
PermissionRules.or(
HytalePermissions.fromCommand("moderator"),
HytalePermissions.fromCommand("admin")
)
);
}
Use
/permin-game to manage player permissions and groups. Run/perm --helpfor usage.
Making a Command Require No Permission
Override canGeneratePermission() (RECOMMENDED):
@Override
protected boolean canGeneratePermission() {
return false; // Prevents auto-generated
---
*Content truncated.*
When not to use it
- →When using standard Hytale commands
- →When the command logic is too complex for plugins
Prerequisites
Limitations
- →AbstractAsyncCommand cannot access world state directly
- →Long IO operations block the world thread
How it compares
Offers a structured, type-safe approach to command creation compared to manual parsing.
Compared to similar skills
hytale-commands side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| hytale-commands (this skill) | 0 | 5mo | No flags | Advanced |
| backend-microservice-development | 2 | 3mo | No flags | Intermediate |
| hexagonal-architecture | 0 | 3mo | No flags | Advanced |
| minecraft-bukkit-pro | 90 | 4mo | No flags | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by JBurlison
View all by JBurlison →You might also like
backend-microservice-development
TencentBlueKing
后端微服务开发规范,涵盖目录结构、分层架构(API/Service/DAO)、依赖注入、配置管理、Spring Boot 最佳实践。当用户进行后端开发、创建新微服务、编写 Kotlin/Java 代码或设计服务架构时使用。
hexagonal-architecture
jssmy
Design, implement, and refactor Ports & Adapters systems with clear domain boundaries, dependency inversion, and testable use-case orchestration across TypeScript, Java, Kotlin, and Go services. Use for: new features needing long-term maintainability, decoupling domain logic from frameworks/DB/HTTP,
minecraft-bukkit-pro
sickn33
Master Minecraft server plugin development with Bukkit, Spigot, and Paper APIs. Specializes in event-driven architecture, command systems, world manipulation, player management, and performance optimization. Use PROACTIVELY for plugin architecture, gameplay mechanics, server-side features, or cross-version compatibility.
java-coding-standards
affaan-m
Java coding standards for Spring Boot services: naming, immutability, Optional usage, streams, exceptions, generics, and project layout.
springboot-tdd
affaan-m
Test-driven development for Spring Boot using JUnit 5, Mockito, MockMvc, Testcontainers, and JaCoCo. Use when adding features, fixing bugs, or refactoring.
kotlin-coroutines
vitorpamplona
Advanced Kotlin coroutines patterns for AmethystMultiplatform. Use when working with: (1) Structured concurrency (supervisorScope, coroutineScope), (2) Advanced Flow operators (flatMapLatest, combine, merge, shareIn, stateIn), (3) Channels and callbackFlow, (4) Dispatcher management and context switching, (5) Exception handling (CoroutineExceptionHandler, SupervisorJob), (6) Testing async code (runTest, Turbine), (7) Nostr relay connection pools and subscriptions, (8) Backpressure handling in event streams. Delegates to kotlin-expert for basic StateFlow/SharedFlow patterns. Complements nostr-expert for relay communication.