Command Types
Přehled typů příkazů v Hytale a kdy který použít.
---
Rozhodovací Tabulka
| Potřeba | Použij |
|---------|--------|
| Jednoduchý příkaz bez komponentů | CommandBase |
| Příkaz s přístupem ke komponentům | AbstractPlayerCommand |
| Těžké/blocking operace | AbstractAsyncCommand |
| Více sub-příkazů | AbstractCommandCollection |
| Jen konzole | CommandBase + !context.isPlayer() |
---
CommandBase
Základní synchronní příkaz. Nepoužívej pro přístup ke komponentům!
public class PingCommand extends CommandBase { public PingCommand() {
super("ping", "myplugin.commands.ping.desc");
}
@Override
protected void executeSync(@Nonnull CommandContext context) {
context.sendMessage(Message.raw("Pong!"));
}
}
// Registrace
@Override
protected void setup() {
getCommandRegistry().registerCommand(new PingCommand());
}
Kdy použít
- Jednoduché příkazy bez přístupu ke komponentům
- Konzolové příkazy
- Příkazy které jen posílají zprávy
- Když potřebuješ přístup ke komponentům hráče
- Způsobí "Assert not in thread!" error
Kdy NEPOUŽÍVAT
---
AbstractPlayerCommand
Příkaz pro hráče s bezpečným přístupem ke komponentům. Doporučeno pro většinu příkazů.
public class StatsCommand extends AbstractPlayerCommand { public StatsCommand() {
super("stats", "myplugin.commands.stats.desc");
this.requirePermission(HytalePermissions.fromCommand("stats.self"));
}
@Override
protected void execute(
@Nonnull CommandContext context,
@Nonnull Store store,
@Nonnull Ref ref,
@Nonnull PlayerRef playerRef,
@Nonnull World world
) {
// BEZPEČNÉ - AbstractPlayerCommand zajišťuje správný thread
Player player = store.getComponent(ref, Player.getComponentType());
if (player != null) {
// Přístup k vlastním komponentům
CustomStatsComponent stats = store.getComponent(ref, CustomStatsComponent.getComponentType());
if (stats != null) {
context.sendMessage(Message.raw("Tvoje statistiky: " + stats.toString()));
}
}
}
}
Parametry execute()
| Parametr | Typ | Popis |
|----------|-----|-------|
| context | CommandContext | Kontext příkazu |
| store | Store | ECS store pro komponenty |
| ref | Ref | Reference na entitu hráče |
| playerRef | PlayerRef | Reference na hráče |
| world | World | Svět hráče |
Kdy použít
---
AbstractPlayerCommand s Targetem
Příkaz který operuje na jiném hráči (ne na odesílateli).
public class HealOtherCommand extends AbstractPlayerCommand { @Nonnull
private final RequiredArg targetArg =
this.withRequiredArg("player", "myplugin.commands.heal.target", ArgTypes.PLAYER_REF);
public HealOtherCommand() {
super("heal", "myplugin.commands.heal.desc");
this.requirePermission(HytalePermissions.fromCommand("heal.other"));
}
@Override
protected void execute(
@Nonnull CommandContext context,
@Nonnull Store store,
@Nonnull Ref ref,
@Nonnull PlayerRef playerRef,
@Nonnull World world
) {
PlayerRef targetPlayerRef = this.targetArg.get(context);
Ref targetRef = targetPlayerRef.getReference();
if (targetRef != null && targetRef.isValid()) {
Store targetStore = targetRef.getStore();
World targetWorld = targetStore.getExternalData().getWorld();
// Spusť na world threadu targetu
targetWorld.execute(() -> {
Player targetPlayer = targetStore.getComponent(targetRef, Player.getComponentType());
if (targetPlayer != null) {
// Heal logic
context.sendMessage(Message.raw("Vyléčil jsi " + targetPlayerRef.getUsername()));
}
});
} else {
context.sendMessage(Message.raw("Hráč není online"));
}
}
}
---
AbstractAsyncCommand
Pro příkazy s těžkými operacemi (databáze, soubory, síť).
public class ExportCommand extends AbstractAsyncCommand { public ExportCommand() {
super("export", "myplugin.commands.export.desc");
}
@Override
protected CompletableFuture executeAsync(@Nonnull CommandContext context) {
return CompletableFuture.runAsync(() -> {
context.sendMessage(Message.raw("Exportuji data..."));
// Těžká práce (soubory, databáze...)
try {
exportDataToFile();
context.sendMessage(Message.raw("Export dokončen!"));
} catch (Exception e) {
context.sendMessage(Message.raw("Chyba: " + e.getMessage()));
}
});
}
}
S přístupem ke komponentům
public class SaveStatsCommand extends AbstractAsyncCommand { @Override
protected CompletableFuture executeAsync(@Nonnull CommandContext context) {
Player player = context.senderAs(Player.class);
World world = player.getWorld();
return CompletableFuture.runAsync(() -> {
// Nejprve získej data na world threadu
final StatsData[] dataHolder = new StatsData[1];
world.execute(() -> {
Ref ref = player.getRef();
Store store = ref.getStore();
CustomStatsComponent stats = store.getComponent(ref, CustomStatsComponent.getComponentType());
dataHolder[0] = stats != null ? stats.toData() : null;
});
// Počkej na world.execute()
// (V praxi bys měl použít CompletableFuture chain)
// Async uložení
if (dataHolder[0] != null) {
database.saveStats(player.getUuid(), dataHolder[0]);
context.sendMessage(Message.raw("Statistiky uloženy!"));
}
});
}
}
---
AbstractCommandCollection
Pro příkazy s více sub-příkazy.
public class WarpCommand extends AbstractCommandCollection { public WarpCommand() {
super("warp", "myplugin.commands.warp.desc");
// Přidej sub-příkazy
this.addSubCommand(new WarpSetCommand());
this.addSubCommand(new WarpGoCommand());
this.addSubCommand(new WarpListCommand());
this.addSubCommand(new WarpDeleteCommand());
}
}
// Sub-příkaz: /warp set
public class WarpSetCommand extends AbstractPlayerCommand {
@Nonnull
private final RequiredArg nameArg =
this.withRequiredArg("name", "myplugin.commands.warp.set.name", ArgTypes.STRING);
public WarpSetCommand() {
super("set", "myplugin.commands.warp.set.desc");
this.requirePermission(HytalePermissions.fromCommand("warp.set"));
}
@Override
protected void execute(
@Nonnull CommandContext context,
@Nonnull Store store,
@Nonnull Ref ref,
@Nonnull PlayerRef playerRef,
@Nonnull World world
) {
String warpName = nameArg.get(context);
TransformComponent transform = store.getComponent(ref, TransformComponent.getComponentType());
if (transform != null) {
Vector3d position = transform.getPosition();
// Uložení warpu...
context.sendMessage(Message.raw("Warp '" + warpName + "' vytvořen!"));
}
}
}
// Sub-příkaz: /warp go
public class WarpGoCommand extends AbstractPlayerCommand {
@Nonnull
private final RequiredArg nameArg =
this.withRequiredArg("name", "myplugin.commands.warp.go.name", ArgTypes.STRING);
public WarpGoCommand() {
super("go", "myplugin.commands.warp.go.desc");
}
@Override
protected void execute(
@Nonnull CommandContext context,
@Nonnull Store store,
@Nonnull Ref ref,
@Nonnull PlayerRef playerRef,
@Nonnull World world
) {
String warpName = nameArg.get(context);
// Teleport logic...
}
}
Použití
/warp set home
/warp go home
/warp list
/warp delete home
---
Usage Variants
Pro příkazy s různými variantami použití.
public class SpawnCommand extends AbstractPlayerCommand { @Nonnull
private final OptionalArg spawnIndexArg =
this.withOptionalArg("spawnIndex", "myplugin.commands.spawn.index", ArgTypes.INTEGER);
public SpawnCommand() {
super("spawn", "myplugin.commands.spawn.desc");
this.requirePermission(HytalePermissions.fromCommand("spawn.self"));
// Přidej variantu pro teleport jiného hráče
this.addUsageVariant(new SpawnOtherCommand());
}
@Override
protected void execute(...) {
// /spawn [index] - teleport sebe
}
// Varianta: /spawn [index]
private static class SpawnOtherCommand extends CommandBase {
@Nonnull
private final RequiredArg playerArg =
this.withRequiredArg("player", "myplugin.commands.spawn.player", ArgTypes.PLAYER_REF);
SpawnOtherCommand() {
super("myplugin.commands.spawn.other.desc");
this.requirePermission(HytalePermissions.fromCommand("spawn.other"));
}
@Override
protected void executeSync(@Nonnull CommandContext context) {
// /spawn [index] - teleport jiného hráče
}
}
}
---
Shrnutí
| Třída | Thread-safe komponenty | Async | Sub-příkazy |
|-------|------------------------|-------|-------------|
| CommandBase | Ne | Ne | Ne |
| AbstractPlayerCommand | Ano | Ne | Ne |
| AbstractAsyncCommand | S world.execute() | Ano | Ne |
| AbstractCommandCollection | - | - | Ano |