Init
This commit is contained in:
14
content/core-concepts/commands/_index.en.md
Normal file
14
content/core-concepts/commands/_index.en.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Commands
|
||||
type: docs
|
||||
weight: 1
|
||||
---
|
||||
|
||||
Commands allow players and operators to interact with your plugin through the chat or console. This section covers how to create and register commands.
|
||||
|
||||
{{< cards >}}
|
||||
{{< card link="creating-commands" title="Creating Commands" subtitle="Implement AbstractCommand for custom commands" >}}
|
||||
{{< card link="argument-types" title="Argument Types" subtitle="All available ArgTypes and how to use them" >}}
|
||||
{{< card link="subcommands" title="Subcommands" subtitle="Create command hierarchies" >}}
|
||||
{{< card link="command-context" title="Command Context" subtitle="Access sender, arguments, and more" >}}
|
||||
{{< /cards >}}
|
||||
14
content/core-concepts/commands/_index.fr.md
Normal file
14
content/core-concepts/commands/_index.fr.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
title: Commandes
|
||||
type: docs
|
||||
weight: 1
|
||||
---
|
||||
|
||||
Les commandes permettent aux joueurs et opérateurs d'interagir avec votre plugin via le chat ou la console. Cette section explique comment créer et enregistrer des commandes.
|
||||
|
||||
{{< cards >}}
|
||||
{{< card link="creating-commands" title="Créer des Commandes" subtitle="Implémenter AbstractCommand pour des commandes personnalisées" >}}
|
||||
{{< card link="argument-types" title="Types d'Arguments" subtitle="Tous les ArgTypes disponibles et comment les utiliser" >}}
|
||||
{{< card link="subcommands" title="Sous-commandes" subtitle="Créer des hiérarchies de commandes" >}}
|
||||
{{< card link="command-context" title="Contexte de Commande" subtitle="Accéder à l'expéditeur, arguments, et plus" >}}
|
||||
{{< /cards >}}
|
||||
1065
content/core-concepts/commands/argument-types.en.md
Normal file
1065
content/core-concepts/commands/argument-types.en.md
Normal file
File diff suppressed because it is too large
Load Diff
1065
content/core-concepts/commands/argument-types.fr.md
Normal file
1065
content/core-concepts/commands/argument-types.fr.md
Normal file
File diff suppressed because it is too large
Load Diff
272
content/core-concepts/commands/command-context.en.md
Normal file
272
content/core-concepts/commands/command-context.en.md
Normal file
@@ -0,0 +1,272 @@
|
||||
---
|
||||
title: Command Context
|
||||
type: docs
|
||||
weight: 4
|
||||
---
|
||||
|
||||
The `CommandContext` provides access to command execution information, including the sender, parsed arguments, and the original input string.
|
||||
|
||||
## Accessing the Context
|
||||
|
||||
The context is passed to your `execute()` method:
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
// Use context here
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
## Command Sender
|
||||
|
||||
Get the entity or console that executed the command:
|
||||
|
||||
```java
|
||||
CommandSender sender = context.sender();
|
||||
|
||||
// Send messages (must use Message class)
|
||||
sender.sendMessage(Message.raw("Hello!"));
|
||||
sender.sendMessage(Message.translation("my.translation.key"));
|
||||
|
||||
// Check permissions
|
||||
if (sender.hasPermission("myplugin.admin")) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Checking Sender Type
|
||||
|
||||
```java
|
||||
if (context.sender() instanceof Player) {
|
||||
Player player = (Player) context.sender();
|
||||
// Player-specific logic
|
||||
} else {
|
||||
// Console or other sender
|
||||
context.sender().sendMessage("This command requires a player!");
|
||||
}
|
||||
```
|
||||
|
||||
## Getting Arguments
|
||||
|
||||
### Required Arguments
|
||||
|
||||
```java
|
||||
private final RequiredArg<PlayerRef> playerArg;
|
||||
private final RequiredArg<Integer> countArg;
|
||||
|
||||
// In execute():
|
||||
PlayerRef player = context.get(playerArg); // Never null for required args
|
||||
int count = context.get(countArg); // Never null for required args
|
||||
```
|
||||
|
||||
### Optional Arguments
|
||||
|
||||
```java
|
||||
private final OptionalArg<String> reasonArg;
|
||||
|
||||
// Get value (may be null if not provided)
|
||||
String reason = context.get(reasonArg);
|
||||
|
||||
// Check if provided before using
|
||||
if (context.provided(reasonArg)) {
|
||||
String reason = context.get(reasonArg);
|
||||
}
|
||||
```
|
||||
|
||||
### Default Arguments
|
||||
|
||||
```java
|
||||
private final DefaultArg<Integer> countArg; // Default: 1
|
||||
|
||||
// Always returns a value (never null)
|
||||
int count = context.get(countArg); // Returns default if not specified
|
||||
```
|
||||
|
||||
### Flag Arguments
|
||||
|
||||
```java
|
||||
private final FlagArg silentFlag;
|
||||
|
||||
// Check if flag was provided
|
||||
boolean isSilent = context.provided(silentFlag);
|
||||
```
|
||||
|
||||
## Input String
|
||||
|
||||
Access the original command input:
|
||||
|
||||
```java
|
||||
String input = context.getInputString();
|
||||
// For "/give player123 sword 5" -> "give player123 sword 5"
|
||||
```
|
||||
|
||||
## The Command
|
||||
|
||||
Access the command being executed:
|
||||
|
||||
```java
|
||||
AbstractCommand command = context.getCalledCommand();
|
||||
String commandName = command.getName();
|
||||
String fullName = command.getFullyQualifiedName(); // e.g., "admin kick"
|
||||
```
|
||||
|
||||
## Argument Type Fallback Behavior
|
||||
|
||||
Some argument types have special processing with fallback behavior. This is handled by the argument type's `processedGet()` method:
|
||||
|
||||
### PLAYER_REF Fallback
|
||||
|
||||
When using `PLAYER_REF` with optional arguments:
|
||||
- If argument is not provided and sender is a player, the argument type can return the sender's PlayerRef
|
||||
|
||||
```java
|
||||
// Note: This fallback behavior is in the argument type, not CommandContext
|
||||
private final OptionalArg<PlayerRef> targetArg;
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
PlayerRef target = context.get(targetArg);
|
||||
|
||||
// Manual fallback if null
|
||||
if (target == null && context.sender() instanceof Player player) {
|
||||
target = player.getPlayerRef();
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### World Argument Fallback
|
||||
|
||||
When using `WORLD` with optional arguments:
|
||||
- If not specified and sender is player, returns player's world
|
||||
- If only one world exists, returns that world
|
||||
|
||||
```java
|
||||
private final OptionalArg<World> worldArg;
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
World world = context.get(worldArg);
|
||||
|
||||
// Manual fallback if null
|
||||
if (world == null && context.sender() instanceof Player player) {
|
||||
world = player.getWorld();
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
Commands can throw exceptions for error cases:
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
PlayerRef playerRef = context.get(playerArg);
|
||||
|
||||
// Check if player is online via ECS reference
|
||||
Ref<EntityStore> ref = playerRef.getReference();
|
||||
if (ref == null || !ref.isValid()) {
|
||||
throw new GeneralCommandException(
|
||||
Message.translation("error.player.offline")
|
||||
.param("player", playerRef.getUsername())
|
||||
);
|
||||
}
|
||||
|
||||
// Continue execution with valid reference
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
## Asynchronous Commands
|
||||
|
||||
Return a `CompletableFuture` for async operations:
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
if (!(context.sender() instanceof Player player)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PlayerRef playerRef = player.getPlayerRef(); // Note: deprecated
|
||||
World world = player.getWorld();
|
||||
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
// Async operation (e.g., database query)
|
||||
// PlayerData data = database.loadData(playerRef.getUuid());
|
||||
|
||||
// Return to world thread for game logic
|
||||
world.execute(() -> {
|
||||
Ref<EntityStore> ref = playerRef.getReference();
|
||||
if (ref != null && ref.isValid()) {
|
||||
// applyData(ref, data);
|
||||
playerRef.sendMessage(Message.raw("Data loaded!"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
```java
|
||||
public class GiveCommand extends AbstractCommand {
|
||||
|
||||
private final RequiredArg<Item> itemArg;
|
||||
private final OptionalArg<PlayerRef> targetArg;
|
||||
private final DefaultArg<Integer> countArg;
|
||||
private final FlagArg silentFlag;
|
||||
|
||||
public GiveCommand() {
|
||||
super("give", "Give items to a player");
|
||||
|
||||
itemArg = withRequiredArg("item", "Item to give", ArgTypes.ITEM_ASSET);
|
||||
targetArg = withOptionalArg("target", "Target player", ArgTypes.PLAYER_REF);
|
||||
countArg = withDefaultArg("count", "Amount", ArgTypes.INTEGER, 1, "1");
|
||||
silentFlag = withFlagArg("silent", "Don't broadcast");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
// Get arguments
|
||||
Item item = context.get(itemArg);
|
||||
int count = context.get(countArg); // Uses default if not specified
|
||||
boolean silent = context.provided(silentFlag);
|
||||
|
||||
// Get target with fallback to sender
|
||||
PlayerRef targetRef = context.get(targetArg);
|
||||
if (targetRef == null && context.sender() instanceof Player senderPlayer) {
|
||||
targetRef = senderPlayer.getPlayerRef(); // Note: deprecated
|
||||
}
|
||||
|
||||
if (targetRef == null) {
|
||||
throw new GeneralCommandException(
|
||||
Message.raw("Must specify a target player!")
|
||||
);
|
||||
}
|
||||
|
||||
// Validate target is online via ECS reference
|
||||
Ref<EntityStore> ref = targetRef.getReference();
|
||||
if (ref == null || !ref.isValid()) {
|
||||
throw new GeneralCommandException(
|
||||
Message.raw("Player is not online!")
|
||||
);
|
||||
}
|
||||
|
||||
// Execute - give item to player via ECS
|
||||
// ...
|
||||
|
||||
// Feedback
|
||||
if (!silent) {
|
||||
context.sendMessage(Message.raw(
|
||||
"Gave " + count + "x " + item.getId() + " to " + targetRef.getUsername()
|
||||
));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
272
content/core-concepts/commands/command-context.fr.md
Normal file
272
content/core-concepts/commands/command-context.fr.md
Normal file
@@ -0,0 +1,272 @@
|
||||
---
|
||||
title: Command Context
|
||||
type: docs
|
||||
weight: 4
|
||||
---
|
||||
|
||||
The `CommandContext` provides access to command execution information, including the sender, parsed arguments, and the original input string.
|
||||
|
||||
## Accessing the Context
|
||||
|
||||
The context is passed to your `execute()` method:
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
// Use context here
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
## Command Sender
|
||||
|
||||
Get the entity or console that executed the command:
|
||||
|
||||
```java
|
||||
CommandSender sender = context.sender();
|
||||
|
||||
// Send messages (must use Message class)
|
||||
sender.sendMessage(Message.raw("Hello!"));
|
||||
sender.sendMessage(Message.translation("my.translation.key"));
|
||||
|
||||
// Check permissions
|
||||
if (sender.hasPermission("myplugin.admin")) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Checking Sender Type
|
||||
|
||||
```java
|
||||
if (context.sender() instanceof Player) {
|
||||
Player player = (Player) context.sender();
|
||||
// Player-specific logic
|
||||
} else {
|
||||
// Console or other sender
|
||||
context.sender().sendMessage("This command requires a player!");
|
||||
}
|
||||
```
|
||||
|
||||
## Getting Arguments
|
||||
|
||||
### Required Arguments
|
||||
|
||||
```java
|
||||
private final RequiredArg<PlayerRef> playerArg;
|
||||
private final RequiredArg<Integer> countArg;
|
||||
|
||||
// In execute():
|
||||
PlayerRef player = context.get(playerArg); // Never null for required args
|
||||
int count = context.get(countArg); // Never null for required args
|
||||
```
|
||||
|
||||
### Optional Arguments
|
||||
|
||||
```java
|
||||
private final OptionalArg<String> reasonArg;
|
||||
|
||||
// Get value (may be null if not provided)
|
||||
String reason = context.get(reasonArg);
|
||||
|
||||
// Check if provided before using
|
||||
if (context.provided(reasonArg)) {
|
||||
String reason = context.get(reasonArg);
|
||||
}
|
||||
```
|
||||
|
||||
### Default Arguments
|
||||
|
||||
```java
|
||||
private final DefaultArg<Integer> countArg; // Default: 1
|
||||
|
||||
// Always returns a value (never null)
|
||||
int count = context.get(countArg); // Returns default if not specified
|
||||
```
|
||||
|
||||
### Flag Arguments
|
||||
|
||||
```java
|
||||
private final FlagArg silentFlag;
|
||||
|
||||
// Check if flag was provided
|
||||
boolean isSilent = context.provided(silentFlag);
|
||||
```
|
||||
|
||||
## Input String
|
||||
|
||||
Access the original command input:
|
||||
|
||||
```java
|
||||
String input = context.getInputString();
|
||||
// For "/give player123 sword 5" -> "give player123 sword 5"
|
||||
```
|
||||
|
||||
## The Command
|
||||
|
||||
Access the command being executed:
|
||||
|
||||
```java
|
||||
AbstractCommand command = context.getCalledCommand();
|
||||
String commandName = command.getName();
|
||||
String fullName = command.getFullyQualifiedName(); // e.g., "admin kick"
|
||||
```
|
||||
|
||||
## Argument Type Fallback Behavior
|
||||
|
||||
Some argument types have special processing with fallback behavior. This is handled by the argument type's `processedGet()` method:
|
||||
|
||||
### PLAYER_REF Fallback
|
||||
|
||||
When using `PLAYER_REF` with optional arguments:
|
||||
- If argument is not provided and sender is a player, the argument type can return the sender's PlayerRef
|
||||
|
||||
```java
|
||||
// Note: This fallback behavior is in the argument type, not CommandContext
|
||||
private final OptionalArg<PlayerRef> targetArg;
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
PlayerRef target = context.get(targetArg);
|
||||
|
||||
// Manual fallback if null
|
||||
if (target == null && context.sender() instanceof Player player) {
|
||||
target = player.getPlayerRef();
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### World Argument Fallback
|
||||
|
||||
When using `WORLD` with optional arguments:
|
||||
- If not specified and sender is player, returns player's world
|
||||
- If only one world exists, returns that world
|
||||
|
||||
```java
|
||||
private final OptionalArg<World> worldArg;
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
World world = context.get(worldArg);
|
||||
|
||||
// Manual fallback if null
|
||||
if (world == null && context.sender() instanceof Player player) {
|
||||
world = player.getWorld();
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
Commands can throw exceptions for error cases:
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
PlayerRef playerRef = context.get(playerArg);
|
||||
|
||||
// Check if player is online via ECS reference
|
||||
Ref<EntityStore> ref = playerRef.getReference();
|
||||
if (ref == null || !ref.isValid()) {
|
||||
throw new GeneralCommandException(
|
||||
Message.translation("error.player.offline")
|
||||
.param("player", playerRef.getUsername())
|
||||
);
|
||||
}
|
||||
|
||||
// Continue execution with valid reference
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
## Asynchronous Commands
|
||||
|
||||
Return a `CompletableFuture` for async operations:
|
||||
|
||||
```java
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
if (!(context.sender() instanceof Player player)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PlayerRef playerRef = player.getPlayerRef(); // Note: deprecated
|
||||
World world = player.getWorld();
|
||||
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
// Async operation (e.g., database query)
|
||||
// PlayerData data = database.loadData(playerRef.getUuid());
|
||||
|
||||
// Return to world thread for game logic
|
||||
world.execute(() -> {
|
||||
Ref<EntityStore> ref = playerRef.getReference();
|
||||
if (ref != null && ref.isValid()) {
|
||||
// applyData(ref, data);
|
||||
playerRef.sendMessage(Message.raw("Data loaded!"));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
```java
|
||||
public class GiveCommand extends AbstractCommand {
|
||||
|
||||
private final RequiredArg<Item> itemArg;
|
||||
private final OptionalArg<PlayerRef> targetArg;
|
||||
private final DefaultArg<Integer> countArg;
|
||||
private final FlagArg silentFlag;
|
||||
|
||||
public GiveCommand() {
|
||||
super("give", "Give items to a player");
|
||||
|
||||
itemArg = withRequiredArg("item", "Item to give", ArgTypes.ITEM_ASSET);
|
||||
targetArg = withOptionalArg("target", "Target player", ArgTypes.PLAYER_REF);
|
||||
countArg = withDefaultArg("count", "Amount", ArgTypes.INTEGER, 1, "1");
|
||||
silentFlag = withFlagArg("silent", "Don't broadcast");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
// Get arguments
|
||||
Item item = context.get(itemArg);
|
||||
int count = context.get(countArg); // Uses default if not specified
|
||||
boolean silent = context.provided(silentFlag);
|
||||
|
||||
// Get target with fallback to sender
|
||||
PlayerRef targetRef = context.get(targetArg);
|
||||
if (targetRef == null && context.sender() instanceof Player senderPlayer) {
|
||||
targetRef = senderPlayer.getPlayerRef(); // Note: deprecated
|
||||
}
|
||||
|
||||
if (targetRef == null) {
|
||||
throw new GeneralCommandException(
|
||||
Message.raw("Must specify a target player!")
|
||||
);
|
||||
}
|
||||
|
||||
// Validate target is online via ECS reference
|
||||
Ref<EntityStore> ref = targetRef.getReference();
|
||||
if (ref == null || !ref.isValid()) {
|
||||
throw new GeneralCommandException(
|
||||
Message.raw("Player is not online!")
|
||||
);
|
||||
}
|
||||
|
||||
// Execute - give item to player via ECS
|
||||
// ...
|
||||
|
||||
// Feedback
|
||||
if (!silent) {
|
||||
context.sendMessage(Message.raw(
|
||||
"Gave " + count + "x " + item.getId() + " to " + targetRef.getUsername()
|
||||
));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
264
content/core-concepts/commands/creating-commands.en.md
Normal file
264
content/core-concepts/commands/creating-commands.en.md
Normal file
@@ -0,0 +1,264 @@
|
||||
---
|
||||
title: Creating Commands
|
||||
type: docs
|
||||
weight: 1
|
||||
---
|
||||
|
||||
Commands in Hytale are created by extending the `AbstractCommand` class. This page covers the basics of creating and registering commands.
|
||||
|
||||
## Basic Command Structure
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
|
||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class HelloCommand extends AbstractCommand {
|
||||
|
||||
public HelloCommand() {
|
||||
super("hello", "Sends a greeting message");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
context.sendMessage(Message.raw("Hello, World!"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Constructors
|
||||
|
||||
`AbstractCommand` provides several constructors:
|
||||
|
||||
```java
|
||||
// Name and description
|
||||
protected AbstractCommand(String name, String description)
|
||||
|
||||
// Name, description, and requires confirmation flag
|
||||
protected AbstractCommand(String name, String description, boolean requiresConfirmation)
|
||||
|
||||
// Description only (for variant commands)
|
||||
protected AbstractCommand(String description)
|
||||
```
|
||||
|
||||
## Registering Commands
|
||||
|
||||
Register commands in your plugin's `start()` method:
|
||||
|
||||
```java
|
||||
@Override
|
||||
public void start() {
|
||||
getCommandRegistry().registerCommand(new HelloCommand());
|
||||
}
|
||||
```
|
||||
|
||||
## Adding Aliases
|
||||
|
||||
Commands can have multiple names:
|
||||
|
||||
```java
|
||||
public class TeleportCommand extends AbstractCommand {
|
||||
|
||||
public TeleportCommand() {
|
||||
super("teleport", "Teleport to a location");
|
||||
addAliases("tp", "warp");
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Players can now use `/teleport`, `/tp`, or `/warp`.
|
||||
|
||||
## Command with Arguments
|
||||
|
||||
Add required arguments using `withRequiredArg`:
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
|
||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
|
||||
// For ECS operations: import com.hypixel.hytale.component.Ref;
|
||||
// For ECS operations: import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GiveCommand extends AbstractCommand {
|
||||
|
||||
private final RequiredArg<PlayerRef> targetArg;
|
||||
private final RequiredArg<Item> itemArg;
|
||||
|
||||
public GiveCommand() {
|
||||
super("give", "Give an item to a player");
|
||||
|
||||
targetArg = withRequiredArg("player", "Target player", ArgTypes.PLAYER_REF);
|
||||
itemArg = withRequiredArg("item", "Item to give", ArgTypes.ITEM_ASSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
PlayerRef targetRef = context.get(targetArg);
|
||||
Item item = context.get(itemArg);
|
||||
|
||||
// PlayerRef provides direct access to player info
|
||||
String username = targetRef.getUsername();
|
||||
|
||||
// For ECS operations, use getReference() to access the EntityStore
|
||||
// Ref<EntityStore> entityRef = targetRef.getReference();
|
||||
|
||||
// Give item to player...
|
||||
context.sendMessage(Message.raw("Gave item to " + username));
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Optional Arguments
|
||||
|
||||
Add optional arguments that don't need to be specified:
|
||||
|
||||
```java
|
||||
private final OptionalArg<Integer> countArg;
|
||||
|
||||
public GiveCommand() {
|
||||
super("give", "Give items to a player");
|
||||
|
||||
targetArg = withRequiredArg("player", "Target player", ArgTypes.PLAYER_REF);
|
||||
itemArg = withRequiredArg("item", "Item to give", ArgTypes.ITEM_ASSET);
|
||||
countArg = withOptionalArg("count", "Number of items", ArgTypes.INTEGER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
PlayerRef targetRef = context.get(targetArg);
|
||||
Item item = context.get(itemArg);
|
||||
Integer count = context.get(countArg); // May be null
|
||||
|
||||
int amount = count != null ? count : 1;
|
||||
// Give items to player...
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
Usage: `/give player123 iron_sword` or `/give player123 iron_sword --count 5`
|
||||
|
||||
## Default Arguments
|
||||
|
||||
Arguments with default values:
|
||||
|
||||
```java
|
||||
private final DefaultArg<Integer> countArg;
|
||||
|
||||
public GiveCommand() {
|
||||
super("give", "Give items to a player");
|
||||
|
||||
countArg = withDefaultArg("count", "Number of items",
|
||||
ArgTypes.INTEGER, 1, "defaults to 1");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
int count = context.get(countArg); // Never null, uses default
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Flag Arguments
|
||||
|
||||
Boolean flags that can be toggled:
|
||||
|
||||
```java
|
||||
private final FlagArg silentFlag;
|
||||
|
||||
public BroadcastCommand() {
|
||||
super("broadcast", "Send a message to all players");
|
||||
|
||||
silentFlag = withFlagArg("silent", "Don't show sender name");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
boolean silent = context.provided(silentFlag);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Usage: `/broadcast Hello everyone! --silent`
|
||||
|
||||
## List Arguments
|
||||
|
||||
For arguments that accept multiple values:
|
||||
|
||||
```java
|
||||
private final RequiredArg<List<String>> playersArg;
|
||||
|
||||
public KickAllCommand() {
|
||||
super("kickall", "Kick multiple players");
|
||||
|
||||
playersArg = withListRequiredArg("players", "Players to kick", ArgTypes.STRING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
List<String> players = context.get(playersArg);
|
||||
for (String player : players) {
|
||||
// Kick each player
|
||||
}
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
Usage: `/kickall player1 player2 player3`
|
||||
|
||||
Available list argument methods:
|
||||
- `withListRequiredArg(name, description, argType)` - Required list
|
||||
- `withListOptionalArg(name, description, argType)` - Optional list
|
||||
- `withListDefaultArg(name, description, argType, defaultValue, defaultDesc)` - List with default
|
||||
|
||||
## Requiring Confirmation
|
||||
|
||||
For dangerous commands, require explicit confirmation:
|
||||
|
||||
```java
|
||||
public class ResetCommand extends AbstractCommand {
|
||||
|
||||
public ResetCommand() {
|
||||
super("reset", "Reset all player data", true); // requiresConfirmation = true
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
// This only runs if --confirm was provided
|
||||
resetAllData();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Usage: `/reset --confirm`
|
||||
|
||||
## Permissions
|
||||
|
||||
Commands automatically generate permissions based on your plugin name:
|
||||
|
||||
```
|
||||
{group}.{plugin}.command.{commandname}
|
||||
```
|
||||
|
||||
For example: `com.example.myplugin.command.give`
|
||||
|
||||
You can also set a custom permission:
|
||||
|
||||
```java
|
||||
public GiveCommand() {
|
||||
super("give", "Give items");
|
||||
requirePermission("myplugin.admin.give");
|
||||
}
|
||||
```
|
||||
264
content/core-concepts/commands/creating-commands.fr.md
Normal file
264
content/core-concepts/commands/creating-commands.fr.md
Normal file
@@ -0,0 +1,264 @@
|
||||
---
|
||||
title: Créer des Commandes
|
||||
type: docs
|
||||
weight: 1
|
||||
---
|
||||
|
||||
Les commandes dans Hytale sont créées en étendant la classe `AbstractCommand`. Cette page couvre les bases de la création et de l'enregistrement des commandes.
|
||||
|
||||
## Structure de Base d'une Commande
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
|
||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class HelloCommand extends AbstractCommand {
|
||||
|
||||
public HelloCommand() {
|
||||
super("hello", "Envoie un message de salutation");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
context.sendMessage(Message.raw("Bonjour, le monde !"));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Constructeurs
|
||||
|
||||
`AbstractCommand` fournit plusieurs constructeurs :
|
||||
|
||||
```java
|
||||
// Nom et description
|
||||
protected AbstractCommand(String name, String description)
|
||||
|
||||
// Nom, description, et flag de confirmation requise
|
||||
protected AbstractCommand(String name, String description, boolean requiresConfirmation)
|
||||
|
||||
// Description seule (pour les commandes variantes)
|
||||
protected AbstractCommand(String description)
|
||||
```
|
||||
|
||||
## Enregistrer des Commandes
|
||||
|
||||
Enregistrez les commandes dans la méthode `start()` de votre plugin :
|
||||
|
||||
```java
|
||||
@Override
|
||||
public void start() {
|
||||
getCommandRegistry().registerCommand(new HelloCommand());
|
||||
}
|
||||
```
|
||||
|
||||
## Ajouter des Alias
|
||||
|
||||
Les commandes peuvent avoir plusieurs noms :
|
||||
|
||||
```java
|
||||
public class TeleportCommand extends AbstractCommand {
|
||||
|
||||
public TeleportCommand() {
|
||||
super("teleport", "Téléporter vers un emplacement");
|
||||
addAliases("tp", "warp");
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Les joueurs peuvent maintenant utiliser `/teleport`, `/tp`, ou `/warp`.
|
||||
|
||||
## Commande avec Arguments
|
||||
|
||||
Ajoutez des arguments requis avec `withRequiredArg` :
|
||||
|
||||
```java
|
||||
import com.hypixel.hytale.server.core.command.system.AbstractCommand;
|
||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
|
||||
// Pour les opérations ECS: import com.hypixel.hytale.component.Ref;
|
||||
// Pour les opérations ECS: import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GiveCommand extends AbstractCommand {
|
||||
|
||||
private final RequiredArg<PlayerRef> targetArg;
|
||||
private final RequiredArg<Item> itemArg;
|
||||
|
||||
public GiveCommand() {
|
||||
super("give", "Donner un item à un joueur");
|
||||
|
||||
targetArg = withRequiredArg("player", "Joueur cible", ArgTypes.PLAYER_REF);
|
||||
itemArg = withRequiredArg("item", "Item à donner", ArgTypes.ITEM_ASSET);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
PlayerRef targetRef = context.get(targetArg);
|
||||
Item item = context.get(itemArg);
|
||||
|
||||
// PlayerRef fournit un accès direct aux infos du joueur
|
||||
String username = targetRef.getUsername();
|
||||
|
||||
// Pour les opérations ECS, utiliser getReference() pour accéder à l'EntityStore
|
||||
// Ref<EntityStore> entityRef = targetRef.getReference();
|
||||
|
||||
// Donner l'item au joueur...
|
||||
context.sendMessage(Message.raw("Item donné à " + username));
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Arguments Optionnels
|
||||
|
||||
Ajoutez des arguments optionnels qui n'ont pas besoin d'être spécifiés :
|
||||
|
||||
```java
|
||||
private final OptionalArg<Integer> countArg;
|
||||
|
||||
public GiveCommand() {
|
||||
super("give", "Donner des items à un joueur");
|
||||
|
||||
targetArg = withRequiredArg("player", "Joueur cible", ArgTypes.PLAYER_REF);
|
||||
itemArg = withRequiredArg("item", "Item à donner", ArgTypes.ITEM_ASSET);
|
||||
countArg = withOptionalArg("count", "Nombre d'items", ArgTypes.INTEGER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
PlayerRef targetRef = context.get(targetArg);
|
||||
Item item = context.get(itemArg);
|
||||
Integer count = context.get(countArg); // Peut être null
|
||||
|
||||
int amount = count != null ? count : 1;
|
||||
// Donner les items au joueur...
|
||||
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
Utilisation : `/give player123 iron_sword` ou `/give player123 iron_sword --count 5`
|
||||
|
||||
## Arguments par Défaut
|
||||
|
||||
Arguments avec valeurs par défaut :
|
||||
|
||||
```java
|
||||
private final DefaultArg<Integer> countArg;
|
||||
|
||||
public GiveCommand() {
|
||||
super("give", "Donner des items à un joueur");
|
||||
|
||||
countArg = withDefaultArg("count", "Nombre d'items",
|
||||
ArgTypes.INTEGER, 1, "par défaut 1");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
int count = context.get(countArg); // Jamais null, utilise la valeur par défaut
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Arguments Flag
|
||||
|
||||
Flags booléens qui peuvent être activés :
|
||||
|
||||
```java
|
||||
private final FlagArg silentFlag;
|
||||
|
||||
public BroadcastCommand() {
|
||||
super("broadcast", "Envoyer un message à tous les joueurs");
|
||||
|
||||
silentFlag = withFlagArg("silent", "Ne pas afficher le nom de l'expéditeur");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
boolean silent = context.provided(silentFlag);
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Utilisation : `/broadcast Bonjour à tous ! --silent`
|
||||
|
||||
## Arguments Liste
|
||||
|
||||
Pour les arguments qui acceptent plusieurs valeurs :
|
||||
|
||||
```java
|
||||
private final RequiredArg<List<String>> playersArg;
|
||||
|
||||
public KickAllCommand() {
|
||||
super("kickall", "Expulser plusieurs joueurs");
|
||||
|
||||
playersArg = withListRequiredArg("players", "Joueurs à expulser", ArgTypes.STRING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
List<String> players = context.get(playersArg);
|
||||
for (String player : players) {
|
||||
// Expulser chaque joueur
|
||||
}
|
||||
return null;
|
||||
}
|
||||
```
|
||||
|
||||
Utilisation : `/kickall player1 player2 player3`
|
||||
|
||||
Méthodes d'arguments liste disponibles :
|
||||
- `withListRequiredArg(name, description, argType)` - Liste requise
|
||||
- `withListOptionalArg(name, description, argType)` - Liste optionnelle
|
||||
- `withListDefaultArg(name, description, argType, defaultValue, defaultDesc)` - Liste avec défaut
|
||||
|
||||
## Exiger une Confirmation
|
||||
|
||||
Pour les commandes dangereuses, exigez une confirmation explicite :
|
||||
|
||||
```java
|
||||
public class ResetCommand extends AbstractCommand {
|
||||
|
||||
public ResetCommand() {
|
||||
super("reset", "Réinitialiser toutes les données joueur", true); // requiresConfirmation = true
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CompletableFuture<Void> execute(CommandContext context) {
|
||||
// Ceci ne s'exécute que si --confirm a été fourni
|
||||
resetAllData();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Utilisation : `/reset --confirm`
|
||||
|
||||
## Permissions
|
||||
|
||||
Les commandes génèrent automatiquement des permissions basées sur le nom de votre plugin :
|
||||
|
||||
```
|
||||
{group}.{plugin}.command.{commandname}
|
||||
```
|
||||
|
||||
Par exemple : `com.example.myplugin.command.give`
|
||||
|
||||
Vous pouvez aussi définir une permission personnalisée :
|
||||
|
||||
```java
|
||||
public GiveCommand() {
|
||||
super("give", "Donner des items");
|
||||
requirePermission("myplugin.admin.give");
|
||||
}
|
||||
```
|
||||
224
content/core-concepts/commands/subcommands.en.md
Normal file
224
content/core-concepts/commands/subcommands.en.md
Normal 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");
|
||||
}
|
||||
```
|
||||
224
content/core-concepts/commands/subcommands.fr.md
Normal file
224
content/core-concepts/commands/subcommands.fr.md
Normal 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");
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user