Skip to content

Using Chimera Commands

Matthias Ngeo edited this page Aug 6, 2022 · 41 revisions

Overview

This guide provides a walkthrough of the creation of a tell command using Chimera's Builders. If you prefer to use annotations, please view using annotations instead.

⚠️ Do not define commands in the plugin.yml: Due to changes in Spigot 1.19, doing so will cause commands to be unregistered

Our tell command allows a player to send a message to one or more other players. It's syntax is /tell|t <players> <messages>. Suggestions are provided for each argument. For <player> and <players>, it's the names of visible, online players. For <messages>, it's Hello World!. Lastly, the message, Hello darkness my old friend is displayed if no arguments are given.

Setting up the project

To use Chimera, include the commons library in your project's pom.xml

releases-maven snapshots-maven javadoc

<repositories>         
    <repository>
        <id>chimera-releases</id>
        <url>https://repo.karuslabs.com/repository/chimera-releases/</url>
    </repository>

    <repository>
        <id>minecraft-libraries</id>
        <url>https://libraries.minecraft.net</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>com.mojang</groupId>
        <artifactId>brigadier</artifactId>
        <version>1.0.17</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>com.karuslabs</groupId>
        <artifactId>commons</artifactId>
        <version>5.1.0</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

Creating the tell command

A command is represented by either a Literal or LiteralCommandNode. Both can contain an arbitrary number of arguments or child commands. It is recommended to use Literal as it may contain aliases and optional children.

A Literal can be created through a builder returned by Literal.<CommandSender>builder(name) or Literal.of(name).

import com.karuslabs.commons.command.tree.nodes.Literal;

Literal<CommandSender> tell = Literal.of("tell").alias("t")
                                     .then(arguments) // covered in the next section
                                     .build();

Creating the <player>, <players> and <message> arguments

WARNING: It is recommended to use WordType.word() instead of StringArgument.word(), and Readers.unquoted(StringReader) instead of StringReader.readUnquoted(). See the FAQ for more information.

An argument is represented by either a Argument or ArgumentCommandNode. Both contain an ArgumentType that is used to resolve a string to a type. An SuggestionProvider can also optionally be used to provide completion suggestions. It is recommended to use Argument.

An Argument can be created through a builder returned by Argument.<CommandSender>builder(name, type) or Argument.of(name, type).

<player> | <players>:

import com.karuslabs.commons.command.tree.nodes.Argument;
import com.karuslabs.commons.command.types.PlayersType;

// Inbuilt players type provided by Chimera
Argument.Builder<CommandSender, List<Player>> players = Argument.of("players", new PlayersType()); 

<message>:

import com.karuslabs.commons.command.tree.nodes.Argument;
import com.mojang.brigadier.arguments.StringArgumentType;

// Inbuilt string type provided by Mojang
Argument.Builder<CommandSender, String> message = Argument.of("message", StringArgumentType.string()) 
                                                          .suggests((c, builder) -> builder
                                                              .suggest("\"Hello World!\"").buildFuture()
                                                           );

Chaining the arguments:

Argument<CommandSender, List<Player>> arguments = players.then(message).build(); // Chains player and message

Defining a command's behaviour

Until now, nothing will happen if the tell command is executed since it does not contain any behaviour. To define a command's behaviour, we must use either an Execution<T> or Command. It is recommend to use Execution` since it allows optional arguments, and provides the source as a parameter.

The last literal or argument in a chain is always executed, e.g. given the input /tell Bob "I'm a banana", the Execution<T> bound to the Argument that represents <message> will be executed.

Editing the Literal in the previous section yields:

import com.karuslabs.commons.command.Execution;
        

Literal<CommandSender> tell = Literal.of("tell").alias("t")
                                     // Add the following
                                     .executes(context -> 
                                        context.getSource().sendMessage("Hello darkness my old friend")) 
                                     .then(message)
                                     .build();

Editing the Argument that represents in the previous section yields:

Argument.Builder<CommandSender, String> message = Argument.of("message", StringArgumentType.string())
                                                          .executes(context -> { // <-- Add this
                                                              CommandSender sender = context.getSource();
                                                              List<Player> recipents = context.getArgument("players", List.class);
                                                              String entered = context.getArgument("message", String.class);   
                                                              
                                                              recipents.forEach(p -> p.sendMessage(sender + " says: " + entered));
                                                           })
                                                          .suggests((context, builder) -> builder.suggest("\"Hello World!\"").buildFuture());

Registering a command

The final step after creating the command is to register it with a Disptcher. Commands not registered in either JavaPlugin.onLoad() or JavaPlugin.onEnable() will only be available after Dispatcher.update() is called.

import com.karuslabs.commons.command.dispatcher.Dispatcher;

Dispatcher dispatcher = Dispatcher.of(yourPlugin);
dispatcher.getRoot().addChild(tell);
// Only needed if called outside JavaPlugin.onLoad() or JavaPlugin.onEnable()
dispatcher.update(); 

Putting it together

In the end, our tell command should look like:

import com.karuslabs.commons.command.Exeuction;
import com.karuslabs.commons.command.dispatcher.Dispatcher;
import com.karuslabs.commons.command.tree.nodes.Literal;
import com.karuslabs.commons.command.tree.nodes.Argument;
import com.karuslabs.commons.command.types.PlayersType;
import com.mojang.brigadier.arguments.StringArgumentType;        
            
Argument.Builder<CommandSender, List<Player>> players = Argument.of("players", new PlayersType()); // Inbuilt players type provided by Chimera
Argument.Builder<CommandSender, String> message = Argument.of("message", StringArgumentType.string()) // Inbuilt string type provided by Mojang
                                                          .executes(context -> { 
                                                              CommandSender sender = context.getSource();
                                                              List<Player> recipents = context.getArgument("players", List.class);
                                                              String entered = context.getArgument("message", String.class);   
                                                              
                                                              recipents.forEach(p -> p.sendMessage(sender.getName() + " says: " + entered));
                                                           })
                                                          .suggests((context, builder) -> builder.suggest("\"Hello World!\"").buildFuture());
        
Argument<CommandSender, List<Player>> arguments = players.then(message).build();

Literal<CommandSender> tell = Literal.of("tell").alias("t")
                                     .executes(context -> context.getSource().sendMessage("Hello darkness my old friend"))
                                     .then(arguments)
                                     .build();

Dispatcher dispatcher = Dispatcher.of(yourPlugin);
dispatcher.getRoot().addChild(tell);
// Only needed if called outside JavaPlugin.onLoad() or JavaPlugin.onEnable()
dispatcher.update();

Taking the tell command for a spin

We have completed creating our tell command and it's time to test it.

Entering /tell in-game yields:

Partial Command Execution

Entering /tell "@r, PanteLegacy" "Hello World!" in-game yields:

Full Command Execution

Where to next?