This commit is contained in:
2026-01-20 20:33:59 +01:00
commit b16a40e431
583 changed files with 87339 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
---
title: Subcommands
type: docs
weight: 3
---
Subcommands allow you to create hierarchical command structures, organizing related functionality under a single parent command.
## Creating Subcommands
### Basic Structure
```java
public class AdminCommand extends AbstractCommand {
public AdminCommand() {
super("admin", "Administration commands");
// Add subcommands
addSubCommand(new KickSubCommand());
addSubCommand(new BanSubCommand());
addSubCommand(new MuteSubCommand());
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// This runs when no subcommand is specified
context.sender().sendMessage(Message.raw("Usage: /admin <kick|ban|mute>"));
return null;
}
}
```
### Subcommand Implementation
```java
public class KickSubCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
private final OptionalArg<String> reasonArg;
public KickSubCommand() {
super("kick", "Kick a player from the server");
playerArg = withRequiredArg("player", "Player to kick", ArgTypes.PLAYER_REF);
reasonArg = withOptionalArg("reason", "Kick reason", ArgTypes.STRING);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef target = context.get(playerArg);
String reason = context.get(reasonArg);
// Check if player is online via ECS reference
Ref<EntityStore> ref = target.getReference();
if (ref != null && ref.isValid()) {
// Disconnect via PacketHandler
String kickReason = reason != null ? reason : "Kicked by admin";
target.getPacketHandler().disconnect(kickReason);
context.sendMessage(Message.raw("Kicked " + target.getUsername()));
}
return null;
}
}
```
Usage: `/admin kick player123 --reason "Breaking rules"`
## Command Collections
For commands that only contain subcommands (no direct execution), extend `AbstractCommandCollection`:
```java
public class ManageCommand extends AbstractCommandCollection {
public ManageCommand() {
super("manage", "Management commands");
addSubCommand(new ManageUsersCommand());
addSubCommand(new ManageWorldsCommand());
addSubCommand(new ManagePluginsCommand());
}
}
```
With `AbstractCommandCollection`, running `/manage` without a subcommand will show available subcommands automatically.
## Nested Subcommands
Subcommands can have their own subcommands:
```java
public class ManageUsersCommand extends AbstractCommand {
public ManageUsersCommand() {
super("users", "User management");
addSubCommand(new ListUsersCommand()); // /manage users list
addSubCommand(new AddUserCommand()); // /manage users add
addSubCommand(new RemoveUserCommand()); // /manage users remove
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// Show usage for /manage users
return null;
}
}
```
## Subcommand Aliases
Subcommands can have aliases just like regular commands:
```java
public class TeleportCommand extends AbstractCommand {
public TeleportCommand() {
super("teleport", "Teleport commands");
addAliases("tp");
addSubCommand(new TeleportHereCommand());
addSubCommand(new TeleportAllCommand());
}
}
public class TeleportHereCommand extends AbstractCommand {
public TeleportHereCommand() {
super("here", "Teleport player to you");
addAliases("h", "tome");
}
// ...
}
```
Now players can use:
- `/teleport here player1`
- `/tp here player1`
- `/tp h player1`
- `/tp tome player1`
## Command Variants
Variants allow the same command to accept different argument patterns:
```java
public class TpCommand extends AbstractCommand {
private final RequiredArg<PlayerRef> targetArg;
public TpCommand() {
super("tp", "Teleport command");
// Main variant: /tp <player>
targetArg = withRequiredArg("target", "Player to teleport to", ArgTypes.PLAYER_REF);
// Add variant: /tp <player> <destination>
addUsageVariant(new TpToPlayerVariant());
// Add variant: /tp <x> <y> <z>
addUsageVariant(new TpToPositionVariant());
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
// Teleport sender to target player
PlayerRef target = context.get(targetArg);
// ...
return null;
}
}
```
### Variant Implementation
```java
public class TpToPlayerVariant extends AbstractCommand {
private final RequiredArg<PlayerRef> playerArg;
private final RequiredArg<PlayerRef> destinationArg;
public TpToPlayerVariant() {
// No name for variants - use description only
super("Teleport one player to another");
playerArg = withRequiredArg("player", "Player to teleport", ArgTypes.PLAYER_REF);
destinationArg = withRequiredArg("destination", "Destination player", ArgTypes.PLAYER_REF);
}
@Override
protected CompletableFuture<Void> execute(CommandContext context) {
PlayerRef player = context.get(playerArg);
PlayerRef destination = context.get(destinationArg);
// Teleport player to destination
return null;
}
}
```
{{< callout type="info" >}}
Variants are distinguished by the number of required parameters. Each variant must have a different number of required arguments.
{{< /callout >}}
## Permission Inheritance
Subcommand permissions are automatically built from the parent:
```
/admin -> myplugin.command.admin
/admin kick -> myplugin.command.admin.kick
/admin ban -> myplugin.command.admin.ban
```
You can also set custom permissions:
```java
public KickSubCommand() {
super("kick", "Kick a player");
requirePermission("myplugin.admin.kick");
}
```