Introduction

Welcome to the documentation for the CommandAPI. The CommandAPI lets you create vanilla Minecraft commands which utilize the new command features which were implemented in Minecraft 1.13, including but not limited to:

  • Having commands compatible with the vanilla /execute command
  • Having commands which can be run using Minecraft functions
  • Having better auto-completion and suggestions
  • Having command type checks before execution (e.g. ensuring a number is within a certain range)

How the CommandAPI works

Developer's Note:

This is a pretty important section, I would recommend reading before implementing the CommandAPI in your own projects. This section tells you about setup which is not stated anywhere else in the documentation. Think of it as the "knowledge you should know before using this API".

The CommandAPI does not follow the "standard" method of registering commands. In other words, commands which are registered with the CommandAPI will be registered as pure vanilla Minecraft commands as opposed to Bukkit or Spigot commands. This means that the following implications exist:

  • Commands do not need to be declared in the plugin.yml file
  • Commands are not "linked" to a certain plugin. In other words, you cannot look up which commands are registered by which plugin.

How this documentation works

This documentation is split into the major sections that build up the CommandAPI. It's been designed in such a way that it should be easy to find exactly what you want to help you get started with the CommandAPI, how it's structured and how to make effective use of it. Each step of the way, the documentation will include examples which showcase how to use the CommandAPI.

You can use the side bar on the left to access the various sections of the documentation and can change the theme to your liking using the paintbrush icon in the top left corner.

Using the search icon in the top left corner, typing "Example" will show a list of examples which are included throughout the documentation.

Documentation changelog

Whenever a new version of the CommandAPI comes out, the version number changes (as you'd expect). In the same manner, if any changes to the documentation were made, the documentation version number changes. Ensure you keep up to date on the latest changes to the documentation (You can view the documentation version at the top of the page) when new versions of the CommandAPI are released. This changelog below gives a brief overview of the changes to pages that were made between each version of the documentation, as only the latest version of the documentation is hosted online.

  • 2.0 → 2.1
    • Click here - Include information about tooltips
    • Click here - Adds information on technical arguments
    • Click here - Improve documentation for adding dependencies and repositories to the pom.xml file
  • 1.8 → 2.0
    • Click here - Deprecated SuggestedStringArgument, use .overrideSuggestions() instead
    • Click here - Adds a new argument, the CustomArgument
    • Click here - The DynamicSuggestedArgument now has access to the CommandSender object

Installation

Installing the CommandAPI is easy! Just download the CommandAPI.jar file and place it in your server's plugins/ folder!


Download latest CommandAPI.jar

Configuring the CommandAPI

The CommandAPI can be configured in the plugins/CommandAPI/config.yml file.

The default config.yml settings are as follows:

verbose-outputs: true
create-dispatcher-json: true
  • verbose-outputs - Outputs command registration and unregistration logs in the console
  • create-dispatcher-json - Creates a command_registration.json file showing the mapping of registered commands

Setting up your development environment

To use the CommandAPI in your plugins, there are two methods of adding it to your development environment:

Manually using the .jar

  • Download the latest CommandAPI.jar from the download page here
  • Add the CommandAPI.jar file to your project/environment's build path
  • Add the plugin as a dependent in the plugin.yml (depend: [CommandAPI])

Using Maven (recommended)

Developer's Note:

If you've never used maven before, I highly recommend it! It makes it easier to keep your code updated with the latest dependency updates. For information on how to set up a plugin using maven, you can read Bukkit's plugin tutorial.

  • Add the maven repository to your pom.xml file:

    <repositories>
        <repository>
            <id>mccommandapi</id>
            <url>https://raw.githubusercontent.com/JorelAli/1.13-Command-API/mvn-repo/1.13CommandAPI/</url>
        </repository>
    </repositories>
    
  • Add the dependency to your pom.xml:

    <dependencies>
        <dependency>
            <groupId>io.github.jorelali</groupId>
            <artifactId>commandapi</artifactId>
            <version>VERSION</version>
        </dependency>
    </dependencies>
    

    A list of version numbers can be found here. For example, if you wanted to use version 2.0, you would use <version>2.0</version>

  • Add the plugin as a dependent in the plugin.yml (depend: [CommandAPI])

Using Gradle

  • Add the repository to your build.gradle file:

    repositories {
        maven {
            name = 'mccommandapi'
            url = 'https://raw.githubusercontent.com/JorelAli/1.13-Command-API/mvn-repo/1.13CommandAPI/'
        }
    }
    
  • Add the dependency to your list of dependencies in your build.gradle file:

    dependencies {
        compile "io.github.jorelali:commandapi:VERSION"
    }
    

    A list of version numbers can be found here. For example, if you wanted to use version 2.0, you would use compile "io.github.jorelali:commandapi:2.0"

  • Add the plugin as a dependent in the plugin.yml (depend: [CommandAPI])

Command registration

To register commands with the CommandAPI, there are several methods which can be used, depending on whether you want your command to have aliases or permissions in order to run it.

CommandRegistration method Outcome
CommandAPI.getInstance().register(String, LinkedHashMap, CommandExecutor) Basic command registration
CommandAPI.getInstance().register(String, String[], LinkedHashMap, CommandExecutor) Register command with an array of aliases
CommandAPI.getInstance().register(String, CommandPermission, LinkedHashMap, CommandExecutor) Register command which need certain permissions
CommandAPI.getInstance().register(String, CommandPermission, String[], LinkedHashMap, CommandExecutor) Register command with aliases and permission requirements

The following fields are as follows:

  • String - The command name

    The first argument represents the command name which will be registered. For instance, to register the command /god, you would use the following:

    CommandAPI.getInstance().register("god", ...);
    
  • LinkedHashMap<String, Argument> - The list of arguments

    The CommandAPI requires a list of arguments which are used for the command. The argument map consists of a key which is the tooltip that is displayed as a prompt to users entering commands, and a value which is an instance of an argument (See the section on arguments). This list of arguments is interpreted in the order that arguments are added to the LinkedHashMap.

  • String[] - An array of aliases that the command can be run via

  • CommandPermission - The required permission to execute a command. (See the section on permissions).

Command loading order

In order to register commands properly, commands must be registered before the server finishes loading. The CommandAPI will prevent command registration after the server has loaded. This basically means that all command registration must occur during a plugin's onLoad() or onEnable() method. With the CommandAPI, depending on which of these functions you load your commands is crutial if your plugin is used with Minecraft's functions.

When to load What to do
onLoad() method Register commands to be used in Minecraft functions (see Function section for more info)
onEnable() method Register regular commands

Command unregistration

The CommandAPI has support to unregister commands completely from Minecraft's command list. This includes Minecraft built in commands!

Method Result
CommandAPI.getInstance().unregister(String cmd) Unregisters a command from the game
CommandAPI.getInstance().unregister(String cmd, boolean force) Attempts to unregister a command from the game by force. This includes /minecraft:cmd, /bukkit:cmd and /spigot:cmd commands as well.

Example - Replacing Minecraft's /gamemode command

To replace a command, we can first unregister it and then register our implementation of that command.

//Unregister the gamemode command from the server (by force)
CommandAPI.getInstance().unregister("gamemode", true);

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();

/* Arguments for the gamemode command. In this sample, I'm just 
 * using a simple literal argument which allows for /gamemode survival */
arguments.put("gamemode", new LiteralArgument("survival"));

CommandAPI.getInstance().register("gamemode", arguments, (sender, args) -> {
    //Implementation of our /gamemode command
});

Developer's Note:

Command unregistration, although powerful, is highly unrecommended. It is the CommandAPI's most "dangerous" feature as it can cause unexpected sideffects, such as command blocks executing commands you wouldn't expect them to. In almost every case, I'd recommend just creating a new command instead of unregistering one to replace it.

For instance, instead of unregistering /gamemode, you could register a command /gm or /customgamemode.

Command executors

Developer's Note:

This section can be a little bit difficult to follow. If you only want the bare basic features (executes a command), read the section below on Normal command executors - this behaves very similar to the onCommand method in Bukkit.


The CommandAPI provides two separate command executors which are lambdas which execute the code you want when a command is called. These are the classes CommandExecutor (Not to be confused with Bukkit's CommandExecutor class), which just runs the contents of a command, and ResultingCommandExecutor that returns an integral (whole number) result.

Developer's Note:

In general, you need not focus too much on what type of command executor to implement. If you know for certain that you're going to be using your command with command blocks, just ensure you return an integer at the end of your declared command executor. Java will infer the type (whether it's a CommandExecutor or ResultingCommandExecutor) automatically, so feel free to return an integer or not.

Normal command executors

Command executors are of the following format, where sender is a CommandSender, and args is an Object[], which represents arguments which are parsed by the CommandAPI.

(sender, args) -> {
  //Code here  
};

With normal command executors, these do not need to return anything. By default, this will return a success value of 1 if it runs successfully, and a success value of 0 if it runs unsuccessfully, either by throwing an exception (RuntimeException) or by forcing the command to fail.

Forcing commands to fail

Sometimes, you want your command to fail on purpose. This is basically the way to "gracefully" handle errors in your command execution. This is performed using the following method:

CommandAPI.fail("Error message goes here");

When the CommandAPI calls the fail method, it will cause the command to return a success value of 0, to indicate failure.

Example - Command failing for element not in a list

Say we have some list, List<String> containing a bunch of fruit and the player can choose from it. In order to do that, we can use a StringArgument and suggest it to the player using .overrideSuggestions(String[]). However, because this only lists suggestions to the player, it does not stop the player from entering an option that isn't on the list of suggestions.

Therefore, to gracefully handle this with a proper error message, we use CommandAPI.fail(String) with a meaningful error message which is displayed to the user.

//Array of fruit
List<String> fruit = new ArrayList<>();
fruit.add("banana");
fruit.add("apple");
fruit.add("orange");

//Argument accepting a String, suggested with the list of fruit
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("item", new StringArgument().overrideSuggestions(fruit.toArray(new String[fruit.size()])));

//Register the command
CommandAPI.getInstance().register("getfruit", arguments, (sender, args) -> {
    String inputFruit = (String) args[0];
    if(fruit.contains(inputFruit)) {
        //Do something with inputFruit
    } else {
        //The player's input is not in the list of fruit
        CommandAPI.fail("That fruit doesn't exist!");
    }
});

Developer's Note:

In general, it's a good idea to handle unexpected cases with the CommandAPI.fail() method. Most arguments used by the CommandAPI will have their own builtin failsafe system (e.g. the EntitySelectorArgument will not execute the command executor if it fails to find an entity), so this feature is for those extra cases.

Proxied commandsenders

The CommandAPI has extra support for vanilla Minecraft's /execute command, by allowing the CommandSender to be an instance of the ProxiedCommandSender class. This allows the CommandSender to contain two extra pieces of information: The "proxied sender" and the original sender.

Example - Running a command as a chicken

Say we have a command which kills the sender of a command. This is easily implemented as follows:

CommandAPI.getInstance().register("killme", new LinkedHashMap<String, Argument>(), (sender, args) -> {
    if(sender instanceof Player) {
        Player player = (Player) sender;
        player.setHealth(0);
    }
});

But what if the sender of the command is not a player? By using Minecraft's /execute command, we could execute the command as any arbitrary entity, as shown with the command below:

/execute as @e[type=chicken] run killme

To handle this case, we can check if the sender is an instance of a ProxiedCommandSender, and kill the callee (the entity which is being 'forced' to run the command /killme)

CommandAPI.getInstance().register("killme", new LinkedHashMap<String, Argument>(), (sender, args) -> {
    if(sender instanceof Player) {
        Player player = (Player) sender;
        player.setHealth(0);
    } else if(sender instanceof ProxiedCommandSender) {

        //Get the proxy CommandSender object from the CommandSender
        ProxiedCommandSender proxy = (ProxiedCommandSender) sender;

        //Check if the callee is an Entity
        if(proxy.getCallee() instanceof Entity) {

            //If so, kill the entity
            Entity target = (Entity) proxy.getCallee();
            entity.setHealth(0);
        }
    }
});

This allows the command above to run successfully, killing all chickens it can find.

Resulting command executors

Resulting command executors are very similar to normal command executors, the only difference is that they can return an integer result value.

(sender, args) -> {
    //Code here
    return /*some integer here*/ ;
};

Example - Random number result command

Say we want a command that returns a random number as a result. This can then be used by vanilla Minecraft's /execute store result ... command, which can be used for other command block chains.

CommandAPI.getInstance().register("randnum", new LinkedHashMap<String, Argument>(), (sender, args) -> {
    return new Random().nextInt();
});

This returns a success value of 1 (Because no errors or CommandAPI.fail(String) was thrown) and a return value of a random number.

Example - Lootbox system with /execute command

We can store state using /execute store and we can perform conditional checks using /execute if. By combining these, we can create a system which can be used with commandblocks to say, give players random lootboxes and redeem them.

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();

//Register random number generator command from 1 to 99 (inclusive)
CommandAPI.getInstance().register("randomnumber", arguments, (sender, args) -> {
    return ThreadLocalRandom.current().nextInt(1, 100); //Returns random number from 1 <= x < 100
});

//Register reward giving system for a target player
arguments.put("target", new EntitySelectorArgument(EntitySelector.ONE_PLAYER));

CommandAPI.getInstance().register("givereward", arguments, (sender, args) -> {
    Player player = (Player) args[0];
    player.getInventory().addItem(new ItemStack(Material.DIAMOND, 64));
    Bukkit.broadcastMessage(player.getName() + " won a rare 64 diamonds from a loot box!");
}

Store a random number under the scoreboard score randVal for a player called SomePlayer, by executing the command /randomnumber

/execute store success score SomePlayer randVal run randomnumber

Check if the random number is equal to 1 (say we have some plugin which gives a reward to a player that happened to get a 1 from a random number command, thus giving them a 1/99 chance of winning)

/execute if score SomePlayer randVal matches 1 run givereward SomePlayer

Arguments

Arguments in the CommandAPI are registered by using a LinkedHashMap<String, Argument> object. There are two things you need to keep in mind when creating arguments:

  • The order which they will be used
  • The type of each argument

By definition of a LinkedHashMap, the order of the elements inserted into it are preserved, meaning the order you add arguments to the LinkedHashMap will be the resulting order of which arguments are presented to the user when they run that command.

Adding arguments for registration is simple:

//Create LinkedHashMap
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();

//Add an argument called "target", which is a PlayerArgument
arguments.put("target", new PlayerArgument());

The String value is the tooltip that is shown to a player when they are entering the command.

Argument Casting

To access arguments, they have to be casted to the type that the argument represents. The order of the arguments in the args[] is the same as the order in which the arguments were declared.

LinkedHashMap<String, ArgumentType> arguments = new LinkedHashMap<>();
arguments.put("arg0", new StringArgument());
arguments.put("arg1", new PotionEffectArgument());
arguments.put("arg2", new LocationArgument());

CommandAPI.getInstance().register("cmd", arguments, (sender, args) -> {
    String stringArg = (String) args[0];
    PotionEffectType potionArg = (PotionEffectType) args[1];
    Location locationArg = (Location) args[2];
});

The types of the arguments declared by the CommandAPI are all listed below:

List of arguments

Arguments are found in the io.github.jorelali.commandapi.api.arguments package.

Argument class Data type Description
AdvancementArgument Advancement
BooleanArgument boolean
ChatColorArgument ChatColor
ChatComponentArgument BaseComponent[] Formatted chat object
DoubleArgument double
DynamicSuggestedStringArgument String Suggested string using a supplier function
EnchantmentArgument Enchantment
EntitySelectorArgument Entity, Player, Collection<Entity>, Collection<Player> Selects an entity (similar to @a or @p)
EntityTypeArgument EntityType Selects a type of entity (e.g. Pig)
FloatArgument float
FunctionArgument FunctionWrapper[] A declared Minecraft function from a data pack
GreedyStringArgument String A string of any length
IntegerArgument int
ItemStackArgument ItemStack Returns an ItemStack with amount 1
LiteralArgument N/A A predefined hardcoded argument name
LocationArgument Location
LootTableArgument LootTable
ParticleArgument Particle
PlayerArgument Player Similar to EntitySelector, but always returns 1 player
PotionEffectArgument PotionEffectType
RecipeArgument Recipe
SoundArgument Sound
StringArgument String String consisting of 1 word
SuggestedStringArgument String A list of suggested one word strings
TextArgument String String which can have spaces (used for text)

Arguments with overrideable suggestions

Some arguments have a feature allowing you to override the list of suggestions they provide. This is achieved by using .overrideSuggestions(String[]) on an instance of an argument, with the String array consisting of suggestions that will be shown to the user whilst they type their command. It's been designed such that this returns the same argument so it can be used inline (handy, eh?)

Example - Friend list by overriding suggestions

Say you have a plugin which has a "friend list" for players. If you want to teleport to a friend in that list, you could use a PlayerArgument, which has the list of suggestions overridden with the list of friends that that player has.

String[] friends = //Some String array populated with friends

LinkedHashMap<String, ArgumentType> arguments = new LinkedHashMap<>();
arguments.put("friend", new PlayerArgument().overrideSuggestions(friends));

CommandAPI.getInstance().register("friendtp", arguments, (sender, args) -> {
    Player target = (Player) args[0];
    Player player = (Player) sender;
    player.teleport(target);
});

Primitive arguments

Boolean arguments

The BooleanArgument class represents boolean values true and false.

Example - Config editing plugin

String[] configKeys = getConfig().getKeys(true).toArray(new String[getConfig().getKeys(true).size()]);

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("config-key", new TextArgument().overrideSuggestions(configKeys));
arguments.put("value", new BooleanArgument());

CommandAPI.getInstance().register("editconfig", arguments, (sender, args) -> {
    getConfig().set((String) args[0], (boolean) args[1]);
});

Numerical arguments

Numbers are represented using the designated number classes:

Class Description
IntegerArgument Whole numbers
DoubleArgument Double precision floating point numbers
FloatArgument Single precision floating point numbers

Each numerical argument can have ranges applied to them, which restricts the user to only entering numbers from within a certain range. This is done using the constructor, and the range specified:

Constructor Description
new IntegerArgument() Any range
new IntegerArgument(2) Values greater than or equal to 2
new IntegerArgument(2, 10) Values greater than or equal to 2 and less than or equal to 10

Each range is inclusive, so it includes the number given to it.

Location argument

The LocationArgument class is used to specify a location in the command sender's current world. This allows the user to enter three numbers as coordinates, or use relative coordinates (i.e. the ~ and ^ operators)

The LocationArgument constructor requires a LocationType, which specifies the type of location that is accepted by the command.

LocationType.BLOCK_POSITION

Integer block coordinates. The suggested location is the coordinates of block you are looking at when you type the command. BLOCK_POSITION

LocationType.PRECISE_POSITION

Exact coordinates. The suggested location is the exact coordinates of where your cursor is pointing at when you type the command. PRECISE_POSITION

By default, the LocationArgument will use PRECISE_POSITION.

Example - Break block by coordinates command

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();

//We want to target blocks in particular, so use BLOCK_POSITION
arguments.put("block", new LocationArgument(LocationType.BLOCK_POSITION));

CommandAPI.getInstance().register("break", arguments, (sender, args) -> {
    ((Location) args[0]).getBlock().setType(Material.AIR);
});

Chat arguments

Chat color argument

The ChatColorArgument class is used to represent a given chat color (e.g. red or green)

Example - Username color changing plugin

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("chatcolor", new ChatColorArgument());

CommandAPI.getInstance().register("namecolor", arguments, (sender, args) -> {
    Player player = (Player) sender;
    ChatColor color = (ChatColor) args[0];
    player.setDisplayName(color + player.getDisplayName());
});

Chat component argument

Developer's Note:

The ChatComponentArgument class is dependent on a Spigot based server. This means that the ChatComponentArgument will not work on a non-Spigot based server, such as CraftBukkit. If you use this class on a non-Spigot based server, it will throw a SpigotNotFoundException

Spigot based servers include, but are not limited to:

  • Spigot
  • PaperSpigot
  • TacoSpigot

The ChatComponentArgument class accepts raw JSON as valid input. This is converted into Spigot's BaseComponent[] which can be used for books and raw messages. You can read more about raw JSON here.

Example - Book made from raw JSON

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("contents", new ChatComponentArgument());

CommandAPI.getInstance().register("makebook", arguments, (sender, args) -> {
    if (sender instanceof Player) {
        Player player = (Player) sender;
        BaseComponent[] arr = (BaseComponent[]) args[0];
        
        //Create book
        ItemStack is = new ItemStack(Material.WRITTEN_BOOK);
        BookMeta meta = (BookMeta) is.getItemMeta(); 
        meta.spigot().addPage(arr);
        is.setItemMeta(meta);
        
        //Give player the book
        player.getInventory().addItem(is);
    }
});

String arguments

String argument

The StringArgument class is used to represent a single word. These words can only contain alphanumeric characters (A-Z, a-z and 0-9), and the underscore character.

Accepted StringArgument values:

Hello
123
hello123
Hello_world

Rejected StringArgument values:

hello@email.com
yesn't

Potential uses for string arguments

  • Entering Strings to identify offline players

Text argument

The TextArgument acts similar to any String in Java. These can be single words, like to the StringArgument, or have additional characters (e.g. spaces, symbols) if surrounded by quotes. To type quotation marks, you can use \" (as similar to Java) to escape these special characters.

Accepted TextArgument values:

hello
"hello world!"
"hello@gmail.com"
"this has \" <<-- speech marks! "

Rejected TextArgument values:

hello world
私
"speech marks: ""

Potential uses for text arguments

  • A command to edit the contents on a sign
  • Any command that may require multiple text arguments

Greedy string argument

The GreedyStringArgument takes the TextArgument a step further. Any characters and symbols are allowed and quotation marks are not required. However, the GreedyStringArgument uses the entirety of the argument array from its position.

Example - Messaging command

Say we have a command /msg <target> <message>

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("target", new PlayerArgument());
arguments.put("message", new GreedyStringArgument());

CommandAPI.getInstance().register("msg", arguments, (sender, args) -> {
    ((Player) args[0]).sendMessage((String) args[1]);
});

Any text entered after the <target> argument would be sent to the player. For example, the command could be used as follows:

/msg Skepter This is some incredibly long string with "symbols" and $p3c!aL characters~

Due to the fact that the GreedyStringArgument has no terminator (it has infinite length), a GreedyStringArgument must be defined at the end of the LinkedHashMap (otherwise the CommandAPI will throw a GreedyStringException)

For example, if the syntax was/msg <message> <target>, it would not be able to determine where the message ends and the <target> argument begins.

Potential uses for greedy strings

  • A messaging/whisper command
  • A mailing command
  • Any command involving lots of text, such as a book writing command
  • Any command which involves an unreasonable/unknown amount of arguments
  • Any command where you want to parse arguments similar to how regular Bukkit would

Entity & player arguments

Entity selector argument

Minecraft's target selectors (e.g. @a or @e) are implemented using the EntitySelectorArgument class. This allows you to select specific entities based on certain attributes.

The EntitySelectorArgument constructor requires an EntitySelector argument to determine what type of data to return. There are 4 types of entity selections which are available:

  • EntitySelector.ONE_ENTITY - A single entity, which returns a Entity object.
  • EntitySelector.MANY_ENTITIES - A collection of many entities, which returns a Collection<Entity> object.
  • EntitySelector.ONE_PLAYER - A single player, which returns a Player object.
  • EntitySelector.MANY_PLAYERS - A collection of players, which returns a Collection<Player> object.

The return type is the type to be cast when retrieved from the Object[] args in the command declaration.

Example - Kill entities command

//LinkedHashMap to store arguments for the command
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();

//Using a collective entity selector to select multiple entities
arguments.put("entities", new EntitySelectorArgument(EntitySelector.MANY_ENTITIES));

CommandAPI.getInstance().register("kill", arguments, (sender, args) -> {
    
    //Parse the argument as a collection of entities (as stated above in the documentation)
    Collection<Entity> entities = (Collection<Entity>) args[0];
    sender.sendMessage("killed " + entities.size() + "entities");
    for(Entity e : entity)
        e.remove();
});

Example command usage for the above code would be:

  • Kill all cows:
    /kill @e[type=cow]
    
  • Kill the 10 furthest pigs from the command sender:
    /kill @e[type=pig,limit=10,sort=furthest]
    

Player argument

The PlayerArgument class is very similar (almost identical) to EntitySelectorArgument, with the EntitySelector ONE_PLAYER. It also allows you to select a player based on their UUID.

Developer's Note:

I've not tested the PlayerArgument enough to recommend using it over the EntitySelectorArgument(EntitySelector.ONE_PLAYER). There may be other advantages to using this than the regular EntitySelectorArgument, but as of writing this documentation, I know not of the advantages nor disadvantages to using this argument type. Internally, the PlayerArgument uses the GameProfile class from Mojang's authlib, which may be able to retrieve offline players (untested).

Entity type argument

The EntityTypeArgument class is used to retrieve a type of entity as defined in the EntityType enum. In other words, this is an entity type, for example a pig or a zombie.

Example - Spawning entities

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();

arguments.put("entity", new EntityTypeArgument());
arguments.put("amount", new IntegerArgument(1, 100)); //Prevent spawning too many entities

CommandAPI.getInstance().register("spawnmob", arguments, (sender, args) -> {
    Player player = (Player) sender;
    for(int i = 0; i < (int) args[1]; i++) {
        player.getWorld().spawnEntity(player.getLocation(), (EntityType) args[0]);
    }
});

Literal arguments

Literal arguments are used to represent "forced options" for a command. For instance, take Minecraft's /gamemode command. The syntax consists of the following:

/gamemode <mode> [player]

It consists of a gamemode, followed by an optional player argument. The list of gamemodes are as follows:

/gamemode survival 
/gamemode creative
/gamemode adventure
/gamemode spectator

Unlike regular commands (as those implemented by Bukkit for example), these four options are "hardcoded" - they're not "suggestions". The user can only enter one of these four examples, no other values are allowed.

Literal arguments vs regular arguments

Unlike regular arguments that are shown in this chapter, the literal argument is technically not an argument. Due to this fact, the literal argument is not present in the args[] for the command declaration.

Example - Literal arguments and regular arguments

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("literal", new LiteralArgument("hello"));
arguments.put("text", new TextArgument());

CommandAPI.getInstance().register("mycommand", arguments, (sender, args) -> {
    /* This gives the variable "text" the contents of the 
     * TextArgument, and not the literal "hello" */
    String text = (String) args[0];
});

As you can see from this example, when you retrieve args[0], it returns the value of the TextArgument instead of the LiteralArgument

Example - Gamemode command using literal arguments

This is a demonstration of how you could create a command similar to Minecraft's /gamemode command by using literal arguments. To do this, we are effectively registering 4 separate commands, each called /gamemode, but with different literal arguments.

//Create a map of gamemode names to their respective objects
HashMap<String, GameMode> gamemodes = new HashMap<>();
gamemodes.put("adventure", GameMode.ADVENTURE);
gamemodes.put("creative", GameMode.CREATIVE);
gamemodes.put("spectator", GameMode.SPECTATOR);
gamemodes.put("survival", GameMode.SURVIVAL);

//Iterate over the map
for(String key : gamemodes.keySet()) {
    
    //Create our arguments as usual, using the LiteralArgument for the name of the gamemode
    LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
    arguments.put(key, new LiteralArgument(key));
    
    //Register the command as usual
    CommandAPI.getInstance().register("gamemode", arguments, (sender, args) -> {
        if(sender instanceof Player) {
            Player player = (Player) sender;
            
            //Retrieve the object from the map via the key and NOT the args[]
            player.setGameMode(gamemodes.get(key));
        }
    });
}

Literal argument warnings

Literal arguments require a string in the constructor. If the literal is an empty String or is null, the CommandAPI will throw a BadLiteralException.

Because literal arguments are "hardcoded", each literal is effectively mapped to a single command. This is shown when using the configuration option create-dispatcher-json: true which shows the JSON result of registered commands. For instance, take the /defaultgamemode command:

"defaultgamemode": {
    "type": "literal",
    "children": {
        "adventure": {
            "type": "literal",
            "executable": true
        },
        "creative": {
            "type": "literal",
            "executable": true
        },
        "spectator": {
            "type": "literal",
            "executable": true
        },
        "survival": {
            "type": "literal",
            "executable": true
        }
    }
},

Each option produces a new "command" in the tree of commands. This means that having exceptionally large lists of literals, or nested literals (e.g. /command <literal1> <literal2>) can cause very large trees which cannot be sent to the clients (it can cause clients to crash).

Developer's Note:

Take care when using literal arguments. If your list of arguments is exceptionally large, or contains many nested arguments, the server may be unable to send the command information to the client. If many command argument choices are required, consider using a StringArgument and using .overrideSuggestions() to create your own list of required arguments.

Dynamically suggested arguments

Dynamically suggested arguments lets you provide a function to generate arguments dynamically. This is achieved using the DynamicSuggestedStringArgument class, which has two constuctor formats:

//Constructor requires a function which returns a String[]
new DynamicSuggestedStringArgument(() -> return new String[]);

//Constructor requires a function which returns a String[] and receives a sender input
new DynamicSuggestedStringArgument((sender) -> return new String[]);

The first one simply lets you return a list of strings as suggestions. Whenever a player begins typing a command, a packet is sent to the server and it will return the list of suggestions (similar to tab completion). The second constructor allows you to also use the command sender as an input (of type CommandSender), which can be used for more intricate suggestions.

Example - Friend command with dynamic suggestions

Say we want a plugin which is a friend system. The friend system allows you to add friends and when you've made a friend, you can message them.

/friend <player>       - Adds a player as a friend
/msgf   <player> <msg> - Message a friend
//Global mapping of a player to a list of their friends
final Map<String, List<String>> friends = new ArrayList<>();

//Register /friend command
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("friend", new PlayerArgument());

CommandAPI.getInstance().register("friend", arguments, (sender, args) -> {
    
    //Get list of friends from the sender
    List<String> senderFriends;

    if(friends.containsKey(sender.getName())) {
        senderFriends = friends.get(sender.getName());
    } else {
        senderFriends = new ArrayList<>(); 
    }

    //Add friend to the global map
    senderFriends.put(((Player) args[0]).getName());
    friends.put(sender.getName(), senderFriends);
});

//Register /msgf command
arguments.clear();

/* Use a DynamicSuggestedStringArgument which consists of the names of
 * friends for that player. Don't forget that this returns a String */
arguments.put("friend", new DynamicSuggestedStringArgument((sender) -> {
    List<String> senderFriends = friends.get(sender.getName());
    return senderFriends.toArray(new String[senderFriends.size()]);
}));

arguments.put("message", new GreedyStringArgument());

CommandAPI.getInstance().register("msgf", arguments, (sender, args) -> {
    //Parse the target player from the String, failing if it can't find that player
    Player target = Bukkit.getPlayer((String) args[0]);
    if(target == null) {
        CommandAPI.fail("Couldn't find that player!");
    } else {
        target.sendMessage((String) args[1]);
    }
});

A thing to note from the code above: The DynamicSuggestedStringArgument is a String argument, meaning that the resulting type is a String. Despite the fact that we want a player object, the CommandAPI only lets you make dynamic suggestions for Strings. Therefore, it could fail in this situation (if the target player isn't found), and we have to handle that case on our own. If we had used a PlayerArgument, that argument would automatically fail due to the implementation of the player argument.

Custom arguments

Custom arguments are arguably the most powerful argument that the CommandAPI offers. This argument is used to represent any String, or Minecraft key (Something of the form String:String, such as minecraft:diamond)

In order to specify which type of custom argument is being used, the additional flag keyed can be added by, instead of using the default constructor which requires a lambda, you use the alternate constructor which requires a lambda, followed by true, which represents a Minecraft key input.

new CustomArgument<T>((input) -> { 
    /* Code which handles input */ 
    return //Some object of type T;
}, true);

The custom argument requires the type of the target object that the custom argument will return when parsing the arguments for a command. For instance, if you have a CustomArgument<Player>, then when parsing the arguments for the command, you would cast to a Player object: (Player) args[n].

Example - Scoreboard objectives as a custom argument

Here, we aim to create a custom argument that represents the Objective object for a scoreboard.

//Function that returns a CustomArgument<Object>
private CustomArgument<Objective> objectiveArgument() {
    Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard();

    /* Create our CustomArgument instance. This takes an input of a 
     * lambda that takes in a String and returns an Objective object. */
    return new CustomArgument<Objective>((input) -> {

        //Parse the objective
        if(scoreboard.getObjective(input) == null) {

            //Throw a custom error to the user if it fails to get the objective
            CustomArgument.throwError(new MessageBuilder("Unknown objective: ").appendArgInput());
            return null;
        } else {
            return scoreboard.getObjective(input);
        }
    
    //Use .overrideSuggestions to suggest the list of names of objectives
    }).overrideSuggestions(scoreboard.getObjectives().stream().map(o -> o.getName()).toArray(String[]::new));
}

From the code above, it uses the CustomArgument.throwError function which throws an error to the command sender if the command that they input is invalid. The throwError function can accept one of two parameters:

CustomArgument.throwError(String message)
CustomArgument.throwError(MessageBuilder message)

Message Builders

The MessageBuilder class is a class to easily create messages to describe errors when a sender sends a command which does not meet the expected syntax for an argument. It acts in a similar way to a StringBuilder, where you can append content to the end of a String.

The following methods are as follows:

Method Description
appendArgInput() Appends the argument that failed that the sender submitted to the end of the builder. E.g. /foo bar will append bar
appendFullInput() Appends the full command that a sender submitted to the end of the builder. E.g. /foo bar will append foo bar
appendHere() Appends the text <--[HERE] to the end of the builder
append(Object) Appends the object to the end of the builder

Example - Message builder for invalid objective argument

See the code above, which uses the following code snippet:

//Creates a MessageBuilder object that handles an invalid objective. 
new MessageBuilder("Unknown objective: ").appendArgInput();

Defined custom arguments

The CommandAPI has a few custom arguments which have been predefined, in the DefinedCustomArguments class. The methods are as follows:

DefinedCustomArguments.objectiveArgument(); //CustomArgument<Objective>
DefinedCustomArguments.teamArgument();      //CustomArgument<Team>

These are included to help reduce the amount of code required if you were to implement custom arguments for the types stated above.

Technical arguments

As of version 2.1, the CommandAPI has additional support for technical arguments. These consist of Minecraft "Keyed" arguments (something that takes the form of String:String, such as minecraft:diamond).

Based on the nature of technical arguments, these are unlikely to survive Minecraft updates and support for these will be implemented as soon as possible when new Minecraft updates are released. (For example, advancement arguments are compatible with Minecraft 1.14, 1.14.1 and 1.14.2, but required a rewrite to enable support for 1.14.3+)

Developer's Note:

As with any future bug, if you notice that a technical argument does not work with a specific version of Minecraft (1.13.2+), I'd be grateful if you could submit a bug report on the CommandAPI's issues page and I'll try to fix it as soon as I can!

Technical arguments are also compatible with data packs which allow you to add additional content to the game. In particular, Advancements, Loot Tables and Recipes are supported to allow extra interaction between Bukkit and data packs.

Sound argument

The SoundArgument class allows a command sender to retrieve the Bukkit Sound object to represent in-game sound effects (such as mob sounds or ambient sound effects), as well as in-game music.

Example - Playing sound to yourself

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("sound", new SoundArgument());

CommandAPI.getInstance().register("sound", arguments, (sender, args) -> {
    Sound sound = (Sound) args[0];
    Player player = (Player) sender;
    player.getWorld().playSound(player.getLocation(), sound, 100.0f, 1.0f);
});

Advancement argument

The AdvancementArgument class is used to represent Bukkit Advancement objects. This can be used to get the advancement progress for a player and a specific advancement, as well as awarding advancements programmatically.

Example - Awarding an advancement to a player

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("advancement", new AdvancementArgument());

CommandAPI.getInstance().register("giveadvancement", arguments, (sender, args) -> {
    Advancement advancement = (Advancement) args[0];
    Player player = (Player) sender;
    
    advancement.getCriteria().forEach(criteria -> {
        player.getAdvancementProgress(advancement).awardCriteria(criteria);
    });
});

Recipe argument

The RecipeArgument class lets you retrieve Bukkit's Recipe object.

Example - Giving the result of a recipe

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("recipe", new RecipeArgument());

CommandAPI.getInstance().register("giverecipe", arguments, (sender, args) -> {
    Recipe recipe = (Recipe) args[0];
    Player player = (Player) sender;        

    player.getInventory().addItem(recipe.getResult());
});

LootTable argument

The LootTableArgument class can be used to get a Bukkit LootTable object.

Example - ¯\_(ツ)_/¯

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("loottable", new LootTableArgument());

CommandAPI.getInstance().register("giveloottable", arguments, (sender, args) -> {
    LootTable lootTable = (LootTable) args[0];
    Player player = (Player) sender;
    
    LootContext context = /* Some generated LootContext relating to the lootTable*/
    lootTable.fillInventory(player.getInventory(), new Random(), context);
}); 

Developer's Note:

Honestly, I've not managed to get a successful example of using a LootTable in practice, due to being unable to generate a suitable LootContext. If you believe you can supply a suitable example for this page, feel free to send an example on the CommandAPI issues page.

Functions

The CommandAPI has support to use Minecraft's functions within your plugins. This is handled by using a class provided by the CommandAPI called FunctionWrapper, which allows you to execute functions. The CommandAPI also provides support to let you run your own commands within Minecraft function files.

Using custom commands in functions

In order to use a command from your plugin in a .mcfunction file, you must register your command in your plugin's onLoad() method, instead of the onEnable() method. Failure to do so will not allow the command to be registered for Minecraft functions, causing the function file to fail to load during the server startup phase.

Developer's Note:

In short, if you want to register a command which can be used in Minecraft functions, register it in your plugin's onLoad() method.

Example - Registering command for use in a function

public class Main extends JavaPlugin {

    @Override
    public void onLoad() {
        //Commands which will be used in Minecraft functions are registered here

        CommandAPI.getInstance().register("killall", new LinkedHashMap<>(), (sender, args) -> {
            //Kills all enemies in all worlds
            Bukkit.getWorlds()
                .forEach(w -> w.getLivingEntities()
                    .forEach(e -> e.setHealth(0))
                );
        });
    }
    
    @Override
    public void onEnable() {
        //Register all other commands here
    } 
}

Setting up functions & tags

Developer's Note:

Most developers can completely skip this section. This is for those that are unfamiliar with functions and tags and is less about how the CommandAPI works.

This section explains how functions are declared and set up for use in a Minecraft server. This is ideal for server owners who've never set up functions, or developers (like the Command API's creator) that has no idea how to set this sort of thing up.

Creating functions

Functions are text files (with the .mcfunction extension) which contains lists of functions which are executed one after another. Each line of the file is a valid Minecraft command. Say we have text.mcfunction:

killall
say Killed all living entities on the server

This will run the custom command killall (as declared in Chapter 5 - Functions), and then broadcast a message to all players stating that all entities were killed.

Creating tags

Tags are json files which contain a list of functions. Tags let you run multiple functions at a time. Say we have a tag called mytag.json:

{
    "values": [
        "mycustomnamespace:test",
        "mycustomnamespace:test2"
    ]
}

This will run the function test and the function test2, which are in the namespace mycustomnamespace.

Namespaces & where to place everything

The following hierarchy explains where functions and tags go. In this diagram, the two functions test and test2 are in a directory called functions. There is also a tag called mytag which is placed in the tags directory under functions. These are all under the namespace called mycustomnamespace

server/
├── world/
│   ├── advancements/
│   ├── data/
│   ├── datapacks/
│   │   └── bukkit/
│   │       ├── pack.mcmeta
│   │       └── data/
│   │           └── mycustomnamespace/
│   │               ├── functions/
│   │               │   ├── test.mcfunction
│   │               │   └── test2.mcfunction
│   │               └── tags/
│   │                   └── functions/
│   │                       └── mytag.json
│   └── ...
├── world_nether/
├── world_the_end/
├── ...
└── spigot.jar

To execute the test function, you would run the following command:

/function mycustomnamespace:test

To execute the mytag tag, you would run the following command:

/function #mycustomnamespace:mytag

The FunctionWrapper

The CommandAPI includes the FunctionWrapper class which is a wrapper for Minecraft's functions. It allows you to execute the commands that are represented by the respective .mcfunction file. There most important thing to note with the FunctionWrapper, and that it does not "store" the functions inside it, instead it just stores what the function does. In other words, you cannot "extract" the list of commands from a FunctionWrapper object.

FunctionWrapper methods

The FunctionWrapper class consists of two methods:

Method Result on execution
run() Executes the Minecraft function
runAs(Entity) Executes the Minecraft function as a specific Entity

The FunctionWrapper also implements the Keyed interface, allowing you to retrieve the NamespacedKey for this function using getKey().

Function arguments

The FunctionArgument class is used to represent a function or a tag in Minecraft. When retrieving an instance of the argument, it will return a FunctionWrapper[], where each FunctionWrapper consists of a Minecraft function.

Therefore, if a user supplies a single function, the FunctionWrapper[] will be of size 1, and if the user supplies a tag which can consist of multiple functions, the FunctionWrapper[] will consist of the array of functions as declared by that tag.

Example - Minecraft's /function command

Here, we implement Minecraft's /function command which is used to execute a function.

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("function", new FunctionArgument());

CommandAPI.getInstance().register("runfunction", arguments, (sender, args) -> {
    FunctionWrapper[] functions = (FunctionWrapper[]) args[0];

    //Run all functions that the user stated
    for(FunctionWrapper function : functions) {
        function.run();
    }

});

Permissions

Permissions let you control which players are allowed to execute which commands. This is handled using the CommandPermission class, which has the following uses:

  • Requires OP to execute a command:
    CommandPermission.OP
    
  • Anyone can execute a command:
    CommandPermission.NONE
    
  • Requires a specific permission node to exeucte a command:
    CommandPermission.fromString("my.permission")
    

Registering permissions to commands

To add a permission to a command, you can use any of the following constructors with a valid CommandAPI instance:

register(String commandName, CommandPermission, LinkedHashMap<String, Argument>, CommandExecutor);
register(String commandName, CommandPermission, String[] aliases, LinkedHashMap<String, Argument>, CommandExecutor)

Example - /god command with permissions

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();

//Register the /god command with the permission node "command.god"
CommandAPI.getInstance().register("god", CommandPermission.fromString("command.god"), arguments, (sender, args) -> {
    if(sender instanceof Player) {
        ((Player) sender).setInvulnerable(true);
    }
});

//Add a target argument to allow /god <target>
arguments.put("target", new EntitySelectorArgument(EntitySelector.ONE_PLAYER));

//Require "command.god" permission node to execute /god <target>
CommandAPI.getInstance().register("god", CommandPermission.fromString("command.god"), arguments, (sender, args) -> {
    ((Player) args[0]).setInvulnerable(true);
});

Registering permissions to arguments

For further fine-tuning of permission management, the CommandAPI allows you to add permissions to individual arguments. This prevents the user from executing a command with a specific argument if they do not have a specific permission.

This is done by using the .withPermission(CommandPermission) method at the end of an argument.

If a player does not have the required permission:

  • The argument hover text which suggests what the command is will not be shown
  • The player will receive an error if they try to type something in for that argument
  • Suggestions, such as a list of materials or players will not be shown

Example - /kill command with argument permissions

LinkedHashMap<String, ArgumentType> arguments = new LinkedHashMap<>();

//Register /kill command normally. Since no permissions are applied, anyone can run this command
CommandAPI.getInstance().register("kill", arguments, (sender, args) -> {
    ((Player) sender).setHealth(0);
});

//Adds the OP permission to the "target" argument. The sender requires OP to execute /kill <target>
arguments.put("target", new PlayerArgument().withPermission(CommandPermission.OP));

CommandAPI.getInstance().register("kill", arguments, (sender, args) -> {
    ((Player) args[0]).setHealth(0);
});

Developer's Note:

As you can see, there are multiple ways of applying permissions to commands with arguments. In the /god command shown above, the permission was applied to the whole command. In the /kill command shown above, the permission was applied to the argument.

There's not really much difference between the two methods, but I personally would use argument permissions with .withPermission() as it has greater control over arguments.

Aliases

Aliases let you create aliases for commands. They are simply added at command regisration time by using either of the following methods:

CommandAPI.getInstance().register(String, String[], LinkedHashMap, CommandExecutor);
CommandAPI.getInstance().register(String, CommandPermission, String[], LinkedHashMap, CommandExecutor);

The String[] represents a list of aliases which can be used to execute a command.

Example - Using aliases for /gamemode

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
//populate arguments here

CommandAPI.getInstance().register("gamemode", new String[] {"gm"}, arguments, (sender, args) -> {
    //Handle gamemode command here
});

Command conversion

Since the CommandAPI is used to register commands as a vanilla Minecraft command, you may want to use other plugins that are not written with the CommandAPI. For instance, if you want to include a command from a plugin which doesn't use the CommandAPI in a commandblock, (such as the /execute command), you can use the CommandAPI's command conversion system.

Developer's Note:

The command conversion system is nowhere near perfect. It tries its best to connect Bukkit plugins to vanilla Minecraft commands, but is not guaranteed to run flawlessly. If possible, consider forking/requesting a plugin and writing it with compatibility for the CommandAPI.

Entire plugins

To register all commands that are declared by a plugin, the Converter.convert(Plugin) method can be used. This attempts to register all commands declared in a plugin's plugin.yml file, as well as any aliases or permissions stated in the plugin.yml file.

It is important to note that the plugin must be loaded before your plugin before attempting conversion. (Use loadbefore: [YourPlugin, CommandAPI] to ensure it loads before your plugin)

Example - Converting commands for a plugin

Say you have some plugin.yml file for a plugin.

name: myPlugin
main: some.random.package.Main
loadbefore: [CommandAPI]
version: 1.0
commands:
  gmc:
    aliases: gm1
  gms:
  i:
    permission: item.permission

Then, the following code will register the commands /gmc, /gm1, /gms and /i:

Converter.convert(Bukkit.getPluginManager().getPlugin("myPlugin"));

Only specific commands

In addition to converting the whole plugin, the CommandAPI allows you to convert single commands at a time using the Converter.convert(Plugin, String) method, where the String argument refers to the command name as declared in the plugin's plugin.yml file.

Troubleshooting

This section basically summarizes the list of things that could go wrong with the CommandAPI and how to mitigate these circumstances.

Server/Plugin reloading

Due to the implementation of the CommandAPI, the CommandAPI does not support plugin reloading for plugins that use the CommandAPI. This includes, but is not limited to:

  • The /reload command which reloads all plugins on the server
  • Plugin reloading plugins, such as PlugMan
  • Any form of plugin enabling/disabling process for plugins which register commands via the CommandAPI

Developer's Note:

Plugin reloading gets very complicated with respect to the CommandAPI. Since the loading sequence of Minecraft commands is so picky, reloading the CommandAPI or a plugin which registers commands can cause commands to be re-registered. This can lead to very odd effects, such as command collisions (commands just don't work), to duplicate commands being registered under different namespaces (e.g. plugins are registered under Bukkit as well as Minecraft). These effects are not "100% guaranteed" and have only been seen during dodgy tests. In short, do not enable or reload plugins, and absolutely do not reload the server with /reload

Players cannot connect/timeout when joining

If players cannot connect, this could be due to the size of the command data packet. To see the resultant packet being sent to players when they log in, enable the create-dispatcher-json: true setting and view the file size of the resultant file. If the file size is abnormally large (Over 2MB is considered very large), consider reducing the number of LiteralArguments which your plugin uses.

The server just hangs/slows down on my PaperSpigot server

Officially, the CommandAPI does not really support PaperSpigot. As PaperSpigot is a fork of Spigot, it just assumes that it'll work on PaperSpigot if it works on Spigot.

Developer's Note:

There's a few things I personally don't like about PaperSpigot:

Command conversion throws a NullPointerException

This is likely caused by the fact that the plugin you want to convert hasn't been loaded yet. Ensure that it loads before your plugin by adding the following to the target plugin's plugin.yml file:

loadbefore: [YourPlugin, CommandAPI]

Aliases don't work properly (It says unknown command)

This is a persistent bug which has been resolved in version 2.1+ of the CommandAPI.

My issue isn't on here, what do I do?!

If you've found a bug that isn't solvable here, submit a bug report on the CommandAPI's issues page and I'll try my best to resolve the issue!

Afterword

Developer's Note:

Congratulations on making it to the end of the documentation! I hope it was easy to understand everything ... I feel like it's a bit content heavy.

This project is by far my most worked on and probably the project I'm most proud of. You can read about the creation of the CommandAPI in my blog post here.

I'd like to give credit to all of the people that have opened issues on GitHub, that have truly made it what it is now. In particular, I'd like to thank:

  • Combustible - My #1 supporter who motivated me to keep this project alive after its release
  • DerpNerb - Giving me the idea to change this to a Maven project has made the CommandAPI that much more accessible for everyone
  • Draycia - Suggesting that I put CommandSender objects inside DynamicSuggestedStringArguments allow that argument to be that much more powerful
  • HielkeMinecraft - Improving the LocationArgument class, adding results and successes for commandsd and being a great bug reporter in general
  • i509VCB - Generally a really good bug spotter. Discovered a severe bug that made libraries depending on the CommandAPI depend on Brigadier
  • Tinkot - Gave a review of the CommandAPI on its spigot page. This motivated me to fix a 6 month old bug

I never really expected more than 5 or so people to use this API, so it was truly an exciting time creating this for everyone - seeing everyone's responses, issues and problems helped me keep going.

In short, thank you, and everyone else that helped make the commandAPI what it is.