Creating Commands

The Sponge API contains a robust set of command interfaces in order to allow developers to specify command handling.

Writing a Command

The first step is to create a class for the command. The class has to implement the interface CommandCallable:

package example.command;

import org.spongepowered.api.util.command.CommandCallable;

public class MyBroadcastCommand implements CommandCallable {

    // Body of the command goes here

}

Your IDE should be throwing a couple of errors at you, so let’s keep going.

The CommandCallable Lifecycle

Unlike what you might expect, CommandCallable does not represent a specific invocation of a command. Instead, it is instantiated once and passed into a command service, which then calls it every time that command is invoked. (We will get to registering your command towards the end of this tutorial.)

A lot of commands require holding an instance to the server when performing their behavior. We can do this by adding it as a constructor parameter and private variable:

private final Server server;

public MyBroadcastCommand(Server server) {
    this.server = server;
}

This private server variable will be referenced later on.

Describing Your Command

Now that we have our command class set up, let’s start describing our command. This is one of the most important functions of a command (other than its actual behavior), because it describes the command, what it does, and how to use it. Commands are associated with three strings describing their usage:

The Usage String

The usage string is an extremely short description of the command, describing sub-commands, arguments, and other properties. It is displayed when a command is run incorrectly, or when asked for explicitly.

It typically looks something like this:

/sponge conf [-g] [-d dim] [-w world] key value

The usage string is the only required property, and is specified by implementing the getUsage() method:

public Optional<String> getShortDescription() {
    return Optional.absent();
}

The Short Description

The short description is a description of the command that is typically one sentence long. It is usually displayed next to the command in a help listing. Unlike the usage string, it is written in English. Also unlike the usage string, it is optional.

The short description is specified by implementing the getShortDescription() method. If your command does not provide a description, use Optional.absent():

public Optional<String> getUsage() {
    return Optional.absent();
}

And if it provides a description, create an Optional using Optional.of():

public Optional<String> getShortDescription() {
    return Optional.of("Description goes here");
}

The Help Text

The help text is a full description of your command and how to use it. It is shown when /help yourcommand is run. Like the short description, it is optional.

Specifying the help text is similar to how the short description is specified:

public Optional<String> getHelp() {
    return Optional.of(
        "My very long help text which is extremely informative and allows
        people to use the command effectively goes here");
}

Command Permissions

Almost every useful command should be restricted by permissions somehow – a server owner doesn’t want the average Bob or Alice to run the /fireball or /broadcast commands, for obvious reasons.

Testing for permissions is required by the CommandCallable interface via the testPermission method:

public boolean testPermission(CommandSource source) {
    return source.hasPermission("example.mycommand");
}

Tip

To learn how to use the Permissions API with more of its features, please see the Working with the Permissions API guide.

Command Behavior

Finally, let’s get to the meat of our command – the behavior. To do this, we implement the call method on CommandCallable for the behavior of the command.

Since this command doesn’t do much, our implementation is very simple:

public boolean call(CommandSource source, String arguments,
    List<String> parents) throws CommandException {

    Message message = Messages.of(arguments);
    this.server.broadcastMessage(message);

    return true;
}

We simply make a message out of the passed-in arguments and broadcast it to the server.

The first parameter given to call is the CommandSource – this can be a player, the console, a command block, or a remote source (Rcon or some developer-defined source). The second parameter is a string of the arguments passed into the command.

The final parameter is a list of parents to the command, starting with the root command. Most commands don’t worry about this, so it is out of scope for this tutorial.

Tip

If you wish to learn about command parenting in Sponge, see the guide.

The command throws CommandException by default. You can use this error to throw messages related to how the command is handled, for instance, if the console attempts to run a command that only a player can run. Be careful about throwing CommandException; if something goes wrong with the actual execution of your command, you most likely need to throw another exception, like an InvalidArgumentsException.

Finally, the command returns a boolean indicating whether it passed or failed. It is not possible to return a boolean if an exception was thrown, Sponge will assume the command failed because of the exception.

Completion Suggestions

While a user is typing your command, they may press “tab” in order to try to autocomplete some parts of it, in order to speed up entering the command. We can customize this by implementing the getSuggestions method, like so:

public List<String> getSuggestions(CommandSource source, String arguments)
    throws CommandException {

    if (arguments.equalsIgnoreCase("H")) {

        ArrayList<String> suggestions = new ArrayList<String>();
        suggestions.add("Hi!");
        suggestions.add("Hello World!");

        return suggestions;

    } else {
        return Collections.emptyList();
    }
}

Like the call method, getSuggestions also takes a CommandSource and a string for the arguments. The CommandSource is the exact same as call – the source of the command. The arguments are almost the same, except they aren’t the completed arguments like call, they are whatever the CommandSource has currently typed in. Keep this in mind when writing your autocomplete code.

Also like call, getSuggestions throws a CommandException something related to command handling fails. Again, be sure to throw this exception only when necessary and use different or more specific classes for other, unrelated exceptions.

Return a list of strings as suggestions, or Collections.emptyList().

Command Example

Here is what we have so far:

package example.command;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.spongepowered.api.Server;
import org.spongepowered.api.text.message.Message;
import org.spongepowered.api.text.message.Messages;
import org.spongepowered.api.util.command.CommandCallable;
import org.spongepowered.api.util.command.CommandException;
import org.spongepowered.api.util.command.CommandSource;

import com.google.common.base.Optional;

// Simple command that sends a message to all players: /broadcast <message>
public class MyBroadcastCommand implements CommandCallable {

    private final Server server;

    private final Optional<String> desc = Optional.of(
        "Displays a message to all players");

    private final Optional<String> help = Optional.of(
        "Displays a message to all players. It has no color support!");

    public MyBroadcastCommand(Server server) {
        this.server = server;
    }

    // Called when a CommandSource (player, console, ...) executes the command
    public boolean call(CommandSource source, String arguments,
        List<String> parents) throws CommandException {

        // Creates a message from the command arguments
        Message message = Messages.of(arguments);

        // Sends the message to all players
        this.server.broadcastMessage(message);

        // Indicates that the command was successful
        return true;
    }

    // Tests if the CommandSource is permitted to run this command
    public boolean testPermission(CommandSource source) {
        return source.hasPermission("example.broadcast");
    }

    // Displayed when a player uses /help, if the help system supports it
    public Optional<String> getShortDescription() {
        return desc;
    }

    // A longer help text
    public Optional<String> getHelp() {
        return help;
    }

    // A string explaining the arguments of the command
    public String getUsage() {
        return "<message>";
    }

    // A list of suggestions when a player presses TAB to complete the command
    public List<String> getSuggestions(CommandSource source, String arguments)
        throws CommandException {

        // Checks if a player entered "h" or "H"
        if (arguments.equalsIgnoreCase("H")) {

            // Create a list of suggestions starting with "H"
            ArrayList<String> suggestions = new ArrayList<String>();
            suggestions.add("Hi!");
            suggestions.add("Hello World!");

            // Return the list
            return suggestions;

        } else {
            // Otherwise return no suggestions
            return Collections.emptyList();
        }
    }
}

Tip

See the documentation for CommandCallable for the purposes of each method in this example.

Registering the Command

Now we can register the class in the CommandService. The CommandService stands as the manager for watching what commands get typed into chat, and redirecting them to the right command handler. To register your command, use the method CommandService.register(), passing your plugin, an instance of the command, and any needed aliases as parameters.

CommandService cmdService = game.getCommandDispatcher();
cmdService.register(plugin, new MyBroadcastCommand(server), "message", "broadcast");

Note

The arguments after the new instance of your command are the aliases to register for the command. You can add as many Strings as you want. The first alias that isn’t used by another command becomes the primary alias. This means aliases used by another command are ignored.