Register Commands¶
In this section, we'll walk you through creating a simple command. By the end of this section, you'll have a basic understanding of how to define and register commands with allay.
Create a Command¶
Let's start with a simple command /hello that greets the command sender.
And it's just as simple as that! You've just created a new command with description. Then you need to register the command into the server:
Add permissions¶
By default, only server operators can use newly created commands. Since we are creating a hello world command here, we might want to change its permission for everyone to use.
Let's make the following changes to our code:
See tips
Tip
The getPermissions() method returns a list of permissions that the command requires,
and OpPermissionCalculator is the default permission calculator for new players.
3rd party permission plugin (e.g. LuckPerms) may replace it (OpPermissionCalculator) with
their own permission calculator, and in that case this line of code will be meaningless.
Handle the commands¶
Now, the next step is to handle the commands we just created. Let's say we want to send a "Hello, World!" greeting message to whoever execute this command. It's just a few more lines away from that.
Now, install your plugin and restart your server. Join the game and type /help hello. You should see the usage
information, which indicates that our command has been successfully added to the game!
Now, type /hello, and you should receive the message "Hello World!".
Add parameters to commands¶
Now, let's say we want to send a custom message to greet the sender instead of "Hello World!". We can add a parameter to the command that accepts a message.
Let's change our code to add an optional parameter message with msg type:
Here, we checked if the optional parameter is provided.
And it's done! Now when the users use
/hello This is my message!, "This is my message!" will
be shown to them instead of "Hello World!".
Tip
To make the parameter mandatory, remove the .optional() method call.
Built-in parameter types¶
In the section above, we added a parameter with type msg which is a built-in type supported by Allay.
Here is a full list of all built-in parameter types:
| Type | Description | Result Type |
|---|---|---|
str(name) |
Single word string | String |
msg(name) |
Message (can contain spaces, consumes remaining args) | String |
intNum(name) |
Integer number | Integer |
floatNum(name) |
Float number | Float |
doubleNum(name) |
Double number | Double |
longNum(name) |
Long number | Long |
shortNum(name) |
Short number | Short |
bool(name) |
Boolean (true/false) | Boolean |
enums(name, values...) |
String enumeration | String |
enumsIgnoreCase(name, values...) |
String enumeration (case-insensitive) | String |
enumClass(name, EnumClass.class) |
Enum class (auto-mapped) | EnumClass |
target(name) |
Entity selector (@a, @e, @p, etc.) | List<Entity> |
playerTarget(name) |
Player selector | List<Entity> |
wildcardTarget(name) |
Wildcard target selector | String |
pos(name) |
Position (x y z, supports ~) | Vector3d |
gameMode(name) |
Game mode | GameMode |
difficulty(name) |
Difficulty | Difficulty |
effect(name) |
Effect type | EffectType |
enchantment(name) |
Enchantment type | EnchantmentType |
itemType(name) |
Item type | ItemType<?> |
blockType(name) |
Block type | BlockType<?> |
blockPropertyValues(name) |
Block property values | List<BlockPropertyValue> |
entityType(name) |
Entity type | EntityType<?> |
remain(name) |
Remaining arguments as list | List<String> |
key(name) |
Literal keyword (for subcommands) | String |
cmd(name) |
Command string | String |
Add command aliases¶
You can add aliases to your command so players can use shorter or alternative names:
Now players can use /hello, /hi, or /greet to execute your command.
Create subcommands¶
Many commands have subcommands (like /time set or /time add). You can create subcommands using
the key() method and navigate the command tree with up() and root():
root()returns to the root node to define a new branch. You can also useup()to go up one level, orup(n)to go up n levels.
Tree navigation methods¶
| Method | Description |
|---|---|
root() |
Return to the root node |
up() |
Go up one level in the tree |
up(n) |
Go up n levels in the tree |
parent() |
Get the parent node |
Common mistake: Duplicate node creation¶
Warning
When defining multiple subcommands, avoid calling the same node-creating method (like .key()) multiple times on
the same parent. Each call creates a new node instead of reusing an existing one.
Incorrect example:
// Wrong! This creates TWO separate "test" nodes under root!
tree.getRoot().key("test").key("sub1");
tree.getRoot().key("test").key("sub2"); // .key("test") is called on root twice
This code appears to create /mycommand test sub1 and /mycommand test sub2, but actually creates a broken tree
structure with two separate "test" branches. The command may parse incorrectly even without throwing errors.
Correct approaches:
Store the intermediate node reference and reuse it:
var test = tree.getRoot().key("test");
test.key("sub1").exec(context -> { /* ... */ });
test.key("sub2").exec(context -> { /* ... */ });
Or use .up() to navigate back:
tree.getRoot()
.key("test")
.key("sub1")
.exec(context -> { /* ... */ })
.up() // Go back to "test" node
.key("sub2")
.exec(context -> { /* ... */ });
Or use .root() with the full path again only when you need a completely different branch:
tree.getRoot()
.key("test")
.key("sub1")
.exec(context -> { /* ... */ })
.root() // Go back to root
.key("other") // Different branch, not "test"
.exec(context -> { /* ... */ });
Restrict command to specific senders¶
Sometimes you want a command to only be executable by players, or only from the server console.
Use SenderType to restrict who can execute a command:
When using
SenderType, the executor receives a typed sender as the second parameter.This command can only be executed by players. Console will receive an error message.
Available sender types¶
| SenderType | Description |
|---|---|
SenderType.ANY |
Any sender (default) |
SenderType.PLAYER |
Only players can execute |
SenderType.ACTUAL_PLAYER |
Only real players (not fake players) |
SenderType.ENTITY |
Only entities can execute |
SenderType.SERVER |
Only server console can execute |
Add permissions to command nodes¶
Besides the main command permission, you can add permissions to specific command nodes. This is useful for hiding subcommands from players who don't have access:
Players without
myplugin.command.adminpermission won't see or be able to use the/mycommand adminsubcommand.
Handle command errors¶
When a command fails, you should return context.fail() and optionally add an error message:
Built-in error helper for "no targets matched" error.
Custom error message (will be displayed in red).
Built-in error helpers¶
| Method | Description |
|---|---|
addNoTargetMatchError() |
No entities matched the selector |
addTooManyTargetsError() |
Too many entities matched |
addPlayerNotFoundError() |
Player not found |
addSyntaxError() |
Syntax error at current argument |
addInvalidExecutorError(SenderType) |
Wrong sender type |
addError(message, args...) |
Custom error message (red) |
addOutput(message, args...) |
Normal output message |
Using enum classes¶
For cleaner code, you can use Java enums directly with enumClass():
The enum values will be automatically converted to command options.
The result is already typed as the enum class, no manual parsing needed.
Advanced: Server-side only commands¶
Some commands should not be sent to the client (e.g., they conflict with client-side commands).
Override isServerSideOnly() to hide the command from clients:
Advanced: Debug commands¶
You can mark a command as a debug command, which makes its name appear blue in the client:
Conclusion¶
You've learned how to:
- Create and register commands
- Add parameters with various types
- Create subcommands with tree navigation
- Restrict commands to specific sender types
- Handle errors properly
- Use enum classes for cleaner code
For more examples, check the built-in commands in the Allay source code.