Creating your first command
At its heart, all commands are created by using the @Command
annotation on a class. You then declare the
command name as a parameter in the annotation:
@Command("firstcommand")class MyFirstCommand {
}
And that is all you need for a simple command. This one obviously doesn’t do anything yet, but before we add some logic, let’s add some metadata.
Adding metadata
Section titled “Adding metadata”Paper supports registering a command with aliases and description. StrokkCommands supports adding those with
the additional, optional annotations @Aliases
and @Description
.
@Command("firstcommand")@Aliases("fc")@Description("My first StrokkCommands command!")class MyFirstCommand {
}
Command logic (Execute methods)
Section titled “Command logic (Execute methods)”Command logic is declared by using the @Executes
annotation on a method. This method has to be at least
package-private. You cannot mark the method as private
. Adding a top-level executes path (with no arguments)
looks like this:
@Command("firstcommand")@Aliases("fc")@Description("My first StrokkCommands-command!")class MyFirstCommand {
@Executes void onExecute(CommandSender sender) { sender.sendRichMessage("<#f29def>Hey there! You just executed your first command ^-^"); }}
Each method requires you to add a CommandSender
parameter. If you don’t do that, you will get a compile-time error.
This parameter describes the command sender of the command. In Brigadier terms, this is the object you get
from calling ctx.getSource().getSender()
.
Player executors
Section titled “Player executors”If your command requires a player to execute it, you have two ways to get it:
instanceof
-cast the sender:
@Executesvoid onExecute(CommandSender sender) { if (!(sender instanceof Player player)) { sender.sendRichMessage("<red>This command requires a player!"); return; }
player.sendRichMessage("<#f29def>Hey there! You just executed your first command ^-^");}
- Add an
Player
parameter using the@Executor
annotation:
@Executesvoid onExecute(CommandSender sender, @Executor Player player) { player.sendRichMessage("<#f29def>Hey there! You just executed your first command ^-^");}
Whilst both ways are acceptable, the second approach is the more ‘correct’ way.
The reason is that instead of casting the command sender, you directly get the Player
entity with executed the command.
This is a distinction from the /execute as
command. Using the /executes
command,
you can change the player for which the command is executed. All Vanilla commands use the executor
instead of the sender for this exact reason, so for parity reasons, you should do as well.
If the command was run by a Player
, player == sender
will evaluate to true
, false
, if not.
Adding a literal (subcommand)
Section titled “Adding a literal (subcommand)”You can declare top-level literals (also referred to as subcommands) by adding a parameter to the @Executes
annotation.
If you want a path called /fc fling
, you’d add this method:
@Command("firstcommand")@Aliases("fc")@Description("My first StrokkCommands-command!")class MyFirstCommand {
@Executes void onExecute(CommandSender sender) { sender.sendRichMessage("<#f29def>Hey there! You just executed your first command ^-^"); }
@Executes("fling") void onFling(CommandSender sender, @Executor Player player) { player.setVelocity(player.getVelocity().add(new Vector(0, 10, 0))); player.sendRichMessage("<b><#c4e6ff>WOOSH</b> <#c4fffd>You've been flung!"); }}
As you can see, declaring the command is incredibly easy! But you (probably) don’t yet know how to register the command to test it out.
Registering the command
Section titled “Registering the command”Command registration works the exact same way as with Paper: by using the Lifecycle API. The command registration page on the Paper docs can also provide some important insight, but for beginners, the page can be a bit complex.
To register your command with StrokkCommands, you have to do the following steps:
Compiling the project
Section titled “Compiling the project”Wait, didn’t we skip a few steps? Nope! In order to be able to register the command, you have to first compile the project. The reason for this is so that the annotation processor can run and generate the required source files for your command.
The source file will always be generated in the same package as the command declaration. Its file name will always be
<command_class_name>Brigadier.java
. That means for your.package.MyCommand
, the new class name will be
your.package.MyCommandBrigadier
.
The new source file will be generated in a separate sources root which is managed by your build system.
For Gradle, the generated source files are located under /build/generated/sources/annotationProcessor
.
Actually registering the command
Section titled “Actually registering the command”Now that we have our Brigadier source file, we can register it.
Wherever you want to register your command, you can get the lifecycle event manager and register a new commands event handler. For the plugin’s main class, this would look like this:
public void onLoad() { this.getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS.newHandler(event -> { // Register the command with the description and aliases declared as annotations: MyFirstCommandBrigadier.register(event.registrar());
// Register the command with your own provided description and aliases: event.registrar().register(MyFirstCommandBrigadier.create(), "description", List.of(/* aliases */)); }));}
The generated Brigadier source file has two static methods for retrieving the build command tree and registering it. It is up to you which approach you prefer, either work just fine.
Testing the command out in-game
Section titled “Testing the command out in-game”You should now be able to spin up a test server with your plugin and see your command in action!