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, 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, you can search for anything in this entire documentation. For example, typing "Example" will show a list of examples which are included throughout the documentation.


Documentation changelog

Here's the list of changes to the documentation between each update. You can view the current documentation version at the top of this page.

Documentation changes 5.3 \(\rightarrow\) 5.6:

Documentation changes 5.2 \(\rightarrow\) 5.3:

Documentation changes 5.1 \(\rightarrow\) 5.2:

Documentation changes 5.0 \(\rightarrow\) 5.1:

Documentation changes 4.3 \(\rightarrow\) 5.0:

Developer's Note:

Lots and lots and lots of changes occurred in version 5.0! I highly recommend reading the Upgrading guide section which covers the changes in more detail and how to update your plugin for this version.

Basically every page has been rewritten in this update and checked for errors. In general, this is the list of new additions:

Documentation changes 4.2 \(\rightarrow\) 4.3:

  • Improve the documentation for 2. Configuration for server owners by using simple YAML lists (using the - symbol) and update the command conversion syntax for all commands using the ~ syntax
  • Adds the command sender priority to 6.1. Normal command executors
  • Adds new method and example for converting specific commands internally in 13. Command conversion
  • Adds two sneaky little buttons in the main title toolbar at the top of the page

Documentation changes 4.1 \(\rightarrow\) 4.2:

Documentation changes 4.0 \(\rightarrow\) 4.1:

Documentation changes 3.4 \(\rightarrow\) 4.0:

Documentation changes 3.3 \(\rightarrow\) 3.4:

Documentation changes 3.1 \(\rightarrow\) 3.3:

Documentation changes 3.0 \(\rightarrow\) 3.1:

  • Adds new section 5.1 Argument suggestions to cover how to override suggestions - Having it all in section 5. Arguments was a bit too content-heavy
  • Adds documentation for the new .overrideSuggestions() method in section 5.1 Argument suggestions
  • Simplified the description of the documentation updates
  • Changed the artifact ID for the dependency of the CommandAPI. Instead of being commandapi, it is now commandapi-core. You can view the changes in section 2 Setting up your development environment
  • Changed the repository information for gradle in section 2 Setting up your development environment. You now have to include the NBTAPI repository because gradle can't automatically detect this for some reason. Kinda stupid tbh.
  • Adds a section on using multiple or optional arguments in section 5 Arguments

Documentation changes 2.1 \(\rightarrow\) 3.0:

Developer's Note:

Lots of changes occurred in version 3.0. I highly recommend reading the Upgrading guide section which covers the changes in more detail and how to update your plugin for this version.

Installation

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


Download latest CommandAPI.jar


Additional dependencies

  • If you use NBT data, you'll also need the NBT API. (You can find out from your developers if you need this or not)
  • If you are using raw JSON chat data, you'll need to be running Spigot or another spigot-related server such as Paper Spigot or Taco Spigot. (Again, you can find out from your developers if you need this or not)

Configuration for server owners

The CommandAPI has a few configuration options to change how it functions. These options can be configured in the plugins/CommandAPI/config.yml file, which is generated automatically when the CommandAPI runs for the first time.

The default config.yml is shown below:

verbose-outputs: false
create-dispatcher-json: false
plugins-to-convert: []
skip-sender-proxy: []

Configuration settings:

  • verbose-outputs - If true, outputs command registration and unregistration logs in the console
  • create-dispatcher-json - If true, the CommandAPI creates a command_registration.json file showing the mapping of registered commands. This is designed to be used by developers - setting this to false will improve command registration performance
  • plugins-to-convert - Controls the list of plugins to process for command conversion. See Command conversion for more information!
  • skip-sender-proxy - Determines whether the proxy sender should be skipped when converting a command. See Skipping proxy senders for more information!

Command Conversion (for server owners)

The CommandAPI has the ability to convert plugin commands into "Vanilla compatible" commands automatically on startup. This allows you to use /execute and Minecraft functions/tags for plugins that do not use the CommandAPI. For example, if you want to use the /hat command from the plugin Essentials in an /execute command or from a command block, you can use the CommandAPI's command conversion setting to do so.

The CommandAPI has 3 different conversion methods, each one more specific and powerful than the others. These are the following:


YAML configuration rules

To configure command conversion, the CommandAPI reads this information from the config.yml file. This file has a bit of a weird structure, so to put it simply, these are the following rules:

  • config.yml cannot have tab characters - The config.yml file must only consist of spaces!
  • Indentation is important and should be two spaces

Converting all plugin commands

To convert all of the commands that a plugin has, add the name of the plugin, followed by a ~ character to the list of plugins to convert in the config.yml file.

Example - Converting all commands from EssentialsX

For example, if you wanted to convert all commands that EssentialsX has, you can use the following config.yml:

verbose-outputs: true
create-dispatcher-json: false
plugins-to-convert: 
  - Essentials: ~

Single command conversion

Often, you don't want to convert every single command that a plugin declares, and instead you only want to convert a few commands that a plugin has.

To convert a single command, you need to first populate the config.yml with the name of the plugin and commands to be converted. To illustrate this, we'll use an example:

Example - Converting commands

Say we're using EssentialsX on our server and we want to be able to use /afk and /hat in command blocks. This would allow us to use (for example) the following commands in command blocks:

/execute as @p run afk
/execute as @p run hat

To do this, we need to add Essentials to our config.yml file, and include the commands afk and hat as the commands to be converted from the Essentials plugin. This would then make our config.yml file look like this:

verbose-outputs: true
create-dispatcher-json: false
plugins-to-convert: 
  - Essentials: 
    - hat
    - afk

Developer's Note:

Note that the commands hat and afk are used, as opposed to an alias such as head. The CommandAPI is only able to convert plugin commands that are declared in a plugin's plugin.yml file. For example, if we take a look at the EssentialsX plugin.yml file, we can see the commands afk and hat have been declared and thus, are the commands which must be used in the CommandAPI's config.yml file:

name: Essentials
main: com.earth2me.essentials.Essentials
version: 2.18.0.0
website: http://tiny.cc/EssentialsCommands
description: Provides an essential, core set of commands for Bukkit.
softdepend: [Vault, LuckPerms]
authors: [Zenexer, ementalo, Aelux, Brettflan, KimKandor, snowleo, ceulemans, Xeology, KHobbits, md_5, Iaccidentally, drtshock, vemacs, SupaHam, md678685]
api-version: "1.13"
commands:
  afk:
    description: Marks you as away-from-keyboard.
    usage: /<command> [player/message...]
    aliases: [eafk,away,eaway]

# (other config options omitted)

  hat:
    description: Get some cool new headgear.
    usage: /<command> [remove]
    aliases: [ehat,head,ehead]

# (other config options omitted)

Single command conversion (with arguments)

For even finer control when converting a single command, you can provide the list of arguments that are required to run the command! This lets you use the command UI in converted commands as you see fit. Before we explain how to do this in detail, let's first take a look at an example of this in action.

Example - Converting EssentialsX's /speed command

EssentialsX includes a command /speed which lets you change the current speed that a player can move at. The command format is the following:

/speed <speed>
/speed <speed> <target>
/speed <walk/fly> <speed>
/speed <walk/fly> <speed> <target>

Which means you can run any of the following commands:

/speed 5
/speed 2 Notch
/speed fly 6
/speed walk 3 Notch

By looking at this, we can see that:

  • <speed> is a number. By using the command, we can determine that the range of values is between 0 and 10 (inclusive).
  • <target> is a player
  • <walk/fly> don't change - these are "fixed" values.

We can represent this using the following config.yml file:

verbose-outputs: false
create-dispatcher-json: false
plugins-to-convert:
  - Essentials:
    - speed <speed>[0..10]
    - speed <target>[minecraft:game_profile]
    - speed (walk|fly) <speed>[0..10]
    - speed (walk|fly) <speed>[0..10] <target>[minecraft:game_profile]

Using this, we can display options, such as "fly" and "walk", as well as optional targets ("Skepter"):

Additionally, we can apply limits to the numbers that can be provided. For example, here we limit the number to a value between 0 to 10. If a value is outside of that range, and error is shown to the user:


Command argument syntax

The argument syntax is a little tricky to get the hang of at the beginning, but it should be fairly straight forward. There are two main types of arguments that you can have:

Literal arguments

Literal arguments are arguments with "fixed" values, such as walk or fly from our example above. To declare a literal value, place brackets around the value. For example:

(walk)

To have multiple different literals, place a pipe symbol | between each entry within the brackets. For example:

(walk|fly)

Named arguments

Named arguments must have a name, declared in angled brackets <name>, followed by the type of the argument in square brackets [type]. In the example above, we had a named argument <target>, with the argument type as a player: [minecraft:game_profile].

The name in the argument can basically be whatever you want, but it is highly recommended to keep it as a lowercase value consisting only of letters.

The following argument types are highly recommended and are very likely to be compatible with every plugin command that you may want to convert:

TypeDescription
api:greedy_stringAn unlimited amount of text. This can only be used as the last entry of a list of arguments
brigadier:boolA Boolean value true or false
brigadier:doubleA decimal number
brigadier:floatA decimal number
brigadier:integerA whole number
brigadier:longA whole number
brigadier:stringA single word
minecraft:block_posA location of x, y and z coordinates (whole numbers)
minecraft:entityAn entity (e.g. Notch)
minecraft:game_profileA player (e.g. Notch)

In the example above, we used the a "range type" in the form [0..10]. This is a special argument type that will conform to brigader:long or brigader:double and apply a limit to the values that can be entered.

Example - Declaring"range type" arguments

To declare the range \(10 \le x \le 50\) (a value must be between 10 and 50 (inclusive)):

<name>[10..50]

To declare the range \(10 \le x\) (a value must be bigger than or equal to 10):

<name>[10..]

To declare the range \(x \le 50\) (a value must be less than or equal to 50):

<name>[..50]

To declare the range \(0 \le x \le 1\), where \(x\) is a decimal value:

<name>[0.0..1.0]

To declare a value \(x\) that can take any range of values and is a decimal number:

<name>[brigadier:double]

List of all supported argument types

The list of types are based on the list of argument types from the Minecraft Wiki, with a few changes. The complete list that the CommandAPI supports is as follows:

TypeDescription
api:advancementAn advancement
api:biomeA biome
api:greedy_stringAn unlimited amount of text. This can only be used as the last entry of a list of arguments
api:loot_tableA loot table
api:recipeA recipe
api:soundA sound effect
api:textText encased in quotes: "text with spaces"
brigadier:boolA Boolean value true or false
brigadier:doubleA decimal number
brigadier:floatA decimal number
brigadier:integerA whole number
brigadier:longA whole number
brigadier:stringA single word
minecraft:angleA yaw angle in degrees (from -180.0 to 179.9)
minecraft:block_posA location of x, y and z coordinates (whole numbers)
minecraft:block_predicateA block predicate
minecraft:block_stateA block type (e.g. stone)
minecraft:colorA chat color (e.g. red, green)
minecraft:column_posA location of x and z coordinates (whole numbers)
minecraft:componentRaw JSON text
minecraft:dimensionA dimension, (e.g. minecraft:overworld)
minecraft:entityAn entity (e.g. Notch)
minecraft:entity_summonAn entity type (e.g. cow, wither)
minecraft:float_rangeA range of decimal numbers
minecraft:functionA datapack function
minecraft:game_profileA player (e.g. Notch)
minecraft:int_rangeA range of whole numbers
minecraft:item_enchantmentAn enchantment (e.g. unbreaking)
minecraft:item_predicateAn item predicate
minecraft:item_stackAn item (e.g. stick)
minecraft:messageA plain text message which can have target selectors (e.g. Hello @p). This can only be used as the last entry of a list of arguments
minecraft:mob_effectA potion effect (e.g. speed, jump_boost)
minecraft:nbt_compound_tagRaw compound NBT in SNBT format
minecraft:objectiveAn objective name (e.g. temperature)
minecraft:objective_criteriaAn objective criteria (e.g. deaths)
minecraft:operationAn operation symbol (e.g. +=, *=)
minecraft:particleA particle (e.g. crit, flame)
minecraft:rotationA rotation of yaw and pitch values (e.g. ~ ~)
minecraft:score_holderA score holder (e.g. Notch)
minecraft:scoreboard_slotA scoreboard slot (e.g. sidebar)
minecraft:swizzleA collection of axes (e.g. xyz, xz)
minecraft:teamA team name (e.g. hunters)
minecraft:timeA duration of time (e.g. 2d)
minecraft:uuidA UUID (e.g. dd12be42-52a9-4a91-a8a1-11c01849e498)
minecraft:vec2A location of x and z coordinates (decimal numbers)
minecraft:vec3A location of x, y and z coordinates (decimal numbers)

Skipping proxy senders

When the CommandAPI converts a command, it does some tweaks to the thing running the command (such as the player or console) to improve compatibility with plugins (mostly permissions). This doesn't always work, and can sometimes produce an error which looks like this:

[20:44:01 ERROR]: java.lang.IllegalArgumentException: object is not an instance of declaring class
[20:44:01 ERROR]:  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[20:44:01 ERROR]:  at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
[20:44:01 ERROR]:  at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
[20:44:01 ERROR]:  at java.lang.reflect.Method.invoke(Unknown Source)
[20:44:01 ERROR]:  at dev.jorel.commandapi.Converter.lambda$mergeProxySender$3(Converter.java:151)
[20:44:01 ERROR]:  at com.sun.proxy.$Proxy33.getInventory(Unknown Source)

To fix this, add the plugin which the command is registered from to the list of plugins under skip-sender-proxy.

Example - Improving compatibility with the SkinsRestorer plugin

SkinsRestorer (not associated or sponsored by the CommandAPI in any way) is a plugin that lets you change the skin for a player. This suffers from the above issue and is not compatible with the CommandAPI's conversion compatibility tweaks. To do this, we'll add SkinsRestorer to the list of plugins which should be skipped:

verbose-outputs: true
create-dispatcher-json: false
plugins-to-convert: 
  - SkinsRestorer: ~
skip-sender-proxy:
  - SkinsRestorer

Setting up your development environment

To use the CommandAPI in your plugins, there are a few 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 CommandAPI 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>commandapi</id>
            <url>https://raw.githubusercontent.com/JorelAli/CommandAPI/mvn-repo/</url>
        </repository>
    </repositories>
    
  • Add the dependency to your pom.xml:

    <dependencies>
        <dependency>
            <groupId>dev.jorel</groupId>
            <artifactId>commandapi-core</artifactId>
            <version>5.6</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    
  • Add the CommandAPI as a dependent in the plugin.yml (depend: [CommandAPI])


Using Gradle

  • Add the repositories to your build.gradle file (the second repository is required because the CommandAPI depends on the NBT-API):

    repositories {
        maven { url = "https://raw.githubusercontent.com/JorelAli/CommandAPI/mvn-repo/" }
        maven { url = "https://repo.codemc.org/repository/maven-public/" }
    }
    
  • Add the dependency to your list of dependencies in your build.gradle file:

    dependencies {
        compileOnly "dev.jorel:commandapi-core:5.6"
    }
    
  • Add the CommandAPI as a dependent in the plugin.yml (depend: [CommandAPI])

Shading the CommandAPI in your plugins

After 2 years, this most requested feature is finally here...

The CommandAPI can now be shaded into your own plugins! "Shading" is the process of including the CommandAPI inside your plugin, rather than requiring the CommandAPI as an external plugin. In other words, if you shade the CommandAPI into your plugin, you don't need to include the CommandAPI.jar in your server's plugins folder.


Shading vs CommandAPI plugin

The CommandAPI plugin has a few slight differences with the shaded CommandAPI jar file. The CommandAPI plugin has the following extra features that are not present in the shaded version:

  • Command conversion via a config.yml file
  • Creation of the command_registration.json file to show the Brigadier command graph

Shading requirements

For the CommandAPI to function as normal, you must call the CommandAPI's initializers in the onLoad() and onEnable() methods of your plugin:

CommandAPI.onLoad(boolean verbose);
CommandAPI.onEnable(Plugin plugin);

The onLoad(boolean) method initializes the CommandAPI's loading sequence. This must be called before you start to access the CommandAPI and must be placed in your plugin's onLoad() method. The argument verbose is used to enable verbose logging output.

The onEnable(Plugin) method initializes the CommandAPI's enabling sequence. As with the onLoad(boolean) method, this one must be placed in your plugin's onEnable() method. This isn't as strict as the onLoad(boolean) method, and can be placed anywhere in your onEnable() method. The argument plugin is your current plugin instance.

Example - Setting up the CommandAPI in your plugin

public class MyPlugin extends JavaPlugin {

    @Override
    public void onLoad() {
        CommandAPI.onLoad(true); //Load with verbose output
        
        new CommandAPICommand("ping")
            .executes((sender, args) -> {
                sender.sendMessage("pong!");
            })
            .register();
    }
    
    @Override
    public void onEnable() {
        CommandAPI.onEnable(this);
        
        //Register commands, listeners etc.
    }

}

Shading with Maven

To shade the CommandAPI into a maven project, you'll need to use the commandapi-shade dependency, which is optimized for shading and doesn't include plugin-specific files (such as plugin.yml):

<dependencies>
	<dependency>
		<groupId>dev.jorel</groupId>
        <artifactId>commandapi-shade</artifactId>
        <version>5.6</version>
    </dependency>
</dependencies>

Once you've added this this, you can shade the CommandAPI easily by adding the maven-shade-plugin to your build sequence:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.4</version>
            <executions>
                <execution>
                    <id>shade</id>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <relocations>
                    <relocation>
                        <pattern>dev.jorel.commandapi-shade</pattern>
                    </relocation>
                </relocations>
            </configuration>
        </plugin>
    </plugins>
</build>

Of course, if you shade the CommandAPI into your plugin, you don't need to add depend: [CommandAPI] to your plugin.yml file.


Shading with Gradle

To shade the CommandAPI into a Gradle project, we'll use the Gradle Shadow Plugin. Add this to your list of plugins:

plugins {
    id 'java'
    id 'com.github.johnrengelman.shadow' version '6.0.0'
}

Next, we declare our dependencies:

dependencies {
    compile "dev.jorel:commandapi-shade:5.6"   
}

Then we add it to the shadowJar task configuration:

shadowJar {
	dependencies {
		include dependency("dev.jorel:commandapi-shade:5.6")
	}
}

Finally, we can build the shaded jar using the following command:

gradlew build shadowJar

Again, as we're shading the CommandAPI into your plugin, we don't need to add depend: [CommandAPI] to your plugin.yml file.

Using the annotation system

The annotation system is a separate part of the CommandAPI, and as a result it needs to be included as an additional dependency to your project.

The annotation system effectively needs to be added twice: Once for compilation and again to invoke the annotation processor itself.


Using Maven

  • If you haven't already done so, add the maven repository to your pom.xml file:

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

    <dependencies>
        <dependency>
            <groupId>dev.jorel</groupId>
            <artifactId>commandapi-annotations</artifactId>
            <version>5.3</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    
  • Add the annotation processor as an annotation process to the compile task in the pom.xml:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <annotationProcessorPaths>
    					<path>
                            <groupId>dev.jorel</groupId>
                            <artifactId>commandapi-annotations</artifactId>
                            <version>5.3</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
          </plugin>
        </plugins>
    </build>
    

Using Gradle

  • If you haven't already done so, add the maven repository to your build.gradle file:

    repositories {
        maven { url = "https://raw.githubusercontent.com/JorelAli/CommandAPI/mvn-repo/" }
        maven { url = "https://repo.codemc.org/repository/maven-public/" }
    }
    
  • Add the dependency and annotation processor to your list of dependencies in your build.gradle file:

    dependencies {
        compileOnly "dev.jorel:commandapi-annotations:5.3"
        annotationProcessor "dev.jorel:commandapi-annotations:5.3"
    }
    

Command registration

To register commands with the CommandAPI, we use the CommandAPICommand class. It follows a simple builder pattern to improve readability.

I think the easiest way to explain it is with an example:

// Create our arguments
List<Argument> arguments = new ArrayList<>();
arguments.add(new GreedyStringArgument("message"));

//Create our command
new CommandAPICommand("broadcastmsg")
    .withArguments(arguments)                     // The arguments
    .withAliases("broadcast", "broadcastmessage") // Command aliases
    .withPermission(CommandPermission.OP)         // Required permissions
    .executes((sender, args) -> {
        String message = (String) args[0];
        Bukkit.getServer().broadcastMessage(message);
    }).register();
  • First, we create our arguments for the command. This is described in more detail in the section on arguments.
  • We then create a new CommandAPICommand, with the name of the command that the sender must enter to run it.
  • We then add the arguments to the command with withArguments.
  • In this example, we add an alias, "broadcast", to the command. This allows the sender to use either /broadcastmsg <message> or /broadcast <message>.
  • By using withPermission, we require the sender to be an OP in order to run the command.
  • We control what the command does using executes (this is described in more detail in the section on command executors).
  • Finally, we register the command to the CommandAPI using register.

That's it! This simple snippet of code fully registers the command to the server. No hassling with a plugin instance, no messing with plugin.yml files.

Throughout this documentation, we will use the various different methods for command registration to give you an idea of when and where certain methods are more suitable than others.


CommandAPICommand methods

The CommandAPICommand has various methods, which are outlined below:

Setting the command name

new CommandAPICommand(String commandName)

This constructor creates a new instance of the CommandAPICommand object. This constructor requires the name of the command.

Setting command properties

CommandAPICommand withArguments(List<Argument> arguments)

The withArguments method is used to add arguments to your command. The arguments parameter is appended to the the list of arguments for the command.

CommandAPICommand withArguments(Argument... arguments)

Similar to the other withArguments method, this method appends the arguments to the list of arguments for the command. This is purely to make adding one or two arguments nice and easy instead of creating lots of List objects everywhere.

CommandAPICommand withPermission(CommandPermission)
CommandAPICommand withPermission(String)

The withPermission method is used to assign a permission that is required to execute the command. (See the section on permissions for more info).

CommandAPICommand withAliases(String... args)

The withAliases method is used to declare a list of aliases that can be used to run this command via. (See the section on aliases for more info).

CommandAPICommand withSubcommand(CommandAPICommand subcommand)

The withSubcommand method is used to declare a subcommand that leads on from the current command. (See the section on subcommands for more info).

Setting the command's executor

CommandAPICommand executes((sender, args) -> {})

Executes a command using the CommandSender object.

CommandAPICommand executesPlayer((player, args) -> {})

Executes a command only if the command sender is a Player.

CommandAPICommand executesEntity((entity, args) -> {})

Executes a command only if the command sender is an Entity.

CommandAPICommand executesCommandBlock((cmdblock, args) -> {})

Executes a command only if the command sender is a BlockCommandSender.

CommandAPICommand executesConsole((console, args) -> {})

Executes a command only if the command sender is a ConsoleCommandSender.

CommandAPICommand executesProxy((proxy, args) -> {})

Executes a command only if the command sender is a ProxiedCommandSender.

CommandAPICommand executesNative((proxy, args) -> {})

Executes a command regardless of what the command sender is, using the NativeProxyCommandSender. Read more about native proxied command senders here.

Developer's Note:

Sometimes, the Java compiler throws an error saying that a method is ambiguous for the type CommandAPICommand. This is due to a limitation in Java's type inference system and is not a fault of the CommandAPI. If we take the following code, used to spawn a pig:

new CommandAPICommand("spawnpigs")
     .executesPlayer((player, args) -> {
           for(int i = 0; i < 10; i++) {
               player.getWorld().spawnEntity(player.getLocation(), (EntityType) args[0]);
           }
     })
     .register();

The Java type inference system cannot determine what the type of the lambda (player, args) -> () is, therefore it produces the following compilation error:

The method executesPlayer(PlayerCommandExecutor) is ambiguous for the type CommandAPICommand

This can easily be resolved by declaring the specific type of the command sender and the arguments. For example:

new CommandAPICommand("spawnpigs")
     .executesPlayer((Player player, Object[] args) -> {
           for(int i = 0; i < 10; i++) {
               player.getWorld().spawnEntity(player.getLocation(), (EntityType) args[0]);
           }
     })
     .register();

Registering the command

void register()

Registers the command.


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 whether you use onLoad() or onEnable() to load your commands depends on whether your plugin is used with Minecraft's functions:

When to loadWhat to do
onLoad() methodRegister commands to be used in Minecraft functions (see the Function section for more info)
onEnable() methodRegister regular commands

Command unregistration

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

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 /changegamemode.

MethodResult
CommandAPI.unregister(String cmd)Unregisters a command from the game
CommandAPI.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.unregister("gamemode", true);

List<Argument> arguments = new ArrayList<>();

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

new CommandAPICommand("gamemode")
    .withArguments(arguments)
    .executes((sender, args) -> {
        //Implementation of our /gamemode command
    }).register();

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 on Normal command executors - this behaves very similar to the onCommand method in Bukkit.


The CommandAPI provides various command executors which are lambdas which execute the code you want when a command is called. With a lot of simplification, there are two main types of command executors:

  • Ones that just runs the command (let's call it a normal command executor)
  • Ones that returns an integer as a result (let's call it a resulting command executor)

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, and specifically want to state whether a command returns a value, just ensure you return an integer at the end of your declared command executor. Java will infer the type (whether it's a normal command executor or a resulting command executor) automatically, so feel free to return an integer or not.

In addition to these two types of command executors, there are ways to restrict the execution of commands to certain CommandSender subclasses. In other words, you can make commands executable by players in game only for instance. These restrictions are covered in more detail in Normal command executors.

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.

new CommandAPICommand("...")
    .executes((sender, args) -> {
        //Code here  
    })
    .register();

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 (See the section on handling command failures.

In short, this is what values are returned when a command is executed from a normal command executor:

Command WorksCommand Doesn't Work
Success Value10
Result Value10

Example - Creating a message broadcasting system

To illustrate this, let's take a look at a simple message broadcasting command. We declare our arguments (in this case, "message"), we provide some aliases and set a permission required to run the command. Then we declare our main command body by using the .executes() method, before finally registering the command:

//Create our command
new CommandAPICommand("broadcastmsg")
    .withArguments(new GreedyStringArgument("message")) // The arguments
    .withAliases("broadcast", "broadcastmessage")       // Command aliases
    .withPermission(CommandPermission.OP)               // Required permissions
    .executes((sender, args) -> {
        String message = (String) args[0];
        Bukkit.getServer().broadcastMessage(message);
    })
    .register();

Note how when we finish up our implementation of .executes(), we don't return anything. This is unlike commands in the standard Bukkit API where the onCommand method returns a Boolean value:

boolean onCommand(CommandSender, Command, String, String[])

The returning of this Boolean value is handled automatically by the CommandAPI on a much lower level.


Restricting who can run your command

The CommandAPICommand has multiple different executes...() methods that can restrict the command sender to any of the following objects:

  • CommandSender - No restriction, players, the console etc. can use this command. This is what Bukkit normally uses.
  • Player - Only in-game players can run this command
  • Entity - Only entities (therefore, players as well) can run this command
  • BlockCommandSender - Only command blocks can run this command
  • ConsoleCommandSender - Only the console can run this command
  • ProxiedCommandSender - Only proxied command senders (e.g. other entities via the /execute as ... command)
  • NativeProxyCommandSender - This type has special rules governing it. See Native commandsenders for more information

This is done using the respective method:

Restricted senderMethod to use
CommandSender.executes()
Player.executesPlayer()
Entity.executesEntity()
BlockCommandSender.executesCommandBlock()
ConsoleCommandSender.executesConsole()
ProxiedCommandSender.executesProxy()
NativeProxyCommandSender.executesNative()

Example - A /suicide command

Say we wanted to create a command /suicide, which kills the player that executes it. Since this command isn't really "designed" for command senders that are not players, we can restrict it so only players can execute this command (meaning that the console and command blocks cannot run this command). Since it's a player, we can use the .executesPlayer() method:

new CommandAPICommand("suicide")
	.executesPlayer((player, args) -> {
	    player.setHealth(0);
	})
	.register();

Multiple command executor implementations

The CommandAPI allows you to chain different implementations of the command depending on the type of CommandSender. This allows you to easily specify what types of CommandSenders are required to run a command.

Extending on the suicide example above, we could write another implementation for a different CommandSender. Here, we write an implementation to make entities (non-player) go out with a bang when they run the command (using /execute as <entity> run <command> command).

Example - A /suicide command with different implementations

new CommandAPICommand("suicide")
    .executesPlayer((player, args) -> {
        player.setHealth(0);
    })
    .executesEntity((entity, args) -> {
        entity.getWorld().createExplosion(entity.getLocation(), 4);
        entity.remove();
    })
    .register();

This saves having to use instanceof multiple times to check the type of the CommandSender.

The different command sender priority is the following (from highest priority to lowest priority):

\begin{align} &\quad\texttt{.executesNative()} && \texttt{(Always chosen if used)}\\ &\quad\texttt{.executesPlayer()} \\ &\quad\texttt{.executesEntity()} \\ &\quad\texttt{.executesConsole()} \\ &\quad\texttt{.executesCommandBlock()} \\ &\quad\texttt{.executesProxy()} \\ &\quad\texttt{.executes()} \end{align}

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:

new CommandAPICommand("killme")
    .executesPlayer((player, args) -> {
        player.setHealth(0);
    })
    .register();

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 use the .executesProxy() method to ensure that the command sender is a ProxiedCommandSender. Then, we can kill the callee (the entity which is being 'forced' to run the command /killme)

new CommandAPICommand("killme")
    .executesPlayer((player, args) -> {
        player.setHealth(0);
    })
    .executesProxy((proxy, args) -> {
        //Check if the callee is an Entity
        if(proxy.getCallee() instanceof LivingEntity) {

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

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

Native commandsenders

In a similar way that the ProxiedCommandSender is used to store information about two command senders: a caller (the one that wrote the command) and a callee (the one that ends up executing the command), the CommandAPI also has a special NativeProxyCommandSender class which is a more powerful representation of the ProxiedCommandSender class. In addition to inheriting all of the methods from ProxiedCommandSender, this class also has the following two methods:

public World getWorld();
public Location getLocation();

These methods contain additional information about the command executor's state, and are primarily designed to be used with Minecraft's /execute command.


Minecraft's /execute arguments

The following table represents how the different /execute arguments affect the NativeProxyCommandSender class:

/execute argumentHow it changes NativeProxyCommandSender
/execute alignChanges getLocation() only
/execute anchoredChanges nothing
/execute asChanges getCallee() only
/execute atChanges getLocation() and getWorld() only
/execute facingChanges getLocation() only
/execute inChanges getWorld() only
/execute positionedChanges getLocation() only
/execute rotatedChanges getLocation()only

Using the native commandsender

As described in the section about normal command executors, there are multiple methods to register a command executor. For the NativeProxyCommandSender, the .executesNative() method should be used.

Note:

The .executesNative() method has the highest priority over all over .executesXXX() methods - if you use the .executesNative() method, no other execution method will be run.


Example - A really simple "break block" command

Say we wanted to make a command that simply sets the current block to air. For this example, we'll use the following command structure:

/break

As you can see, this command takes no arguments. This is fine, since our "argument" will be the sender's location. We can access the sender's location using the getLocation() method from the NativeProxyCommandSender object, available from the .executesNative() method:

new CommandAPICommand("break")
    .executesNative((sender, args) -> {
        Location location = (Location) sender.getLocation();
        if(location != null) {
            location.getBlock().breakNaturally();
        }
    })
    .register();

This can now be used via the following command examples:

/execute positioned 100 62 50 run break
/execute at @e[type=pig] run break
/execute in minecraft:overworld positioned 20 60 -20 run break

Resulting command executors

Resulting command executors are very similar to normal command executors, except they can return an integer result value.

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

Similarly, these will return a success value of 1 if it runs successfully, and a success value of 0 if it runs unsuccessfully. If a success value of 0 occurs, the result value will be 0. In short:

Command WorksCommand Doesn't Work
Success Value10
Result Valueresult defined in your code0

The concept of result values are better explained through examples:

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.

new CommandAPICommand("randnum")
    .executes((sender, args) -> {
        return new Random().nextInt();
    })
    .register();

This returns a success value of 1 (Because no errors or CommandAPI.fail(String) was thrown) and a result 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. The concept is to create a command that generates a random number from 1 to 100. If the number is 1 (thus, the chance of being chosen is \(\frac{1}{100}\)), then we award a player with some reward, say 64 diamonds.

To do this, we'll declare two commands:

/randomnumber        - returns a random number between 1 and 99 (inclusive)
/givereward <player> - gives a player 64 diamonds and broadcasts it in the chat

Since we're declaring commands that are to be used in /execute, we must ensure that these commands are registered in your plugin's onLoad() method. First, we write our implementation for /randomnumber. It is fairly straight forward using Java's ThreadLocalRandom to generate a random number:

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

Now we write our implementation for /givereward. In this example, we use the EntitySelectorArgument to select a single player. We cast it to Player and then add the items to their inventory.

//Register reward giving system for a target player
new CommandAPICommand("givereward")
    .withArguments(new EntitySelectorArgument("target", EntitySelector.ONE_PLAYER))
    .executes((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!");
    })
    .register();

Now that we've declared these commands, we can now use them in practice. We can use a command block to store a random number under the scoreboard score randVal for a player called SomePlayer, by executing the command /randomnumber. Since /randomnumber returns an integer, this value is stored in the scoreboard score:

/execute store result score SomePlayer randVal run randomnumber

To check if the random number is equal to 1, we can use the /execute if command. If their score stored in randVal matches 1, then we run the /givereward command.

/execute if score SomePlayer randVal matches 1 run givereward SomePlayer

Handling command failures

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 containing 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
String[] fruit = new String[] {"banana", "apple", "orange"};

//Argument accepting a String, suggested with the list of fruit
List<Argument> arguments = new ArrayList<>();
arguments.add(new StringArgument("item").overrideSuggestions(fruit));

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

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 built-in 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.

Arguments

Arguments in the CommandAPI are registered by using a List<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 List, the order of the elements inserted into it are preserved, meaning the order you add arguments to the List 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 a List
List<Argument> arguments = new ArrayList<>();

//Add an argument with the node "target", which is a PlayerArgument
arguments.add(new PlayerArgument("target"));

The String value is the node that is registered into Minecraft's internal command graph. This is name is also used as a prompt 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.

List<ArgumentType> arguments = new ArrayList<>();
arguments.add(new StringArgument("arg0"));
arguments.add(new PotionEffectArgument("arg1"));
arguments.add(new LocationArgument("arg2"));

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

The type to cast each argument (declared in the dev.jorel.commandapi.arguments package) is listed below:

Argument classData type
AngleArgumentfloat
AdvancementArgumentorg.bukkit.advancement.Advancement
AxisArgumentjava.util.EnumSet<org.bukkit.Axis>
BiomeArgumentorg.bukkit.block.Biome
BlockPredicateArgumentjava.util.function.Predicate<org.bukkit.block.Block>
BlockStateArgumentorg.bukkit.block.data.BlockData
BooleanArgumentboolean
ChatArgumentnet.md_5.bungee.api.chat.BaseComponent[]
ChatColorArgumentorg.bukkit.ChatColor
ChatComponentArgumentnet.md_5.bungee.api.chat.BaseComponent[]
CustomArgument<T>T
DoubleArgumentdouble
EnchantmentArgumentorg.bukkit.enchantments.Enchantment
EntitySelectorArgumentThe cast type changes depending on the input parameter:
  • EntitySelector.MANY_ENTITIES - Collection<org.bukkit.entity.Entity>

  • EntitySelector.MANY_PLAYERS - Collection<org.bukkit.entity.Player>

  • EntitySelector.ONE_ENTITY - org.bukkit.entity.Entity

  • EntitySelector.ONE_PLAYER - org.bukkit.entity.Player
EntityTypeArgumentorg.bukkit.entity.EntityType
EnvironmentArgumentorg.bukkit.World.Environment
FloatArgumentfloat
FloatRangeArgumentdev.jorel.commandapi.wrappers.FloatRange
FunctionArgumentdev.jorel.commandapi.wrappers.FunctionWrapper[]
GreedyStringArgumentString
IntegerArgumentint
IntegerRangeArgumentdev.jorel.commandapi.wrappers.IntegerRange
ItemStackArgumentorg.bukkit.inventory.ItemStack
ItemStackPredicateArgumentjava.util.function.Predicate<org.bukkit.inventory.ItemStack>
LiteralArgumentN/A
Location2DArgumentdev.jorel.commandapi.wrappers.Location2D
LocationArgumentorg.bukkit.Location
LongArgumentlong
LootTableArgumentorg.bukkit.loot.LootTable
MathOperationArgumentdev.jorel.commandapi.wrappers.MathOperation
MultiLiteralArgumentString
NBTCompoundArgumentde.tr7zw.nbtapi.NBTContainer
ObjectiveArgumentString
ObjectiveCriteriaArgumentString
ParticleArgumentorg.bukkit.Particle
PlayerArgumentorg.bukkit.entity.Player
PotionEffectArgumentorg.bukkit.potion.PotionEffectType
RecipeArgumentThe cast type changes depending on your Minecraft version:
  • Version 1.14.4 and below - org.bukkit.inventory.Recipe

  • 1.15 and above - org.bukkit.inventory.ComplexRecipe
RotationArgumentdev.jorel.commandapi.wrappers.Rotation
ScoreboardSlotArgumentdev.jorel.commandapi.wrappers.ScoreboardSlot
ScoreHolderArgumentThe cast type changes depending on the input parameter:
  • ScoreHolderType.SINGLE - String

  • ScoreHolderType.MULTIPLE - Collection<String>
SoundArgumentorg.bukkit.Sound
StringArgumentString
TeamArgumentString
TextArgumentString
TimeArgumentint
UUIDArgumentjava.util.UUID

Optional/Different Arguments

Sometimes, you want to register a command that has a different effect whether arguments are included or not. For example, take the /kill command. If you run /kill on its own, it will kill the command sender. If however you run /kill <target>, it will kill the target. In other words, we have the following command structure:

/kill          - Kills yourself
/kill <target> - Kills a target player

As shown by the command structure, we need to register two commands.

Example - /kill command with two separate arguments

For example, say we're registering a command /kill:

/kill          - Kills yourself
/kill <target> - Kills a target player

We first register the first /kill command as normal:

new CommandAPICommand("kill")
    .executesPlayer((player, args) -> {
        player.setHealth(0);
    })
    .register();

Now we declare our command with arguments for our second command. Then, we can register our second command /kill <target> as usual:

// Register our second /kill <target> command
new CommandAPICommand("kill")
    .withArguments(new PlayerArgument("target"))
    .executesPlayer((player, args) -> {
        ((Player) args[0]).setHealth(0);
    })
    .register();

This gives us the ability to run both /kill and /kill <target> with the same command name "kill", but have different results based on the arguments used.

In this example, we use the simpler, inline .withArguments(Argument... arguments) method to register our argument. There is no difference to using this method as opposed to explicitly declaring a list and using .withArguments(List<Argument> arguments), so feel free to use whichever method you want!

Argument suggestions

Sometimes, you want to override the list of suggestions that are provided by an argument. To handle this, CommandAPI arguments contain three methods to override suggestions:

Argument overrideSuggestions(String... suggestions);
Argument overrideSuggestions(Function<CommandSender, String[]> suggestions);
Argument overrideSuggestions(BiFunction<CommandSender, Object[], String[]> suggestions);

Argument suggestion deferral

Before we go into detail about what the above methods do, we must first describe suggestion deferral. When the server loads, arguments that are provided with a String array are fixed, and do not change. However, arguments that are provided using the function and bifunction parameters are evaluated when they are needed - their execution is deferred until requested by the user. As such, if you wanted to retrieve something that isn't available during server load but is available during normal server running, it is recommended to use either of those functions instead of the String array.

For example, instead of doing the following, which retrieves a list of worlds on the server:

overrideSuggestions(Bukkit.getWorlds().stream().map(World::getName).toArray(String[]::new))

You should defer it using the function parameter:

overrideSuggestions(sender -> Bukkit.getWorlds().stream().map(World::getName).toArray(String[]::new))

Suggestions with a String Array

The first method, overrideSuggestions(String... suggestions), allows you to replace the suggestions normally associated with that argument with an array of strings. As described above, this doesn't use suggestion deferral, so this list will not update automatically.

Example - Teleport to worlds by overriding suggestions

Say we're creating a plugin with the ability to teleport to different warps on the server. If we were to retrieve a list of warps, we would be able to override the suggestions of a typical StringArgument to teleport to that warp. Let's create a command with the following structure:

/warp <warp>

We then implement our warp teleporting command using overrideSuggestions() on the StringArgument to provide a list of warps to teleport to:

List<Argument> arguments = new ArrayList<>();
arguments.add(new StringArgument("world").overrideSuggestions("northland", "eastland", "southland", "westland"));

new CommandAPICommand("warp")
    .withArguments(arguments)
    .executesPlayer((player, args) -> {
       	String warp = (String) args[0];
		player.teleport(warps.get(warp)); // Look up the warp in a map, for example
    })
    .register();

Suggestions depending on a command sender

The overrideSuggestions(Function<CommandSender, String[]> suggestions) method allows you to replace the suggestions normally associated with that argument with an array of strings that are evaluated dynamically using information about the command sender.

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. Since the list of friends depends on the sender, we can use the function to determine what our suggestions should be. Let's use the following command to teleport to a friend from our friend list:

/friendtp <friend>

Let's say we have a simple class to get the friends of a command sender:

public class Friends {
	
	static Map<UUID, String[]> friends /* = ... */;
	
    public static String[] getFriends(CommandSender sender) {
        if(sender instanceof Player) {
        	//Look up friends in a database or file
            return friends.get(((Player) sender).getUniqueId());
        } else {
            return new String[0];
        }
    }
}

We can then use this to generate our suggested list of friends:

List<Argument> arguments = new ArrayList<>();
arguments.add(new PlayerArgument("friend").overrideSuggestions((sender) -> {
    return Friends.getFriends(sender);
}));

new CommandAPICommand("friendtp")
    .withArguments(arguments)
    .executesPlayer((player, args) -> {
       	Player target = (Player) args[0];
		player.teleport(target);
    })
    .register();

Developer's Note:

The syntax of inlining the .overrideSuggestions() method has been designed to work well with Java's lambdas. For example, we could write the above code more consisely, such as:

List<Argument> arguments = new ArrayList<>();
arguments.add(new PlayerArgument("friend").overrideSuggestions(Friends::getFriends));

Suggestions depending on previous arguments

The overrideSuggestions(BiFunction<CommandSender, Object[], String[]> suggestions) method is the most powerful suggestion overriding function that the CommandAPI offers. It has the capability to suggest arguments based on the values of previously inputted arguments.

This method requires a function that takes in a command sender and the list of previous arguments and must return a String[] of suggestions. The arguments are parsed exactly like any regular CommandAPI command argument.

Note:

The ability to use previously declared arguments does not work via redirects. This means that any command that comes before it that leads into a command that uses suggestions depending on previous arguments will not work. For example, if we had a command /mycommand <arg1> <arg2> <arg3> and ran it as normal, it would work as normal:

/mycommand arg1 arg2 arg3

However, if we redirect execution via the /execute command to have the following:

/execute run mycommand <suggestions>

This won't work, because we make use of a redirect:

\[\texttt{/execute run} \xrightarrow{redirect} \texttt{mycommand arg1 arg2 arg3}\]

To clarify, by "does not work", I mean that it is not possible to access the Object[] of previously declared arguments. If a command occurs via a redirect, the Object[] of previously declared arguments will be null.

Example - Sending a message to a nearby player

Say we wanted to create a command that lets you send a message to a specific player in a given radius. (This is a bit of a contrived example, but let's roll with it). To do this, we'll use the following command structure:

/localmsg <radius> <target> <message>

When run, this command will send a message to a target player within the provided radius. To help identify which players are within a radius, we can override the suggestions on the <target> argument to include a list of players within the provided radius. We do this with the following code:

// Declare our arguments as normal
List<Argument> arguments = new ArrayList<>();
arguments.add(new IntegerArgument("radius"));

// Override the suggestions for the PlayerArgument, using (sender, args) as the parameters
// sender refers to the command sender that is running this command
// args refers to the Object[] of PREVIOUSLY DECLARED arguments (in this case, the IntegerArgument radius)
arguments.add(new PlayerArgument("target").overrideSuggestions((sender, args) -> {

    // Cast the first argument (radius, which is an IntegerArgument) to get its value
	int radius = (int) args[0];
	
    // Get nearby entities within the provided radius
	Player player = (Player) sender;
	Collection<Entity> entities = player.getWorld().getNearbyEntities(player.getLocation(), radius, radius, radius);
	
    // Get player names within that radius
	return entities.stream()
		.filter(e -> e.getType() == EntityType.PLAYER)
		.map(Entity::getName)
		.toArray(String[]::new);
}));
arguments.add(new GreedyStringArgument("message"));

// Declare our command as normal
new CommandAPICommand("localmsg")
	.withArguments(arguments)
	.executesPlayer((player, args) -> {
		Player target = (Player) args[1];
		String message = (String) args[2];
		target.sendMessage(message);
	})
	.register();

As shown in this code, we use the (sender, args) -> ... lambda to override suggestions with previously declared arguments. In this example, our variable args will be { int }, where this int refers to the radius. Note how this object array only has the previously declared arguments (and not for example { int, Player, String }).

Safe argument suggestions

So far, we've covered how to override suggestions using the overrideSuggestions() method. The issue with using Strings for suggestion listings is that they are prone to errors. As a result, some arguments include the safeOverrideSuggestions(), which provides type-safety checks for argument suggestions, as well as automatic "Bukkit-to-suggestion" conversion.

The whole point of the safe argument suggestions method is that parameters entered in this method are guaranteed to work.

The use of the safe override suggestions function is basically the same as overrideSuggestions() from the previous section, except instead of returning a String[], you now return a T[], where T is the class corresponding to the argument. This is described in more detail in the table below.

Argument safeOverrideSuggestions(T... suggestions);
Argument safeOverrideSuggestions(Function<CommandSender, T[]> suggestions);
Argument safeOverrideSuggestions(BiFunction<CommandSender, Object[], T[]> suggestions);

Supported arguments

Not all arguments support safe suggestions. This is mostly due to implementation constraints or inadequate support by the Bukkit API.

The list of supported arguments are displayed in the following table. The parameter T (shown in the method signatures above) are also provided for each argument. This parameter is the same as the cast argument described in Argument Casting, except for a few exceptions which are outlined in bold.

ArgumentClass (T)
AdvancementArgumentorg.bukkit.advancement.Advancement
AxisArgumentjava.util.EnumSet<org.bukkit.Axis>
BiomeArgumentorg.bukkit.block.Biome
BooleanArgumentBoolean
ChatColorArgumentorg.bukkit.ChatColor
DoubleArgumentDouble
EnchantmentArgumentorg.bukkit.enchantments.Enchantment
EntityTypeArgumentorg.bukkit.entity.EntityType
EnvironmentArgumentorg.bukkit.World.Environment
FloatArgumentFloat
FloatRangeArgumentdev.jorel.commandapi.wrappers.FloatRange
FunctionArgumentorg.bukkit.NamespacedKey
GreedyStringArgumentString
IntegerArgumentInteger
IntegerRangeArgumentdev.jorel.commandapi.wrappers.IntegerRange
ItemStackArgumentorg.bukkit.inventory.ItemStack
Location2DArgumentdev.jorel.commandapi.wrappers.Location2D
LocationArgumentorg.bukkit.Location
LongArgumentLong
LootTableArgumentorg.bukkit.loot.LootTable
MathOperationArgumentdev.jorel.commandapi.wrappers.MathOperation
NBTCompoundArgumentde.tr7zw.nbtapi.NBTContainer
ObjectiveArgumentorg.bukkit.scoreboard.Objective
ParticleArgumentorg.bukkit.Particle
PlayerArgumentorg.bukkit.entity.Player
PotionEffectArgumentorg.bukkit.potion.PotionEffectType
RecipeArgumentorg.bukkit.inventory.Recipe
RotationArgumentdev.jorel.commandapi.wrappers.Rotation
ScoreboardSlotArgumentdev.jorel.commandapi.wrappers.ScoreboardSlot
SoundArgumentorg.bukkit.Sound
TeamArgumentorg.bukkit.scoreboard.Team
TimeArgumentdev.jorel.commandapi.wrappers.Time

Safe time arguments

While most of the arguments are fairly straight forward, I'd like to bring your attention to the TimeArgument's safe suggestions function. This uses dev.jorel.commandapi.wrappers.Time as the class for T to ensure type-safety. The Time class has three static methods:

Time ticks(int ticks);
Time days(int days);
Time seconds(int seconds);

These create representations of ticks (e.g. 40t), days (e.g. 2d) and seconds (e.g. 60s) respectively.


Safe function arguments

Although all safe arguments are indeed "type-safe", the function argument uses a NamespacedKey which cannot be checked fully at compile time. As a result, this is argument should be used with caution - providing a NamespacedKey suggestion that does not exist when the server is running will cause that command to fail if that suggestion is used.


Safe scoreboard slot arguments

Scoreboard slots now include two new static methods so they can be used with safe arguments:

ScoreboardSlot of(DisplaySlot slot);
ScoreboardSlot ofTeamColor(ChatColor color);

This allows you to create ScoreboardSlot instances which can be used with the safe override suggestions method.


Examples

While this should be fairly straight forward, here's a few examples of how this can be used in practice:

Example - Safe recipe arguments

Say we have a plugin that registers custom items which can be crafted. In this example, we use an "emerald sword" with a custom crafting recipe. Now say that we want to have a command that gives the player the item from our declared recipes. To do this, we first register our custom items:

// Create our itemstack
ItemStack emeraldSword = new ItemStack(Material.DIAMOND_SWORD);
ItemMeta meta = emeraldSword.getItemMeta();
meta.setDisplayName("Emerald Sword");
meta.setUnbreakable(true);
emeraldSword.setItemMeta(meta);

// Create and register our recipe
ShapedRecipe emeraldSwordRecipe = new ShapedRecipe(new NamespacedKey(this, "emerald_sword"), emeraldSword);
emeraldSwordRecipe.shape(
	"AEA", 
	"AEA", 
	"ABA"
);
emeraldSwordRecipe.setIngredient('A', Material.AIR);
emeraldSwordRecipe.setIngredient('E', Material.EMERALD);
emeraldSwordRecipe.setIngredient('B', Material.BLAZE_ROD);
getServer().addRecipe(emeraldSwordRecipe);

... // Omitted, more itemstacks and recipes

Once we've done that, we can now include them in our command registration. To do this, we use safeOverrideSuggestions(recipes) and then register our command as normal:

// Safely override with the recipe we've defined
List<Argument> arguments = new ArrayList<>();
arguments.add(new RecipeArgument("recipe").safeOverrideSuggestions(emeraldSwordRecipe, /* Other recipes */));

// Register our command
new CommandAPICommand("giverecipe")
	.withArguments(arguments)
	.executesPlayer((player, args) -> {
		Recipe recipe = (Recipe) args[0];
		player.getInventory().addItem(recipe.getResult());
	})
	.register();

Example - Safe /spawnmob suggestions

Say we have a command to spawn mobs:

/spawnmob <mob>

Now say that we don't want non-op players to spawn bosses. To do this, we'll create a List<EntityType> which is the list of all mobs that non-ops are allowed to spawn:

EntityType[] forbiddenMobs = new EntityType[] {EntityType.ENDER_DRAGON, EntityType.WITHER};
List<EntityType> allowedMobs = new ArrayList<>(Arrays.asList(EntityType.values()));
allowedMobs.removeAll(Arrays.asList(forbiddenMobs)); //Now contains everything except enderdragon and wither

We then use our safe arguments to return an EntityType[] as the list of values that are suggested to the player. In this example, we use the Function<CommandSender, EntityType[]> argument to determine if the sender has permissions to view the suggestions:

List<Argument> arguments = new ArrayList<>();
arguments.add(new EntityTypeArgument("mob").safeOverrideSuggestions(
	sender -> {
		if(sender.isOp()) {
			return EntityType.values(); //All entity types
		} else {
			return allowedMobs.toArray(new EntityType[0]); //Only allowsMobs
		}
	})
);

Now we register our command as normal:

new CommandAPICommand("spawnmob")
	.withArguments(arguments)
	.executesPlayer((player, args) -> {
		EntityType entityType = (EntityType) args[0];
		player.getWorld().spawnEntity(player.getLocation(), entityType);
	})
	.register();

Example - Removing a potion effect from a player

Say we wanted to remove a potion effect from a player. To do this, we'll use the following command structure:

/removeeffect <player> <potioneffect>

Now, we don't want to remove a potion effect that already exists on a player, so instead we'll use the safe arguments to find a list of potion effects on the target player and then only suggest those potion effects. To do this, we'll use the BiFunction<CommandSender, Object[], PotionEffectType[]> parameter, as it allows us to access the previously defined <player> argument.

List<Argument> arguments = new ArrayList<>();
arguments.add(new EntitySelectorArgument("target", EntitySelector.ONE_PLAYER));
arguments.add(new PotionEffectArgument("potioneffect").safeOverrideSuggestions(
	(sender, prevArgs) -> {
		Player target = (Player) prevArgs[0];
        
        //Convert PotionEffect[] into PotionEffectType[]
		return target.getActivePotionEffects().stream()
			.map(PotionEffect::getType)
			.toArray(PotionEffectType[]::new);
	})
);

And then we can register our command as normal:

new CommandAPICommand("removeeffect")
	.withArguments(arguments)
	.executesPlayer((player, args) -> {
		EntityType entityType = (EntityType) args[0];
		player.getWorld().spawnEntity(player.getLocation(), entityType);
	})
	.register();

Argument suggestions with tooltips

The CommandAPI can also display tooltips for specific argument suggestions. These are shown to the user when they hover over a given suggestion and can be used to provide more context to a user about the suggestions that are shown to them. In this section, we'll outline the two ways of creating suggestions with tooltips:

  • Normal (String) suggestions with tooltips
  • Safe suggestions with tooltips

Tooltips can have formatting to change how the text is displayed by using the ChatColor class.


Tooltips with normal (String) suggestions

To use these features, the CommandAPI includes the overrideSuggestionsT methods for arguments, that accept IStringTooltip objects instead of String objects:

Argument overrideSuggestionsT(IStringTooltip... suggestions);
Argument overrideSuggestionsT(Collection<IStringTooltip> suggestions);
Argument overrideSuggestionsT(Function<CommandSender, IStringTooltip[]> suggestions);
Argument overrideSuggestionsT(BiFunction<CommandSender, Object[], IStringTooltip[]> suggestions);

The StringTooltip class is the CommandAPI's default implementation of IStringTooltip, which has two static methods to construct it easily:

StringTooltip none(String suggestion);
StringTooltip of(String suggestion, String tooltip);

The first method, StringTooltip.none(String) creates a normal suggestion entry with no tooltip, whereas the StringTooltip.of(String, String) method creates a suggestion with the provided tooltip text.

Example - An emotes command with string suggestion tooltips

Say we want to create a simple command to provide ingame emotes between players. For example, if you did /emote wave Bob, you'll "wave" to the player Bob. For this example, we'll use the following command structure:

/emote <emote> <target>

First, we'll declare our arguments. Here, we'll use the overrideSuggestionsT method, along with the StringTooltip.of(String, String) method to create emote suggestions and include suitable descriptions:

List<Argument> arguments = new ArrayList<>();
arguments.add(new StringArgument("emote")
	.overrideSuggestionsT( 
        StringTooltip.of("wave", "Waves at a player"),
        StringTooltip.of("hug", "Gives a player a hug"),
        StringTooltip.of("glare", "Gives a player the death glare")
	)
);
arguments.add(new PlayerArgument("target"));

Finally, we declare our command as normal:

new CommandAPICommand("emote")
	.withArguments(arguments)
	.executesPlayer((player, args) -> {
		String emote = (String) args[0];
		Player target = (Player) args[1];
		
		switch(emote) {
		case "wave":
			target.sendMessage(player.getName() + " waves at you!");
			break;
		case "hug":
			target.sendMessage(player.getName() + " hugs you!");
			break;
		case "glare":
			target.sendMessage(player.getName() + " gives you the death glare...");
			break;
		}
	})
	.register();

The IStringTooltip interface can be implemented by any other class to provide tooltips for custom objects. The IStringTooltip interface has the following methods:

public interface IStringTooltip {
	public String getSuggestion();
	public String getTooltip();
}

This is incredibly useful if you are using suggestions with custom objects, such as a plugin that has custom items.

Example - Using IStringTooltip for custom items

Let's say we've created a simple plugin which has custom items. For a custom item, we'll have a super simple class CustomItem that sets its name, lore and attached itemstack:

public class CustomItem implements IStringTooltip {

	private ItemStack itemstack;
	private String name;
	
	public CustomItem(ItemStack itemstack, String name, String lore) {
		ItemMeta meta = itemstack.getItemMeta();
		meta.setDisplayName(name);
		meta.setLore(Arrays.asList(lore));
		itemstack.setItemMeta(meta);
		this.itemstack = itemstack;
		this.name = name;
	}
	
	public String getName() {
		return this.name;
	}
	
	public ItemStack getItem() {
		return this.itemstack;
	}
	
	@Override
	public String getSuggestion() {
		return this.itemstack.getItemMeta().getDisplayName();
	}

	@Override
	public String getTooltip() {
		return this.itemstack.getItemMeta().getLore().get(0);
	}
	
}

Let's also say that our plugin has registered lots of CustomItems and has this stored in a CustomItem[] in our plugin. We could then use this as our input for suggestions:

CustomItem[] customItems = new CustomItem[] {
	new CustomItem(new ItemStack(Material.DIAMOND_SWORD), "God sword", "A sword from the heavens"),
	new CustomItem(new ItemStack(Material.PUMPKIN_PIE), "Sweet pie", "Just like grandma used to make")
};  //
	
new CommandAPICommand("giveitem")
	.withArguments(new StringArgument("item").overrideSuggestionsT(customItems)) // We use customItems[] as the input for our suggestions with tooltips
	.executesPlayer((player, args) -> {
		String itemName = (String) args[0];
		
		//Give them the item
		for(CustomItem item : customItems) {
			if(item.getName().equals(itemName)) {
				player.getInventory().addItem(item.getItem());
			}
		}
	})
	.register();

Tooltips with safe suggestions

Using tooltips with safe suggestions is almost identical to the method described above for normal suggestions, except for two things. Firstly, you must use safeOverrideSuggestionsT method instead of the overrideSuggestionsT method and secondly, instead of using StringTooltip, you must use Tooltip<S>. Let's look at these differences in more detail.

The safeOverrideSuggestionsT methods are fairly similar to the overrideSuggestionsT methods, except instead of using StringTooltip, it simply uses Tooltip<S>.

Argument safeOverrideSuggestionsT(Tooltip<S>... suggestions);
Argument safeOverrideSuggestionsT(Collection<Tooltip<S>> suggestions);
Argument safeOverrideSuggestionsT(Function<CommandSender, Tooltip<S>[]> suggestions);
Argument safeOverrideSuggestionsT(BiFunction<CommandSender, Object[], Tooltip<S>[]> suggestions);

The Tooltip<S> class represents a tooltip for a given object S. For example, a tooltip that is for a LocationArgument would be a Tooltip<Location> and a tooltip for an EnchantmentArgument would be a Tooltip<Enchantment>.

Just like the StringTooltip class, the Tooltip<S> class provides the following static methods, which operate exactly the same as the ones in the StringTooltip class:

Tooltip<S> none(S object);
Tooltip<S> of(S object, String tooltip);
Tooltip<S>[] arrayOf(Tooltip<S>... tooltips);

The use of arrayOf is heavily recommended as it provides the necessary type safety for Java code to ensure that the correct types are being passed to the safeOverrideSuggestionsT method.

Example - Teleportation command with suggestion descriptions

Say we wanted to create a custom teleport command which suggestions a few key locations. In this example, we'll use the following command structure:

/warp <location>

First, we'll declare our arguments. Here, we use a LocationArgument and use the safeOverrideSuggestionsT method, with a parameter for the command sender, so we can get information about the world. We populate the suggestions with tooltips using Tooltip.of(Location, String) and collate them together with Tooltip.arrayOf(Tooltip<Location>...):

List<Argument> arguments = new ArrayList<>();
arguments.add(new LocationArgument("location")
    .safeOverrideSuggestionsT((sender) -> {
        return Tooltip.arrayOf(
            Tooltip.of(((Player) sender).getWorld().getSpawnLocation(), "World spawn"),
            Tooltip.of(((Player) sender).getBedSpawnLocation(), "Your bed"),
            Tooltip.of(((Player) sender).getTargetBlockExact(256).getLocation(), "Target block")
        );
    }));

In the arguments declaration, we've casted the command sender to a player. To ensure that the command sender is definitely a player, we'll use the executesPlayer command execution method in our command declaration:

new CommandAPICommand("warp")
	.withArguments(arguments)
	.executesPlayer((player, args) -> {
	    player.teleport((Location) args[0]);
	})
	.register();

Listed arguments

Arguments have a setting which determine whether or not they are present in the Object[] args that is populated when executing a command.

By default, the LiteralArgument has this setting set to false, hence the literal values are not present in the Object[] args.

This flag is set using the following function:

Argument setListed(boolean listed);

Example - Setting listed arguments

Say we have the following command:

/mycommand <player> <value> <message>

Let's also say that in our implementation of this command, we don't actually perform any processing for <value>. Hence, listing it in the Object args[] is unnecessary.

new CommandAPICommand("mycommand")
    .withArguments(new PlayerArgument("player"))
    .withArguments(new IntegerArgument("value").setListed(false))
    .withArguments(new GreedyStringArgument("message"))
    .executes((sender, args) -> {
    	// args == [player, message]
    	Player player = (Player) args[0];
    	String message = (String) args[1]; //Note that this is args[1] and NOT args[2]
        player.sendMessage(message);
    })
    .register();

In this scenario, the argument <value> is not present in the Object args[] for the executor.

Argument types

Primitive arguments

Primitive arguments are arguments that represent Java primitive types, such as int, float, double, boolean and long. These arguments are defined in their respective classes:

Primitive typeCommandAPI class
intIntegerArgument
floatFloatArgument
doubleDoubleArgument
longLongArgument
booleanBooleanArgument

These arguments simply cast to their primitive type and don't need any extra work.


Boolean arguments

The BooleanArgument class represents the Boolean values true and false.

Example - Config editing plugin

Say we want to create a plugin that lets you edit its own config.yml file using a command. To do this, let's create a command with the following structure:

/editconfig <config-key> <value>

We first retrieve the keys from the configuration file using the typical Bukkit API. We construct our List to hold our arguments, with the first parameter being a String key (in the form of a TextArgument, overridden with an array of suggestions). Finally, we register our command and update the config, ensuring that we cast the BooleanArgument to boolean:

// Load keys from config file
String[] configKeys = getConfig().getKeys(true).toArray(new String[0]);

// Create arguments with the config key and a boolean value to set it to
List<Argument> arguments = new ArrayList<>();
arguments.add(new TextArgument("config-key").overrideSuggestions(configKeys));
arguments.add(new BooleanArgument("value"));

// Register our command
new CommandAPICommand("editconfig")
    .withArguments(arguments)
    .executes((sender, args) -> {
        // Update the config with the boolean argument
        getConfig().set((String) args[0], (boolean) args[1]);
    })
    .register();
}

Numerical arguments

Numbers are represented using the designated number classes:

ClassDescription
IntegerArgumentWhole numbers between Integer.MIN_VALUE and Integer.MAX_VALUE
LongArgumentWhole numbers between Long.MIN_VALUE and Long.MAX_VALUE
DoubleArgumentDouble precision floating point numbers
FloatArgumentSingle 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:

ConstructorDescription
new IntegerArgument()Any range
new IntegerArgument(min)Values greater than or equal to min
new IntegerArgument(min, max)Values greater than or equal to min and less than or equal to max

Each range is inclusive, so it includes the number given to it. If the minimum value provided is larger than the maximum value, an InvalidRangeException is thrown.

Ranged arguments

Ranged arguments allow players to provide a range between two numbers, all within a single argument. The CommandAPI provides two ranged arguments, IntegerRangeArgument for ranges with only integer values, and FloatRangeArgument for ranged with potential floating point values.

These consist of values such as:

InputWhat it means
5The number 5
5..10Numbers between 5 and 10, including 5 and 10
5..Numbers greater than or equal to 5 (bounded by Java's max number size)
..5Numbers less than or equal to 5 (bounded by Java's min number size)

This allows you to let users define a range of values, which can be used to limit a value, such as the number of players in a region or for a random number generator.


The IntegerRange & FloatRange class

The CommandAPI returns an IntegerRange from the IntegerRangeArgument, and a FloatRange from the FloatRangeArgument, which represents the upper and lower bounds of the numbers provided by the command sender, as well as a method to check if a number is within that range.

The IntegerRange class has the following methods:

class IntegerRange {
    public int getLowerBound();
    public int getUpperBound();
    public boolean isInRange(int);
}

The FloatRange class has the following methods:

class FloatRange {
    public float getLowerBound();
    public float getUpperBound();
    public boolean isInRange(float);
}

Example - Searching chests for certain items

Say you're working on a plugin for server administrators to help them find restricted items. A method of doing so would be to search chests in a given radius for certain items. As such, we can use the following structure:

/searchchests <range> <item>

Now, we simply create our arguments using IntegerRangeArgument for our range and ItemStackArgument as the item to search for. We can then find all chests in a given area and determine if it is within the range provided by the command sender by using range.isInRange(distance):

// Declare our arguments for /searchrange <IntegerRange> <ItemStack>
List<Argument> arguments = new ArrayList<>();
arguments.add(new IntegerRangeArgument("range"));
arguments.add(new ItemStackArgument("item"));

new CommandAPICommand("searchrange")
    .withArguments(arguments)
    .executesPlayer((player, args) -> {
        // Retrieve the range from the arguments
        IntegerRange range = (IntegerRange) args[0];
        ItemStack itemStack = (ItemStack) args[1];

        // Store the locations of chests with certain items
        List<Location> locations = new ArrayList<>();

        // Iterate through all chunks, and then all tile entities within each chunk
        for(Chunk chunk : player.getWorld().getLoadedChunks()) {
            for(BlockState blockState : chunk.getTileEntities()) {

                // The distance between the block and the player
                int distance = (int) blockState.getLocation().distance(player.getLocation());

                // Check if the distance is within the specified range 
                if(range.isInRange(distance)) {

                    // Check if the tile entity is a chest
                    if(blockState instanceof Chest) {
                        Chest chest = (Chest) blockState;

                        // Check if the chest contains the item specified by the player
                        if(chest.getInventory().contains(itemStack.getType())) {
                            locations.add(chest.getLocation());
                        }
                    }
                }

            }
        }

        // Output the locations of the chests, or whether no chests were found
        if(locations.isEmpty()) {
            player.sendMessage("No chests were found");
        } else {
            player.sendMessage("Found " + locations.size() + " chests:");
            locations.forEach(location -> {
                player.sendMessage("  Found at: " 
                        + location.getX() + ", " 
                        + location.getY() + ", " 
                        + location.getZ());
            });
        }
    })
    .register();

String arguments

There are three types of arguments that return Java's String object. Each have their own unique set of features which make them suitable for specific needs.


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

Examples of StringArgument uses:

  • 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: ""

Examples of TextArgument uses:

  • Editing the contents of a sign
  • A command that requires multiple text arguments (say, username and password?)

Greedy string argument

Greedy Arguments:

The GreedyStringArgument, similar to the ChatArgument uses the entire argument array from its current position. This means that it never ends, therefore if it is used, it must be the last element of your List of arguments.

For example, if you have a command /message <message> <target>, it would not be able to determine where the message ends and the <target> argument begins.

If a GreedyStringArgument or ChatArgument is not declared at the end of the List of arguments, or multiple of these arguments are used in the same List, the CommandAPI throws a GreedyArgumentException.

The GreedyStringArgument takes the TextArgument a step further. Any characters and symbols are allowed and quotation marks are not required.

Example - Messaging command

Say we have a simple message command of the following form:

/message <target> <message>

This would be ideal for a greedy string, since it can consume all text after the player's name:

List<Argument> arguments = new ArrayList<>();
arguments.add(new PlayerArgument("target"));
arguments.add(new GreedyStringArgument("message"));

new CommandAPICommand("message")
    .withArguments(arguments)
    .executes((sender, args) -> {
        ((Player) args[0]).sendMessage((String) args[1]);
    })
    .register();

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

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

Note how this only works if the greedy string argument is at the end. If, say, the command was /message <message> <target>, it would not be able to determine where the <message> argument ends and the <target> argument begins.

Examples of GreedyStringArgument uses:

  • A messaging/whisper command (as shown in the example above)
  • A mailing command
  • Any command involving lots of text, such as a command to write the contents of a book
  • Any command which involves an unreasonable/unknown amount of arguments
  • Any command where you want to parse arguments similar to how regular Bukkit would

Position-based arguments

Location arguments

In the CommandAPI, there are two arguments used to represent location. The LocationArgument argument, which represents a 3D location \( (x, y, z) \) and the Location2DArgument, which represents 2D location \( (x, z) \).


Location (3D space)

The LocationArgument class is used to specify a location in the command sender's current world, returning a Bukkit Location object. It 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. The LocationType enum consists of two values:

LocationType.BLOCK_POSITION

BLOCK_POSITION refers to integer block coordinates. When in-game as a player, the suggested location is the coordinates of block you are looking at when you type the command.

BLOCK_POSITION

LocationType.PRECISE_POSITION

PRECISE_PRECISION uses exact coordinates, using the double primitive type. When in-game as a player, the suggested location is the exact coordinates of where your cursor is pointing at when you type the command.

PRECISE_POSITION

If no LocationType is provided, the LocationArgument will use PRECISE_POSITION by default.


Example - Break block using coordinates

We can declare a simple command to break a block:

/break <location>

Simply put, given the coordinates provided to the command, "break" the block by setting it's type to Material.AIR. For this example, we're referring to block specific coordinates, so we want to use LocationType.BLOCK_POSITION:

new CommandAPICommand("break")
    //We want to target blocks in particular, so use BLOCK_POSITION
    .withArguments(new LocationArgument("block", LocationType.BLOCK_POSITION))
    .executesPlayer((player, args) -> {
        ((Location) args[0]).getBlock().setType(Material.AIR);
    })
    .register();

Location (2D space)

The Location2DArgument is pretty much identical in use to the LocationArgument for 3D coordinates, except instead of returning a Location object, it instead returns a Location2D object that extends Location (thus, being compatible anywhere you would normally be able to use Location).

Note:

The Location2DArgument cannot be used with LocationType.PRECISE_POSITION in Minecraft 1.13. However, it can be used normally with LocationType.PRECISE_POSITION in Minecraft versions 1.13.1 and later.

Rotation arguments

The RotationArgument allows users to specify a pair of pitch and yaw coordinates. By default (using the ~ symbol), this refers to the player's current pitch and yaw of where they are looking at.

The RotationArgument class returns a Rotation object, which consists of the following methods:

Method nameWhat it does
float getPitch()Returns a player's pitch (up and down rotation)
float getYaw()Returns a player's yaw (left and right rotation)
float getNormalizedPitch()Returns a player's pitch between -90 and 90 degrees
float getNormalizedYaw()Returns a player's yaw between -180 and 180 degrees

Example: Rotate an armor stand head

Say we want to make an armor stand look in a certain direction. To do this, we'll use the following command:

/rotate <rotation> <target>

To do this, we'll use the rotation from the RotationArgument and select an entity using the EntitySelectorArgument, with EntitySelector.ONE_ENTITY. We then check if our entity is an armor stand and if so, we set its head pose to the given rotation.

new CommandAPICommand("rotate")
    .withArguments(new RotationArgument("rotation"))
    .withArguments(new EntitySelectorArgument("target", EntitySelector.ONE_ENTITY))
    .executes((sender, args) -> {
        Rotation rotation = (Rotation) args[0];
        Entity target = (Entity) args[1];

        if(target instanceof ArmorStand) {
            ArmorStand a = (ArmorStand) target;
            a.setHeadPose(new EulerAngle(Math.toRadians(rotation.getPitch()), Math.toRadians(rotation.getYaw() - 90), 0));
        }
    })
    .register();

Note how the head pose requires an EulerAngle as opposed to a pitch and yaw. To account for this, we convert our rotation (which is in degrees) into an EulerAngle in radians.

AxisArgument

The AxisArgument class refers to the x, y and z axes. When used with the CommandAPI, it returns an EnumSet<Axis> (You can view the documentation for EnumSet here).

Examples of AxisArgument uses:

  • Reflecting a structure in the x, y or z axis

Chat arguments

The CommandAPI provides three main classes to interact with chat formatting in Minecraft.

Chat color argument

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

Example - Username color changing plugin

Say we want to create a plugin to change the color of a player's username. We want to create a command of the following form:

/namecolor <chatcolor>

We then use the ChatColorArgument to change the player's name color:

new CommandAPICommand("namecolor")
    .withArguments(new ChatColorArgument("chatcolor"))
    .executesPlayer((player, args) -> {
        ChatColor color = (ChatColor) args[0];
        player.setDisplayName(color + player.getName());
    })
    .register();

Spigot-based chat arguments

Developer's Note:

The two following classes, ChatComponentArgument and ChatArgument depend on a Spigot based server. This means that these arguments 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:

Chat component argument

The ChatComponentArgument class accepts raw chat-based JSON as valid input. Despite being regular JSON, it must conform to the standard declared here, which consists of JSON that has a limited subset of specific keys (In other words, you can have a JSON object that has the key text, but not one that has the key blah).

This is converted into Spigot's BaseComponent[], which can be used for the following:

  • Broadcasting messages to all players on the server using:

    Bukkit.getServer().spigot().broadcast(BaseComponent[]);
    
  • Adding and setting pages to books using BookMeta:

    BookMeta meta = // ...
    meta.spigot().setPages(BaseComponent[]);
    
  • Sending messages to Player objects:

    Player player = // ...
    player.spigot().sendMessage(BaseComponent[]);
    
  • Sending messages to CommandSender objects:

    CommandSender sender = // ...
    sender.spigot().sendMessage(BaseComponent[]);
    

Example - Book made from raw JSON

Say we want to generate a book using raw JSON. For this example, we'll use the following JSON (generated from minecraftjson.com) to generate our book:

["", {
    "text": "Once upon a time, there was a guy call "
}, {
    "text": "Skepter",
    "color": "light_purple",
    "hoverEvent": {
        "action": "show_entity",
        "value": "Skepter"
    }
}, {
    "text": " and he created the "
}, {
    "text": "CommandAPI",
    "underlined": true,
    "clickEvent": {
        "action": "open_url",
        "value": "https://github.com/JorelAli/CommandAPI"
    }
}]

Since we're writing a book, we must ensure that all quotes have been escaped. This can also be performed on the minecraftjson.com website by selecting "book":

["[\"\",{\"text\":\"Once upon a time, there was a guy call \"},{\"text\":\"Skepter\",\"color\":\"light_purple\",\"hoverEvent\":{\"action\":\"show_entity\",\"value\":\"Skepter\"}},{\"text\":\" and he created the \"},{\"text\":\"CommandAPI\",\"underlined\":true,\"clickEvent\":{\"action\":\"open_url\",\"value\":\"https://github.com/JorelAli/CommandAPI\"}}]"]

Now let's define our command. Since book text is typically very large - too large to be entered into a chat, we'll make a command block compatible command by providing a player parameter:

/makebook <player> <contents>

Now we can create our book command. We use the player as the main target by using their name for the author field, as well as their inventory to place the book. We finally construct our book using the .setPages(BaseComponent[]) method:

new CommandAPICommand("makebook")
    .withArguments(new PlayerArgument("player"))
    .withArguments(new ChatComponentArgument("contents"))
    .executes((sender, args) -> {
        Player player = (Player) args[0];
        BaseComponent[] arr = (BaseComponent[]) args[1];
        
        //Create book
        ItemStack is = new ItemStack(Material.WRITTEN_BOOK);
        BookMeta meta = (BookMeta) is.getItemMeta(); 
        meta.setTitle("Custom Book");
        meta.setAuthor(player.getName());
        meta.spigot().setPages(arr);
        is.setItemMeta(meta);
        
        //Give player the book
        player.getInventory().addItem(is);
    })
    .register();

Chat argument

Developer's Note:

It has been observed that the ChatArgument does not work on Spigot 1.16.1. This is not the case however for Spigot versions 1.15.2 and below, as well as Spigot 1.16.2.

Note:

The ChatArgument class is an argument similar to the GreedyStringArgument, in the sense that it has no terminator and must be defined at the end of your List of arguments. For more information on this, please read the section on Greedy arguments.

The ChatArgument is basically identical to the GreedyStringArgument, with the added functionality of enabling entity selectors, such as @e, @p and so on. The ChatArgument also returns a BaseComponent[], similar to the ChatComponentArgument.

Example - Sending personalized messages to players

Say we wanted to broadcast a "personalized" message to players on the server. By "personalized", we mean a command which changes its output depending on who we are sending the output to. Simply put, we want a command of the following structure:

/pbroadcast <message>

Say we're on a server with 2 players: Bob and Michael. If I were to use the following command:

/pbroadcast Hello @p

Bob would receive the message "Hello Bob", whereas Michael would receive the message "Hello Michael". We can use the ChatArgument to create this "personalized" broadcast:

new CommandAPICommand("pbroadcast")
    .withArguments(new ChatArgument("message"))
    .executes((sender, args) -> {
        BaseComponent[] message = (BaseComponent[]) args[0];
    
        //Broadcast the message to everyone on the server
        Bukkit.getServer().spigot().broadcast(message);
    })
    .register();

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 - Remove entities command

Say we want a command to remove certain types of entities. Typically, this would be implemented using a simple command like:

/remove <player>
/remove <mob type>
/remove <radius>

Instead, we can combine all of these into one by using the EntitySelectorArgument. We want to be able to target multiple entities at a time, so we want to use the EntitySelector.MANY_ENTITIES value in our constructor. We can simply retrieve the Collection<Entity> from this argument and iteratively remove each entity:

new CommandAPICommand("remove")
    //Using a collective entity selector to select multiple entities
    .withArguments(new EntitySelectorArgument("entities", EntitySelector.MANY_ENTITIES))
    .executes((sender, args) -> {
        //Parse the argument as a collection of entities (as stated above in the documentation)
        @SuppressWarnings("unchecked")
        Collection<Entity> entities = (Collection<Entity>) args[0];
        
        sender.sendMessage("Removed " + entities.size() + " entities");
        for(Entity e : entities) {
            e.remove();
        }
    })
    .register();

We could then use this to target specific entities, for example:

  • To remove all cows:
    /remove @e[type=cow]
    
  • To remove the 10 furthest pigs from the command sender:
    /remove @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).

(Of course, if anyone is able to confirm any major differences between the PlayerArgument and the EntitySelectorArgument(EntitySelector.ONE_PLAYER), I would be more than happy to include your findings in the documentation. If so, feel free to make a documentation amendment here.)


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

Say we want a command to spawn a specific type of entity, similar to the /summon command in Vanilla Minecraft, with the addition of specifying how many entities to spawn. We want to create a command of the following form:

/spawnmob <entity> <amount>

Since we're trying to specify an entity type, we will use the EntityTypeArgument as our argument type for <entity>. We combine this with the IntegerArgument class with a specified range of \( 1 \le \textit{amount} \le 100 \):

new CommandAPICommand("spawnmob")
    .withArguments(new EntityTypeArgument("entity"))
    .withArguments(new IntegerArgument("amount", 1, 100)) //Prevent spawning too many entities
    .executesPlayer((Player player, Object[] args) -> {
        for(int i = 0; i < (int) args[1]; i++) {
            player.getWorld().spawnEntity(player.getLocation(), (EntityType) args[0]);
        }
    })
    .register();

Note how in this example above, we have to explicitly state Player player, Object[] args. This is due to a limitation of Java's type inference system which is discussed here.

Scoreboard arguments

The scoreboard arguments that the CommandAPI provides allows you to interact with various scoreboard elements, such as objectives, teams and score holders. Since commands are registered in the onEnable() or onLoad() method of a plugin, this means that these are registered before Bukkit's main scoreboard is loaded.

This means that calling Bukkit.getScoreboardManager().getMainScoreboard() will always result in a NullPointerException. To avoid this scenario, try using a lambda which delays the call that gets Bukkit's main scoreboard to the moment when the command is executed, which typically occurs after the server has initialized it. For example, if you wanted to populate a TeamArgument with a list of all registered teams on the server, you should use the following:

List<Argument> arguments = new ArrayList<>();
	        	
arguments.add(new TeamArgument("team").safeOverrideSuggestions(s ->
    Bukkit.getScoreboardManager().getMainScoreboard().getTeams().toArray(new Team[0]))
);

Scoreboard arguments

The CommandAPI uses two classes to provide information about a scoreboard:

  • The ScoreHolderArgument class represents score holder - a player's name or an entity's UUID that has scores in an objective. This is described in more detail on the Minecraft Wiki.
  • The ScoreboardSlotArgument class represents a display slot (sidebar, list or belowName) as well as the team color if the display is the sidebar. This is described in more detail on the Minecraft Wiki.

Score holder argument

The score holder argument can accept either a single entity or a collection of multiple entities. In order to specify which one to use, you must provide a ScoreHolderType enum value to the ScoreHolderArgument constructor, which is either ScoreHolderType.SINGLE or ScoreHolderType.MULTIPLE:

new ScoreHolderArgument(nodeName, ScoreHolderType.SINGLE);
new ScoreHolderArgument(nodeName, ScoreHolderType.MULTIPLE);

Depending on which constructor is used, the cast type changes. If you use a ScoreHolderType.SINGLE, the argument must be casted to a String. Otherwise, if you use ScoreHolderType.MULTIPLE, the argument must be casted to a Collection<String>.

Example - Rewarding players with scoreboard objectives

Say we want to reward all players that fit a certain criteria. We want a command with the following structure:

/reward <players>

Since we could have multiple players that fit a certain criterion, we want to use ScoreHolderType.MULTIPLE as the parameter for the argument's constructor.

To give this example a bit more context, let's say we want to reward all players that have died less than 10 times in the server. To do this, we will use the following command:

/reward @e[type=player,scores={deaths=..9}]

Note how we use ..9 to represent 9 or less deaths (since ranges are inclusive). Also note how we restrict our input to players via the command using type=player. We can now implement our command:

new CommandAPICommand("reward")
    //We want multiple players, so we use ScoreHolderType.MULTIPLE in the constructor
    .withArguments(new ScoreHolderArgument("players", ScoreHolderType.MULTIPLE))
    .executes((sender, args) -> {
        //Get player names by casting to Collection<String>
        @SuppressWarnings("unchecked")
        Collection<String> players = (Collection<String>) args[0];
        
        for(String playerName : players) {
            Bukkit.getPlayer(playerName).getInventory().addItem(new ItemStack(Material.DIAMOND, 3));
        }
    })
    .register();

Developer's Note:

In the example above, we have our user use the @e[type=player] entity selector to restrict the Collection<String> so it only returns player names, which allows us to use Bukkit.getPlayer(playerName). In practice, we cannot guarantee that such a selector will be used, so we could update the code to accept both entities and players. For example, we can differentiate between players and entities by using the UUID.fromString(String) method:

Collection<String> entitiesAndPlayers = (Collection<String>) args[0];
for(String str : entitiesAndPlayers) {
    try {
        UUID uuid = UUID.fromString(str);
        //Is a UUID, so it must by an entity
        Bukkit.getEntity(uuid);
    } catch(IllegalArgumentException exception) {
        //Not a UUID, so it must be a player name
        Bukkit.getPlayer(str); 
    }
}

Scoreboard slot argument

The ScoreboardSlotArgument represents where scoreboard information is displayed. Since the Bukkit scoreboard DisplaySlot is not able to represent the case where team colors are provided, the CommandAPI uses the ScoreboardSlot wrapper class as the representation of the ScoreboardSlotArgument.

ScoreboardSlot wrapper

The ScoreboardSlot wrapper class has 3 methods:

class ScoreboardSlot {
    public DisplaySlot getDisplaySlot();
    public ChatColor getTeamColor();
    public boolean hasTeamColor();
}

The getDisplaySlot() method returns the display slot that was chosen. If the display slot is DisplaySlot.SIDEBAR and hasTeamColor() returns true, then it is possible to use getTeamColor() to get the team color provided.

Example - Clearing objectives in a scoreboard slot

Say we want to clear all objectives in a specific scoreboard slot. In this example, we will use the main server scoreboard, which is accessed using Bukkit.getScoreboardManager.getMainScoreboard(). We want a command with the following structure:

/clearobjectives <slot>

We implement this simply by using the ScoreboardSlotArgument as our argument, and then we can clear the slot using the scoreboard clearSlot(DisplaySlot) method.

new CommandAPICommand("clearobjectives")
    .withArguments(new ScoreboardSlotArgument("slot"))
    .executes((sender, args) -> {
        Scoreboard scoreboard = Bukkit.getScoreboardManager().getMainScoreboard();
        DisplaySlot slot = ((ScoreboardSlot) args[0]).getDisplaySlot();
        scoreboard.clearSlot(slot);
    })
    .register();

Objective arguments

In the CommandAPI, objectives are split into two classes:

  • The ObjectiveArgument class, which represents objectives as a whole
  • The ObjectiveCriteriaArgument class, which represents objective criteria

Objective argument

The objective argument refers to a single scoreboard objective. Unconventionally, the ObjectiveArgument must be cast to String due to implementation limitations.

Developer's Note:

The two classes ObjectiveArgument and TeamArgument must both be cast to String, as opposed to Objective and Team respectively. This is due to the fact that commands are typically registered in the onLoad() method during a plugin's initialization. At this point in the server start-up sequence, the main server scoreboard is not initialized, so it cannot be used.

Example - Move objective to sidebar

As an example, let's create a command to move an objective to a player's sidebar. To do this, we will use the following command structure:

/sidebar <objective>

Given that an objective has to be casted to a String, we have to find a way to convert it from its name to a Bukkit Objective object. We can do that by using the getObjective(String) method from a Bukkit Scoreboard:

new CommandAPICommand("sidebar")
    .withArguments(new ObjectiveArgument("objective"))
    .executes((sender, args) -> {
        //The ObjectArgument must be casted to a String
        String objectiveName = (String) args[0];
        
        //An objective name can be turned into an Objective using getObjective(String)
        Objective objective = Bukkit.getScoreboardManager().getMainScoreboard().getObjective(objectiveName);
        
        //Set display slot
        objective.setDisplaySlot(DisplaySlot.SIDEBAR);
    })
    .register();

Objective criteria argument

The ObjectiveCriteriaArgument is fairly straight forward - it represents the criteria for an objective. Similar to Bukkit, the objective criteria is simply represented as a String, so it must be casted to a String when being used.

Example - Unregister all objectives by criteria

Say we wanted to create a command to unregister all objectives based on a given criteria. Let's create a command with the following form:

/unregisterall <objective critera>

To do this, we're going to take advantage of Bukkit's Scoreboard.getObjectivesByCriteria(String) method

new CommandAPICommand("unregisterall")
    .withArguments(new ObjectiveCriteriaArgument("objective criteria"))
    .executes((sender, args) -> {
        String objectiveCriteria = (String) args[0];
        Set<Objective> objectives = Bukkit.getScoreboardManager().getMainScoreboard().getObjectivesByCriteria(objectiveCriteria);
        
        //Unregister the objectives
        for(Objective objective : objectives) {
            objective.unregister();
        }
    })
    .register();

Team arguments

The TeamArgument class interacts with the Minecraft scoreboard and represents a team. Similar to the ObjectiveArgument class, the TeamArgument class must be casted to a String.

Example - Toggling friendly fire in a team

Let's say we want to create a command to toggle the state of friendly fire in a team. We want a command of the following form

/togglepvp <team>

To do this, given a team we want to use the setAllowFriendlyFire(boolean) function. As with the ObjectiveArgument, we must convert the String into a Team object.

new CommandAPICommand("togglepvp")
    .withArguments(new TeamArgument("team"))
    .executes((sender, args) -> {
        //The TeamArgument must be casted to a String
        String teamName = (String) args[0];
        
        //A team name can be turned into a Team using getTeam(String)
        Team team = Bukkit.getScoreboardManager().getMainScoreboard().getTeam(teamName);
        
        //Toggle pvp
        team.setAllowFriendlyFire(team.allowFriendlyFire());
    })
    .register();

Miscellaneous arguments

Angle arguments

The angle argument is used to represent the yaw (horizontal) angle in degrees. The value returned from this argument range from -180.0 (inclusive) to 180 (exclusive), with -180.0 being due north:

\begin{align} -1&80.0 \\ &\hspace{0.1em}N \\ &\uparrow \\ 90.0\ W \leftarrow &\hspace{0.75em}\rightarrow E\ -90.0 \\ &\downarrow \\ &\hspace{0.2em}S \\ &0.0 \\ \end{align}

The ~ notation can be used to specify a rotation relative to the executor's yaw angle.

Note:

The AngleArgument is only supported in Minecraft versions 1.16.2 and later, meaning it will not work on Minecraft versions 1.16 or 1.16.1. This is due to the fact that Minecraft added the time argument in 1.16.2. Attempting to use the AngleArgument on an incompatible version will throw aa AngleArgumentException.

Advancement arguments

The AdvancementArgument class represents in-game advancements. As expected, the AdvancementArgument can be casted to Bukkit's Advancement class.

Example - Awarding a player an advancement

Say we want to award a player an advancement. First, we need the structure of our command:

/award <player> <advancement>

Since we require a player, we will use the PlayerArgument for this example. Given a player, we can simply get the AdvancementProgress for that player, and then award the criteria required to fully complete the provided advancement.

new CommandAPICommand("award")
    .withArguments(new PlayerArgument("player"))
    .withArguments(new AdvancementArgument("advancement"))
    .executes((sender, args) -> {
        Player target = (Player) args[0];
        Advancement advancement = (Advancement) args[1];
        
        //Award all criteria for the advancement
        AdvancementProgress progress = target.getAdvancementProgress(advancement);
        for(String criteria : advancement.getCriteria()) {
            progress.awardCriteria(criteria);
        }
    })
    .register();

Biome arguments

In Minecraft 1.16, they added the ability to refer to in-game biomes. The CommandAPI implements this using the BiomeArgument. As expected, this returns Bukkit's Biome enum when used.

Note:

The BiomeArgument is only supported in Minecraft versions 1.16 and later. Attempting to use the BiomeArgument on an incompatible version of Minecraft will throw a BiomeArgumentException.

Example - Setting the biome of a chunk

Say you want to set the biome of the current chunk that a player is in. We can do this using the World.setBiome(x, y, z, biome) method for a given world. We will use this command structure to set the biome of our current chunk:

/setbiome <biome>

And we can set the biome of the current chunk as expected:

new CommandAPICommand("setbiome")
	.withArguments(new BiomeArgument("biome"))
	.executesPlayer((player, args) -> {
		Biome biome = (Biome) args[0];

		Chunk chunk = player.getLocation().getChunk();
		player.getWorld().setBiome(chunk.getX(), player.getLocation().getBlockY(), chunk.getZ(), biome);
	})
	.register();

BlockState arguments

The BlockStateArgument is used to represent data about blocks in the world. These refer to any blocks that have data or states, such as dispensers, signs, doors and pistons. The BlockStateArgument creates a Bukkit BlockData object when used.

Developer's Note:

Make sure to not confuse the cast type with BlockState. The naming of this argument refers to the internal Minecraft vanilla argument naming convention - this argument casts to BlockData and NOT BlockState.

Example - Setting a block

Say we want a simple command to set the block that you're looking at. We'll use the following command structure:

/set <block>

And then we can simply set our block using setBlockData():

new CommandAPICommand("set")
    .withArguments(new BlockStateArgument("block"))
    .executesPlayer((player, args) -> {
        BlockData blockdata = (BlockData) args[0];
        Block targetBlock = player.getTargetBlockExact(256);
        
        // Set the block, along with its data
        targetBlock.setType(blockdata.getMaterial());
        targetBlock.getState().setBlockData(blockdata);
    })
    .register();

Enchantment argument

The EnchantmentArgument class lets users input a specific enchantment. As you would expect, the cast type is Bukkit's Enchantment class.

Example - Giving a player an enchantment on their current item

Say we want to give a player an enchantment on the item that the player is currently holding. We will use the following command structure:

/enchantitem <enchantment> <level>

Since most enchantment levels range between 1 and 5, we will also make use of the IntegerArgument to restrict the level of the enchantment by usng its range constructor.

new CommandAPICommand("enchantitem")
    .withArguments(new EnchantmentArgument("enchantment"))
    .withArguments(new IntegerArgument("level", 1, 5))
    .executesPlayer((player, args) -> {
        Enchantment enchantment = (Enchantment) args[0];
        int level = (int) args[1];
        
        //Add the enchantment
        player.getInventory().getItemInMainHand().addEnchantment(enchantment, level);
    })
    .register();

Environment arguments

The EnvironmentArgument class allows a command sender to refer to a specific world environment, declared in Bukkit's World.Environment class. This includes the following three environments: NORMAL, NETHER and THE_END.

Note:

The EnvironmentArgument is only supported in Minecraft versions 1.13.1 and later, meaning it will not work on Minecraft 1.13. This is due to fact that Minecraft added the environment argument in 1.13.1. Attempting to use the EnvironmentArgument on Minecraft 1.13 will throw an EnvironmentArgumentException.

Example - Creating a new world

Say we want to create a new world on our Minecraft server. To do this, we need to know the name of the world, and the type (i.e. overworld, nether or the end). As such, we want to create a command with the following structure:

/createworld <worldname> <type>

Using the world name and the environment of the world, we can use Bukkit's WorldCreator to create a new world that matches our provided specifications:

new CommandAPICommand("createworld")
    .withArguments(new StringArgument("worldname"))
    .withArguments(new EnvironmentArgument("type"))
    .executes((sender, args) -> {
        String worldName = (String) args[0];
        Environment environment = (Environment) args[1];

        // Create a new world with the specific world name and environment
        Bukkit.getServer().createWorld(new WorldCreator(worldName).environment(environment));
        sender.sendMessage("World created!");
    })
    .register();

Itemstack arguments

The ItemStackArgument class represents in-game items. As expected, this should be casted to Bukkit's ItemStack object.

Example - Giving a player an itemstack

Say we want to create a command that gives you items. For this command, we will use the following structure:

/item <itemstack>

With this structure, we can easily create our command:

new CommandAPICommand("item")
    .withArguments(new ItemStackArgument("itemstack"))
    .executesPlayer((player, args) -> {
        player.getInventory().addItem((ItemStack) args[0]);
    })
    .register();

LootTable argument

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

Example - Filling an inventory with loot table contents

new CommandAPICommand("giveloottable")
    .withArguments(new LootTableArgument("loottable"))
    .executesPlayer((player, args) -> {
        LootTable lootTable = (LootTable) args[0];
    
        /* Some generated LootContext relating to the lootTable*/
    	LootContext context = new LootContext.Builder(player.getLocation()).build();
    	
        lootTable.fillInventory(player.getInventory(), new Random(), context);
    })
    .register();

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.

MathOperation arguments

The CommandAPI's MathOperationArgument is used to represent the Minecraft scoreboard arithmetic operation to alter scoreboard scores. Since there is no default representation in the Bukkit API, the CommandAPI provides the MathOperation class to represent each operation:

Symbol (in Minecraft)MathOperation enum value
\(+=\)MathOperation.ADD
\(-=\)MathOperation.SUBTRACT
\(*=\)MathOperation.MULTIPLY
\(/=\)MathOperation.DIVIDE
\(\%=\)MathOperation.MOD
\(=\)MathOperation.ASSIGN
\(<\)MathOperation.MIN
\(>\)MathOperation.MAX
\(><\)MathOperation.SWAP

The MathOperation also has two methods:

public int apply(int val1, int val2);
public float apply(float val1, float val2);

These methods are used to provide a basic implementation of these math operations on a given input. Given the values val1 and val2, these are the operation that the apply(val1, val2) method performs:

MathOperation enum valueResult
MathOperation.ADDval1 + val2
MathOperation.SUBTRACTval1 - val2
MathOperation.MULTIPLYval1 * val2
MathOperation.DIVIDEval1 / val2
MathOperation.MODval1 % val2
MathOperation.ASSIGNval2
MathOperation.MINMath.min(val1, val2)
MathOperation.MAXMath.max(val1, val2)
MathOperation.SWAPval2

Example - Changing a player's level

Say we wanted to create a player's level. Typically, this is implemented in the following manner:

/xp set <player> <level>
/xp add <player> <levels>

Using the MathOperationArgument, we can extend the functionality of adding and setting a player's level by allowing the user to choose what operation they desire. To do this, we'll use the following structure:

/changelevel <player> <operation> <value>

As with any command, we declare our arguments, cast them properly and then we write our main code. In this example, we use the apply(int, int) method from our MathOperation to calculate the player's new level.

new CommandAPICommand("changelevel")
    .withArguments(new PlayerArgument("player"))
    .withArguments(new MathOperationArgument("operation"))
    .withArguments(new IntegerArgument("value"))
    .executes((sender, args) -> {
        Player target = (Player) args[0];
        MathOperation op = (MathOperation) args[1];
        int value = (int) args[2];

        target.setLevel(op.apply(target.getLevel(), value));
    })
    .register();

There are various applications for the changelevel command based on what the user inputs. For example:

  • To set the player Notch to level 10:

    /changelevel Notch = 10
    
  • To double the player Notch's level:

    /changelevel Notch *= 2
    
  • To set the player Notch's level to 20, or keep it as their current level if it is higher than 20:

    /changelevel Notch > 20
    

Particle arguments

The ParticleArgument class represents Minecraft particles. As expected, this is casted to the Bukkit Particle class.

Example - Show particles at a player's location

Say we wanted to have a command that displayed particles at a player's location. We will use the following command structure:

/showparticle <particle>

With this, we can simply spawn the particle using the World.spawnParticle(Particle, Location, int) method:

new CommandAPICommand("showparticle")
    .withArguments(new ParticleArgument("particle"))
    .executesPlayer((player, args) -> {
        player.getWorld().spawnParticle((Particle) args[0], player.getLocation(), 1);
    })
    .register();

Potion effect arguments

The PotionEffectArgument class represents Minecraft potion effects. When used, this argument is casted to Bukkit's PotionEffectType class.

Example - Giving a player a potion effect

Say we wanted to have a command that gives a player a potion effect. For this command, we'll use the following structure:

/potion <target> <potion> <duration> <strength>

In this example, we utilize some of the other arguments that we've described earlier, such as the PlayerArgument and TimeArgument. Since duration for the PotionEffect constructor is in ticks, this is perfectly fit for the TimeArgument, which is represented in ticks.

new CommandAPICommand("potion")
    .withArguments(new PlayerArgument("target"))
    .withArguments(new PotionEffectArgument("potion"))
    .withArguments(new TimeArgument("duration"))
    .withArguments(new IntegerArgument("strength"))
    .executes((sender, args) -> {
        Player target = (Player) args[0];
        PotionEffectType potion = (PotionEffectType) args[1];
        int duration = (int) args[2];
        int strength = (int) args[3];
        
        //Add the potion effect to the target player
        target.addPotionEffect(new PotionEffect(potion, duration, strength));
    })
    .register();

Recipe arguments

The RecipeArgument class lets you retrieve Bukkit's Recipe object. Unlike the other arguments, this argument has a slightly different implementation for Minecraft 1.15+.

Developer's Note:

In Minecraft 1.15 and onwards, Bukkit now has the ComplexRecipe class. This class is a subclass of the typical Recipe class, so casting still works when casting to Recipe. The only major difference is the ComplexRecipe class also inherits the Keyed interface, allowing you to access its NamespacedKey. This is used to grant the recipe to players. (This is shown in the second example below).

Example - Giving a player the result of a recipe

Say we want to give youself the result of a specific recipe. Since Bukkit's Recipe class contains the getResult() method, we will use that in our example. We want to create the following command:

/giverecipe <recipe>

As such, we easily implement it by specifying the RecipeArgument, casting it and adding it to the player's inventory:

new CommandAPICommand("giverecipe")
    .withArguments(new RecipeArgument("recipe"))
    .executesPlayer((player, args) -> {
        Recipe recipe = (Recipe) args[0];
    	player.getInventory().addItem(recipe.getResult());
    })
    .register();

Example - Unlocking a recipe for a player (1.15+ only)

Since 1.15 has the ComplexRecipe class, we will take advantage of this to unlock a recipe for a player. For this command, we'll use the following structure:

/unlockrecipe <player> <recipe>

Since the discoverRecipe(NamespacedKey) method for a player requires a NamespacedKey, we have to ensure that this recipe is of type ComplexRecipe. This is simply done by using instanceof. If this is the case, then we can unlock the recipe for the player. Otherwise, we cannot, so we fail gracefully using the CommandAPI.fail(String) method;

new CommandAPICommand("unlockrecipe")
    .withArguments(new PlayerArgument("player"))
    .withArguments(new RecipeArgument("recipe"))
    .executes((sender, args) -> {
        Player target = (Player) args[0];
        Recipe recipe = (Recipe) args[1];
		
        //Check if we're running 1.15+
        if(recipe instanceof ComplexRecipe) {
            ComplexRecipe complexRecipe = (ComplexRecipe) recipe;
            target.discoverRecipe(complexRecipe.getKey());
        } else {
            //Error here, can't unlock recipe for player
            CommandAPI.fail("Cannot unlock recipe for player (Are you using version 1.15 or above?)");
        }
    })
    .register();

Sound arguments

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

Say we want a simple command that plays a specific sound at your location. To do this, we will make the following command:

/sound <sound>

This command simply plays the provided sound to the current player:

new CommandAPICommand("sound")
    .withArguments(new SoundArgument("sound"))
    .executesPlayer((player, args) -> {
        player.getWorld().playSound(player.getLocation(), (Sound) args[0], 100.0f, 1.0f);
    })
    .register();

Time arguments

The TimeArgument class represents in-game time, in the number of in-game ticks. This allows command senders to specify a certain number of ticks in a simpler way, by including the characters d to specify the numbers of days, s to specify the number of seconds or t to specify a number of ticks.

The CommandAPI converts the inputs provided by the command sender into a number of ticks as an integer.

Note:

The TimeArgument is only supported in Minecraft versions 1.14 and later, meaning it will not work on Minecraft versions 1.13, 1.13.1 or 1.13.2. This is due to the fact that Minecraft added the time argument in 1.14. Attempting to use the TimeArgument on an incompatible version will throw a TimeArgumentException.

Developer's Note:

The TimeArgument provides inputs such as 2d (2 in-game days), 10s (10 seconds) and 20t (20 ticks), but does not let you combine them, such as 2d10s.

Example - Displaying a server-wide announcement

Say we have a command bigmsg that displays a title message to all players for a certain duration:

/bigmsg <duration> <message>
new CommandAPICommand("bigmsg")
    .withArguments(new TimeArgument("duration"))
    .withArguments(new GreedyStringArgument("message"))
    .executes((sender, args) -> {
        //Duration in ticks
        int duration = (int) args[0];
        String message = (String) args[1];

        for(Player player : Bukkit.getOnlinePlayers()) {
            //Display the message to all players, with the default fade in/out times (10 and 20).
            player.sendTitle(message, "", 10, duration, 20);
        }
    })
    .register();

UUID arguments

The UUID argument is used to uniquely identify players, entities and attribute modifiers. As a result, its cast type is Java's UUID.

Note:

The UUIDArgument is only compatible with Minecraft version 1.16 and above, since that is the version this argument was introduced. Attempting to use the UUID argument on a version lower than Minecraft 1.16 will throw a UUIDArgumentException.

Predicate arguments

Block predicate arguments

The BlockPredicateArgument is used to represent a "test" for Minecraft blocks. This can consist of tags, such as the ones listed here on the MinecraftWiki, or individual blocks. If a block matches the tag or block, then the predicate is satisfied.

For example, if we were to use the predicate #leaves, then the following blocks will be satisfied by that predicate: jungle_leaves, oak_leaves, spruce_leaves, dark_oak_leaves, acacia_leaves, birch_leaves.

When used, this argument must be casted to a Predicate<Block>. As with other similar arguments with parameterized types, you can ignore Java's unchecked cast type safety warning.

Example - Replacing specific blocks in a radius

Say you want to replace blocks in a given radius. To do this, we'll use the following command structure:

/replace <radius> <fromBlock> <toBlock>

Of course, we could simply use a BlockStateArgument or even an ItemStackArgument as our <fromBlock> in order to get the material to replace, but the block predicate argument provides a test for a given block, which if satisfied, allows certain code to be executed.

First, we declare our arguments. We want to use the BlockPredicateArgument since it also allows us to use Minecraft tags to identify blocks, as well as individual blocks. We then use BlockStateArgument to set the block to a given type. The BlockStateArgument also allows the user to provide any block data (e.g. contents of a chest or a stair's orientation).

Argument[] arguments = new Argument[] {
	new IntegerArgument("radius"),
	new BlockPredicateArgument("fromBlock"),
	new BlockStateArgument("toBlock"),
};

We then register our /replace command. First, we parse the arguments making sure to cast to Predicate<Block> and BlockData (and not BlockState). After that, we use a few simple for loops to find the blocks within a radius sphere from the player.

In our most nested loop, we can then check if the block meets the requirements of our predicate. This is simply performed using predicate.test(block), and if satisfied, we can set the block's type.

Lastly, we register our command as normal using the register() method.

new CommandAPICommand("replace")
	.withArguments(arguments)
	.executesPlayer((player, args) -> {
	    
	    // Parse the arguments
	    int radius = (int) args[0];
	    @SuppressWarnings("unchecked")
	    Predicate<Block> predicate = (Predicate<Block>) args[1];
	    BlockData blockData = (BlockData) args[2];
	    
	    // Find a (solid) sphere of blocks around the player with a given radius
	    Location center = player.getLocation();
	    for (int Y = -radius; Y < radius; Y++) {
	        for (int X = -radius; X < radius; X++) {
	            for (int Z = -radius; Z < radius; Z++) {
	                if (Math.sqrt((X * X) + (Y * Y) + (Z * Z)) <= radius) {
	                    Block block = center.getWorld().getBlockAt(X + center.getBlockX(), Y + center.getBlockY(), Z + center.getBlockZ());
	                    
	                    // If that block matches a block from the predicate, set it
	                    if(predicate.test(block)) {
	                        block.setType(blockData.getMaterial());
	                        block.setBlockData(blockData);
	                    }
	                }
	            }
	        }
	    }
	    return;
	})
	.register();

ItemStack predicate arguments

Similar to the BlockPredicateArgument, the ItemStackPredicateArgument is a way of performing predicate checks on ItemStack objects. These can represent tags, such as the ones declared here on the MinecraftWiki, or individual items. The cast type for this argument is Predicate<ItemStack>.

Example - Removing items in inventories based on predicates

Say we wanted to remove items in your inventory (I know, the /clear command does this, but this is the only example I could come up with). To do this, we'll use the following command structure:

/rem <item>

We implement this with a simple for loop over the player's inventory and remove items that satisfy the predicate.

// Register our command
new CommandAPICommand("rem")
	.withArguments(new ItemStackPredicateArgument("items"))
	.executesPlayer((player, args) -> {
	    
	    // Get our predicate
		@SuppressWarnings("unchecked")
	    Predicate<ItemStack> predicate = (Predicate<ItemStack>) args[0];
	    
	    for(ItemStack item : player.getInventory()) {
	        if(predicate.test(item)) {
	            player.getInventory().remove(item);
	        }
	    }
	})
	.register();

NBT arguments

The CommandAPI includes support for NBT compounds by using the NBT API by tr7zw. To use NBT, use the NBTCompoundArgument and simply cast the argument to an NBTContainer.

Since this argument depends on the NBT API, if this is used and the NBT API is not available on the server, an NBTAPINotFoundException will be thrown.

Example - ???

new CommandAPICommand("award")
    .withArguments(new NBTCompoundArgument("nbt"))
    .executes((sender, args) -> {
        NBTContainer nbt = (NBTContainer) args[0];
        
        //Do something with "nbt" here...
    })
    .register();

Developer's Note:

I haven't personally explored much with using this argument, so this example isn't great. If you believe you can supply a suitable example for this page, feel free to send an example on the CommandAPI issues page.

Literal arguments

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.

Developer's Note:

There is a simpler alternative to the LiteralArgument class! Instead of having to deal with arguments not being present in the array of arguments, consider using the much more intuitive MultiLiteralArgument, which is described in more detail here!


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, literal arguments are unlisted by default. In other words, the literal argument is not present in the args[] for the command declaration.

Example - Literal arguments and regular arguments

To illustrate the behavior of literal arguments, we create a command of the following form:

/mycommand <literal> <text>

As an example, let's declare the literal "hello" as a valid literal for this command. When we retrieve the result from args[0], it returns the value of the TextArgument, as opposed to the literal "hello":

new CommandAPICommand("mycommand")
    .withArguments(new LiteralArgument("hello"))
    .withArguments(new TextArgument("text"))
    .executes((sender, args) -> {
        // This gives the variable "text" the contents of the TextArgument, and not the literal "hello"
        String text = (String) args[0];
    })
    .register();

If I were to run the following command:

/mycommand hello goodbye

The value of text in the code above would be "goodbye".

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()) {
    
    //Register the command as usual
    new CommandAPICommand("changegamemode")
        .withArguments(new LiteralArgument(key))
        .executesPlayer((player, args) -> {
            //Retrieve the object from the map via the key and NOT the args[]
            player.setGameMode(gamemodes.get(key));
        })
        .register();
}	

Note how, since we don't have access to the literal from args, we must access the provided gamemode from elsewhere.


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.

Multi literal arguments

So far, we've described normal arguments and literal arguments. We've described the nuances with literal arguments and how they're not really "arguments", so they don't appear in the args[] for commands.

Now forget all of that. Multi literal arguments are the same as literal arguments but they do appear in the args[] for commands (i.e. they are listed). Multi literal arguments are just a way better alternative to literal arguments. The multi literal argument constructor allows you to provide a String[] of possible values which you can use for your command declaration.

The multi literal argument has all of the same benefits of a regular literal argument - they are hardcoded options that the user must enter - they don't allow other values.

Developer's Note:

The only reason that LiteralArgument still exists is for legacy purposes. MultiLiteralArgument is much more recommended because it's easier to understand and implement. The LiteralArgument has a very slight performance improvement over the MultiLiteralArgument, but it's basically unnoticeable.

Example - Using multi literals to make the gamemode command

In this example, we'll show how to use multi literals to declare Minecraft's /gamemode command. As you can see from the example code below, the argument declaration and command declaration is the same as if you were declaring any normal argument or command.

new CommandAPICommand("gamemode")
    .withArguments(new MultiLiteralArgument("adventure", "creative", "spectator", "survival"))
    .executesPlayer((player, args) -> {
        // The literal string that the player enters IS available in the args[]
        switch((String) args[0]) {
            case "adventure":
                player.setGameMode(GameMode.ADVENTURE);
                break;
            case "creative":
                player.setGameMode(GameMode.CREATIVE);
                break;
            case "spectator":
                player.setGameMode(GameMode.SPECTATOR);
                break;
            case "survival":
                player.setGameMode(GameMode.SURVIVAL);
                break;
        }
    }) 
    .register();

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). They basically represent StringArgument with overrideable suggestions and a built-in parser for any object of your choice. They are designed to be used for multiple commands - you define the argument once and can use it wherever you want when declaring commands.


The CustomArgument<T> has two constructors, declared as follows:

public CustomArgument(String nodeName, CustomArgumentFunction<T> parser);
public CustomArgument(String nodeName, CustomArgumentFunction<T> parser, boolean keyed);

The second argument is the CustomArgumentFunction, which is a lambda that takes in a String and returns some custom object of type T. The first constructor will construct a CustomArgument which uses the StringArgument as a base (thus, only simple strings). The second argument has the field keyed. When this field is set to true, the CustomArgument will use a Minecraft key as a base, allowing you to use Minecraft keys as input.

Developer's Note:

I may have complicated this too much, so let me clarify what I mean. The CustomArgument constructor is of the following forms:

CustomArgument(nodeName, (String) -> { ... return T; });
CustomArgument(nodeName, (String) -> { ... return T; }, boolean keyed);

Both constructors take in a String as input and return T. When enabling keyed, it allows the input to be of the form of a Minecraft key, but doesn't change the input type.

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 it to a Player object.

Example - World argument

Say we want to create an argument to represents the list of available worlds on the server. We basically want to have an argument which always returns a Bukkit World object as the result. Here, we create a method worldArgument() that returns our custom argument that returns a World. First, we retrieve our String[] of world names to be used for our suggestions. We then write our custom argument that creates a World object from the input (in this case, we simply convert the String to a World using Bukkit.getWorld(String)). We perform error handling before returning our result:

//Function that returns our custom argument
public Argument worldArgument(String nodeName) {
	
	//Construct our CustomArgument that takes in a String input and returns a World object
	return new CustomArgument<World>(nodeName, (input) -> {
	    //Parse the world from our input
	    World world = Bukkit.getWorld(input);
	
	    if(world == null) {
	        throw new CustomArgumentException(new MessageBuilder("Unknown world: ").appendArgInput());
	    } else {
	        return world;
	    }
	}).overrideSuggestions(sender -> {
		//List of worlds on the server, as Strings. We use overrideSuggestions(sender -> ...)
		//since this evaluates the list of worlds when the player types the command as opposed
		//to when the plugin starts up
		return Bukkit.getWorlds().stream().map(World::getName).toArray(String[]::new);
	});
}

In our error handling step, we check if the world is equal to null (since the Bukkit.getWorld(String) is @Nullable). To handle this case, we throw a CustomArgumentException with an error from a MessageBuilder. The CustomArgumentException has two constructors, so a message builder isn't required each time:

new CustomArgumentException(String message);
new CustomArgumentException(MessageBuilder message);

We can use our custom argument like any other argument. Say we wanted to write a command to teleport to a specific world. We will create a command of the following structure:

/tpworld <world>

Since we have defined the method worldArgument() which automatically generates our argument, we can use it as follows:

new CommandAPICommand("tpworld")
    .withArguments(worldArgument("world"))
    .executesPlayer((player, args) -> {
        player.teleport(((World) args[0]).getSpawnLocation());
    })
    .register();

By using a CustomArgument (as opposed to a simple StringArgument and overriding its suggestions), we are able to provide a much more powerful form of error handling (automatically handled inside the argument), and we can reuse this argument for other commands.


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:

MethodDescription
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 an object to the end of the builder

Example - Message builder for invalid objective argument

To create a MessageBuilder, simply call its constructor and use whatever methods as you see fit. Unlike a StringBuilder, you don't have to "build" it when you're done - the CommandAPI does that automatically:

new MessageBuilder("Unknown world: /").appendFullInput().appendHere();

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.


Functions in 1.16+

Developer's Note:

Minecraft 1.16+ change the way that datapacks are loaded on the server, so that they load before plugins are enabled. This means that non-vanilla commands that are declared in functions and tags will be detected as invalid, causing the server to throw a lot of errors at the very start.

The CommandAPI reloads datapacks once the server has finished loading using all declared commands, therefore the error messages at the start of the server can be ignored.


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

Say we have a command /killall that simply kills all entities in all worlds on the server. If we were to register this in our onLoad() method, this would allow us to use the /killall command in Minecraft functions and tags.

public class Main extends JavaPlugin {

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

        new CommandAPICommand("killall")
            .executes((sender, args) -> {
                //Kills all enemies in all worlds
                Bukkit.getWorlds().forEach(w -> w.getLivingEntities().forEach(e -> e.setHealth(0)));
        	})
            .register();
    }
    
    @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 6 - Functions & Tags), 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 SimpleFunctionWrapper class

To represent Minecraft functions and tags, the CommandAPI uses the SimpleFunctionWrapper class. Simply put, this class represents one Minecraft function, which are defined in .mcfunction files.

Developer's Note

The SimpleFunctionWrapper class only represents on Minecraft function. As a result, to represent a Minecraft "tag", which is a collection of Minecraft functions, the CommandAPI simply uses a SimpleFunctionWrapper[].

SimpleFunctionWrapper methods

The SimpleFunctionWrapper class has the following methods:

class SimpleFunctionWrapper implements Keyed {
    
    // Methods that creates SimpleFunctionWrapper instances
    static SimpleFunctionWrapper getFunction(NamespacedKey key);
    static SimpleFunctionWrapper[] getTag(NamespacedKey key);

    // Methods that query the Minecraft server
    static Set<NamespacedKey> getFunctions();
    static Set<NamespacedKey> getTags();
    
    // Methods for using the SimpleFunctionWrapper
    int run(CommandSender sender);
    
    // Utility functions
    String[] getCommands();
    NamespacedKey getKey();
}

getTag(NamespacedKey) and getFunction(NamespacedKey)

The getFunction(NamespacedKey) function is used to get a function that has been declared in a datapack and is loaded on the server.

The getTag(NamespacedKey) function is used to get a Tag that has been declared in a datapack and is loaded on the server. This returns a SimpleFunctionWrapper[], since a tag is simply an ordered collection of functions. When using this method, the # symbol which is typically used at the start of the tag's name is not needed.

getFunctions() and getTags()

The methods getFunctions() and getTags() simply return a set of NamespacedKey objects which are the names of functions or tags that have been declared by all datapacks on the server.

run(CommandSender)

This method simply runs the current SimpleFunctionWrapper as the provided command sender. The method will return a numerical result value, stating whether it succeeds or returns a result. This is documented in more detail here and here. For example:

getCommands()

The getCommands() method returns a String[] that contains the list of commands that the Minecraft function "holds". In other words, running this Minecraft function is basically as simple as iterating through its commands and running them in order. The commands that this String[] holds are the raw strings that this function represents - in other words, it can include things such as @p and ~ ~ ~ instead of "filled in" values.

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.

The FunctionWrapper class is an extension of the SimpleFunctionWrapper class. It is basically a SimpleFunctionWrapper, which has been constructed from an existing command sender when a command is used. This basically means that the command sender has already been "baked into" the FunctionWrapper object, allowing you to run it without having to provide a command sender.

FunctionWrapper methods

The FunctionWrapper class has the following methods:

class FunctionWrapper extends SimpleFunctionWrapper {

    // Methods specific to this class
	int run();
	int runAs(Entity e);

    // Methods inherited from SimpleFunctionWrapper
    static SimpleFunctionWrapper getFunction(NamespacedKey key);
    static SimpleFunctionWrapper[] getTag(NamespacedKey key);
    static Set<NamespacedKey> getFunctions();
    static Set<NamespacedKey> getTags();
    int run(CommandSender sender);
    String[] getCommands();
    NamespacedKey getKey();
}

These methods allow you to interact with the Minecraft function that this class wraps.

run()

The run() method basically does what it says on the tin - it runs the function. The command executor that runs this function is the command executor that was used to retrieve it. For example, if a player in-game populated this argument, then the player will be filled in for @p and the player's location would be used for things such as ~ ~ ~:

new CommandAPICommand("runfunc")
    .withArguments(new FunctionArgument("function"))
	.executes((sender, args) -> {
        FunctionWrapper[] functions = (FunctionWrapper[]) args[0];
        for(FunctionWrapper function : functions) {
            function.run(); // The command executor in this case is 'sender'
        }
    })
    .register();

runAs(Entity)

The runAs(Entity) is basically the same as the run() method, but it allows you to change the command executor to another entity.

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

Since it's a little difficult to demonstrate a custom use for the FunctionArgument, we will show how you can implement Vanilla Minecraft's /function command. In this example, we want a command that uses the following structure:

/runfunction <function>

When provided with a function, it will execute that function. If instead a tag is provided, it will execute that tag (i.e. execute all functions declared in that tag).

new CommandAPICommand("runfunction")
    .withArguments(new FunctionArgument("function"))
    .executes((sender, args) -> {
        FunctionWrapper[] functions = (FunctionWrapper[]) args[0];

        //Run all functions in our FunctionWrapper[]
        for(FunctionWrapper function : functions) {
            function.run();
        }
    })
    .register();

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:

PermissionWhat it does
CommandPermission.OPRequires OP to execute the command
CommandPermission.NONEAnyone can execute the command
CommandPermission.fromString("my.permission")Requires a specific permission node to execute the command

In addition to the CommandPermission class, there are two different ways to assign permissions (compared to the simple CommandSender.hasPermission() method that is provided by Bukkit), by using the withPermission method for arguments or for commands.

The withPermission method can take two values:

  • A CommandPermission, which represents a permission such as OP or NONE
  • A String, which will be converted automatically to a CommandPermission using CommandPermission.fromString()

Adding permissions to commands

To add a permission to a command, you can use the withPermission(CommandPermission) or withPermission(String) method when declaring a command.

Example - /god command with permissions

Say we created a command /god that sets a player as being invulnerable. Since this is a pretty non-survival command, we want to restrict who can run this command. As such, we want our player to have the permission command.god in order to run this command. To do this, we simply use the withPermission(CommandPermission) method from our command builder:

// Register the /god command with the permission node "command.god"
new CommandAPICommand("god")
    .withPermission(CommandPermission.fromString("command.god"))
    .executesPlayer((player, args) -> {
        player.setInvulnerable(true);
    })
    .register();

As stated above, it is possible to assign a permission using a String instead of using CommandPermission.fromString():

//Register the /god command with the permission node "command.god", without creating a CommandPermission
new CommandAPICommand("god")
    .withPermission("command.god")
    .executesPlayer((player, args) -> {
        player.setInvulnerable(true);
    })
    .register();

Adding 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

For example, say we're registering a command /kill:

/kill          - Kills yourself
/kill <target> - Kills a target player

We first declare the command as normal. Nothing fancy is going on here:

//Register the /god command with the permission node "command.god", without creating a CommandPermission
new CommandAPICommand("god")
    .withPermission("command.god")
    .executesPlayer((player, args) -> {
        player.setInvulnerable(true);
    })
    .register();

Now we declare our command with arguments. We use a PlayerArgument and apply the permission to the argument. After that, we register our command as normal:

// Adds the OP permission to the "target" argument. The sender requires OP to execute /kill <target>
new CommandAPICommand("kill")
    .withArguments(new PlayerArgument("target").withPermission(CommandPermission.OP))
    .executesPlayer((player, args) -> {
        ((Player) args[0]).setHealth(0);
    })
    .register();

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 as it has greater control over arguments.

Requirements

Requirements is a feature that allows you to put a constraint on commands and arguments. Similar to permissions, a requirement is something that must be fulfilled in order to use a given command or argument.

This section is broken up into four parts:

  • Adding requirements to commands
  • Adding requirements to arguments
  • Updating requirements
  • Multiple requirements

Please don't skip the section on updating requirements - the last section is necessary to get requirements to work as you'd want!


Adding requirements to commands

To add a requirement to a command, similar to adding permissions to commands, use the withRequirement method:

CommandAPICommand withRequirement(Predicate<CommandSender> sender);

The withRequirement method requires a predicate that determines if the sender is able to run the command - if the predicate is satisfied, then the command sender will be able to execute that command.

Example - Perks based on a player's level

Say we have a perks-based command system that depends on a player's level. For example, if a player has over 30 levels of experience, they would then be able to run a command that lets them repair the item in their hand in exchange for 30 levels. As such, we'll use the following command structure:

/repair

We want to put a requirement on this command that the player needs to have at least 30 levels of experience in order to run the command - if the player has less than 30 levels, the player should not be able to run the command. The easiest way to make the player not able to run the command is to literally tell the user that the command doesn't exist. That's what requirements do in the CommandAPI:

new CommandAPICommand("repair")
    .withRequirement(sender -> ((Player) sender).getLevel() >= 30)
    .executesPlayer((player, args) -> {
        
        //Repair the item back to full durability
        ItemStack is = player.getInventory().getItemInMainHand();
        ItemMeta itemMeta = is.getItemMeta();
        if(itemMeta instanceof Damageable) {
            ((Damageable) itemMeta).setDamage(0);
            is.setItemMeta(itemMeta);
        }
        
        //Subtract 30 levels
        player.setLevel(player.getLevel() - 30);
    })
    .register();

It's important to note that in this example, we case the sender to a player for the requirement method. We know that the sender is definitely a player because we use executesPlayer(), which ensures that this is the case. Now that we've got this, we need to make sure we update the player's requirements when their exp changes. This is covered in more detail in the section about updating requirements below.


Adding requirements to arguments

In a similar way that you can restrict certain arguments by adding permissions to them, you can restrict them by using arbitrary predicates by using the withRequirement method on the arguments themselves.

Example - A party creation and teleportation system

Let's say that we're working on a plugin that has a system to form groups of players called "parties". If you are not already in a party, you can create one of your own and if you are in a party, you can teleport to any other member in your party.

For this example, we'll use the following command structure:

/party create <partyName>
/party tp <player>

To represent our party in code, we'll use a simple Map called partyMembers which maps the player's UUID to the name of their registered party:

Map<UUID, String> partyMembers = new HashMap<>();

To begin with, let's create the /party create <partyName> command. First, we must declare our arguments:

List<Argument> arguments = new ArrayList<>();

// The "create" literal, with a requirement that a player must have a party
arguments.add(new LiteralArgument("create")
	.withRequirement(sender -> {
		
		return !partyMembers.containsKey(((Player) sender).getUniqueId());
		
	}));

arguments.add(new StringArgument("partyName"));

In this argument declaration, we put a requirement on the literal create, where the player does not have a party. In other words, if the player does not have a party, they are allowed to run /party create <partyName>. If a player already has a party, then they won't be allowed to run this command.

Now that we've declared our arguments, we can now declare our main command /party create <partyName>. We populate it with the arguments, and we create an entry in our partyMembers with the player's UUID and the name of the party that they created. Since this updates the requirements of the player, we'll have to make sure we update it (which is covered in more detail in the section about updating requirements below) - until then, I'll omit this from the code:

new CommandAPICommand("party")
	.withArguments(arguments)
	.executesPlayer((player, args) -> {
		
		//Get the name of the party to create
		String partyName = (String) args[0];
		
		partyMembers.put(player.getUniqueId(), partyName);
	})
	.register();

So now we've added the ability to create a party if we're not already in it. Now we need to implement our party tp <player> command. Again, we must start by declaring our arguments:

arguments = new ArrayList<>();
arguments.add(new LiteralArgument("tp")
	.withRequirement(sender -> {
		
		return partyMembers.containsKey(((Player) sender).getUniqueId());
        
	}));

arguments.add(new PlayerArgument("player")
	.safeOverrideSuggestions((sender) -> {
		
		//Store the list of party members to teleport to
		List<Player> playersToTeleportTo = new ArrayList<>();
		
		String partyName = partyMembers.get(((Player) sender).getUniqueId());
		
		//Find the party members
		for(UUID uuid : partyMembers.keySet()) {
			
			//Ignore yourself
			if(uuid.equals(((Player) sender).getUniqueId())) {
				continue;
			} else {
				//If the party member is in the same party as you
				if(partyMembers.get(uuid).equals(partyName)) {
					Player target = Bukkit.getPlayer(uuid);
					if(target.isOnline()) {
						//Add them if they are online
						playersToTeleportTo.add(target);
					}
				}
			}
		}
		
		return playersToTeleportTo.toArray(new Player[0]);
	}));

Notice something here? There's some code repetition for the withRequirement method - this is the same predicate that we used earlier, except we remove the negation. If you are interested, you can view the section Predicate tips for a method to improve code reuse.

Once the arguments have been declared, we can now implement our party teleportation command:

new CommandAPICommand("party")
	.withArguments(arguments)
	.executesPlayer((player, args) -> {
		Player target = (Player) args[0];
		player.teleport(target);
	})
	.register();

What's important to note in this example is that if you spend the time to set up the arguments properly, it severely decreases the amount of code required to write your command. This makes the commands you declare easier to understand and follow and you don't end up having to put all of these checks in the body of your command executor.


Updating requirements

Finally, the part you've all been waiting for - how to update requirements. With the way requirements work, they need to be updated manually. To illustrate why this is the case, I'll explain using the example of the /repair command:

When a player joins the game, the server tells the client the list of all commands that the client can run (don't worry, this is completely normal, as declared here). Let's say that the player has joined and has less than 30 levels.

When a player has less than 30 levels, they are unable to execute the /repair command, because the list of commands that the server sent to the client did not contain the /repair command. Eventually, the player will fight some mobs or mine some ores and eventually will reach 30 levels. Despite this, the player's client doesn't actually know that they're now able to use the /repair command until the server tells them. As such, the server needs to somehow update the requirements that a player has so a player knows they can run the command.

The CommandAPI handles this in a very simple method call:

CommandAPI.updateRequirements(player);

Developer's Note:

The CommandAPI.updateRequirements(player); method can be used anywhere, except for the withRequirement method. Using it inside this method will crash the server. This is by design - just make sure you don't use it within the withRequirement method and everything will be fine!

To illustrate how to use this, we'll go over the two examples above:

Example - Updating requirements for xp changes

In the example of requirements with the /repair command, we needed to ensure that the player's requirements update when their experience changes. To do this, we'll simply use a normal event to check this:

@EventHandler
public void onExpChange(PlayerExpChangeEvent event) {
    CommandAPI.updateRequirements(event.getPlayer());
}

And of course, you have to ensure that this event is registered in your onEnable() method:

@Override
public void onEnable() {
    getServer().getPluginManager().registerEvents(this, this);
}

Developer's Note:

I'm assuming you already know how to register events and don't need me to go into great detail how to do so, take the code above with a pinch of salt - I know how much everyone likes to divide their event handlers and listeners to organise their code.

Example - Updating requirements for the party creation example

In the example for a party creation, we declared two commands:

/party create <partyName>
/party tp <player>

When a player creates a new party, we need to ensure that their requirements are updated when they create the party. As such, we simply add this to our party creation command executor:

new CommandAPICommand("party")
	.withArguments(arguments)
	.executesPlayer((player, args) -> {
		
		//Get the name of the party to create
		String partyName = (String) args[0];
		
		partyMembers.put(player.getUniqueId(), partyName);
	    
	    CommandAPI.updateRequirements(player);
	})
	.register();

That's it!


Multiple requirements

The CommandAPI lets you handle multiple requirements really easily! The withRequirement method can be called multiple times, so you don't have to worry about shoving everything in one expression.

Example - Using multiple requirements

For example, you can apply multiple requirements for a command by calling the withRequirement method multiple times:

new CommandAPICommand("someCommand")
	.withRequirement(sender -> ((Player) sender).getLevel() >= 30)
	.withRequirement(sender -> ((Player) sender).getInventory().contains(Material.DIAMOND_PICKAXE))
	.withRequirement(sender -> ((Player) sender).isInvulnerable())
	.executesPlayer((player, args) -> {
		//Code goes here
	})
	.register();

Aliases

Aliases for commands can be added by using the withAliases() method when registering a command. Aliases allow you to run the same command with a different 'name' from the original registered command name.

Example - Using aliases for /getpos

In this example, we register the command /getpos that returns the command sender's location. We apply the aliases /getposition, /getloc, /getlocation and /whereami as well, using the withAliases() method.

new CommandAPICommand("getpos")
	// Declare your aliases
	.withAliases("getposition", "getloc", "getlocation", "whereami")
	  
	    //Declare your implementation
	.executesEntity((entity, args) -> {
	    entity.sendMessage(String.format("You are at %d, %d, %d", 
	        entity.getLocation().getBlockX(), 
	        entity.getLocation().getBlockY(), 
	        entity.getLocation().getBlockZ())
	    );
	})
	.executesCommandBlock((block, args) -> {
	    block.sendMessage(String.format("You are at %d, %d, %d", 
	            block.getBlock().getLocation().getBlockX(), 
	            block.getBlock().getLocation().getBlockY(), 
	            block.getBlock().getLocation().getBlockZ())
	        );
	    })
	  
	    //Register the command
	.register();

Subcommands

Subcommands is another method for registering commands that makes use of creating multiple different CommandAPICommand instances. Given a CommandAPICommand, we can add a subcommand by using the following method:

CommandAPICommand withSubcommand(CommandAPICommand subcommand);

Using subcommands has no disadvantages to using regular commands with the LiteralArgument or MultiLiteralArgument, and should be slightly more intuitive to implement if you've used other command frameworks before.

Example - Permission system with subcommands

Say we wanted to write a permission management system. To do this, we'll use the following command structure:

/perm group add <permission> <groupName>
/perm group remove <permission> <groupName>
/perm user add <permission> <userName>
/perm user remove <permission> <userName>

Let's start with the simplest example - the /perm group ... command. We have one command which is basically the following:

add <permission> <groupName>

We can implement this by creating a CommandAPICommand with the command name add:

CommandAPICommand groupAdd = new CommandAPICommand("add")
	.withArguments(new StringArgument("permission"))
	.withArguments(new StringArgument("groupName"))
	.executes((sender, args) -> {
	    //perm group add code
	});

Similarly, we have another part remove <permission> <groupName>. We can declare this similar to our add command. Once we've done that, we can now join everything up together. Here, we create a command group which adds the two other subcommands:

CommandAPICommand groupRemove = new CommandAPICommand("remove")
	.withArguments(new StringArgument("permission"))
	.withArguments(new StringArgument("groupName"))
	.executes((sender, args) -> {
	    //perm group remove code
	});

CommandAPICommand group = new CommandAPICommand("group")
	.withSubcommand(groupAdd)
	.withSubcommand(groupRemove);

Finally, we can link everything up together to the perm command and register the whole thing together:

new CommandAPICommand("perm")
    .withSubcommand(group)
    .register();

Another, more intuitive method, is to shove everything in one go without creating lots of variables all over the place:

new CommandAPICommand("perm")
    .withSubcommand(new CommandAPICommand("group")
        .withSubcommand(new CommandAPICommand("add")
            .withArguments(new StringArgument("permission"))
            .withArguments(new StringArgument("groupName"))
            .executes((sender, args) -> {
                //perm group add code
            })
        )
        .withSubcommand(new CommandAPICommand("remove")
            .withArguments(new StringArgument("permission"))
            .withArguments(new StringArgument("groupName"))
            .executes((sender, args) -> {
                //perm group remove code
            })
        )
    )
    .withSubcommand(new CommandAPICommand("user")
        .withSubcommand(new CommandAPICommand("add")
            .withArguments(new StringArgument("permission"))
            .withArguments(new StringArgument("userName"))
            .executes((sender, args) -> {
                //perm user add code
            })
        )
        .withSubcommand(new CommandAPICommand("remove")
            .withArguments(new StringArgument("permission"))
            .withArguments(new StringArgument("userName"))
            .executes((sender, args) -> {
                //perm user remove code
            })
        )
    )
    .register();

Annotation-based commands

The CommandAPI also includes a very small lightweight annotation-based command framework. This works very differently compared to previous commands shown in this documentation and it is less feature-rich than registering commands using the other methods.

In short, the CommandAPI's annotation-based system:

  • Has no runtime overhead compared to using the regular command registration system (unlike other annotation-based frameworks such as ACF).
  • Reduces code bloat (to an extent).
  • Improves readability since commands are declared declaratively instead of imperatively.
  • Is not as powerful as the regular command registration system.

Developer's Note:

Currently, the annotation framework is in its infancy, so any suggestions or improvements are heavily appreciated!

Before we go into too much detail, let's take a look at an example of what this annotation framework looks like, and compare this to the existing method.


Example: A warp command

(So I would put this section in a big green box, but this example is REALLY big and that wouldn't look good)

Let's say we're writing a plugin with the capability to create warps to places on the server. To do this, we'll make a simple command /warp, defined as follows:

/warp - Shows help
/warp <warp> - Teleports a player to <warp>
/warp create <name> - Creates a new warp <name> at the player's location

Warp command (without annotations)

Using the regular CommandAPI, this is one way we can create this command. In the code below, we use StringArguments to represent the warp names. To teleport to a warp, we also populate it with suggestions (deferred so it updates), and also use a subcommand to represent /warp create:

Map<String, Location> warps = new HashMap<>();

// /warp
new CommandAPICommand("warp")
	.executes((sender, args) -> {
		sender.sendMessage("--- Warp help ---");
		sender.sendMessage("/warp - Show this help");
		sender.sendMessage("/warp <warp> - Teleport to <warp>");
		sender.sendMessage("/warp create <warpname> - Creates a warp at your current location");
	})
	.register();

// /warp <warp>
new CommandAPICommand("warp")
	.withArguments(new StringArgument("warp").overrideSuggestions(sender -> {
		return warps.keySet().toArray(new String[0]);
	}))
	.executesPlayer((player, args) -> {
		player.teleport(warps.get((String) args[0]));
	})
	.register();

// /warp create <warpname>
new CommandAPICommand("warp")
    .withSubcommand(
		new CommandAPICommand("create")
			.withPermission("warps.create")
			.withArguments(new StringArgument("warpname"))
			.executesPlayer((player, args) -> {
				warps.put((String) args[0], player.getLocation());
			})
	)
    .register();

Seems fairly straightforward, given everything else covered in this documentation. Now let's compare it to using annotations!

Warp command (with annotations)

I think it's best to show the example and the explain it afterwards:

@Command("warp")	
public class WarpCommand {
	
	// List of warp names and their locations
	static Map<String, Location> warps = new HashMap<>();
	
	@Default
	public static void warp(CommandSender sender) {
		sender.sendMessage("--- Warp help ---");
		sender.sendMessage("/warp - Show this help");
		sender.sendMessage("/warp <warp> - Teleport to <warp>");
		sender.sendMessage("/warp create <warpname> - Creates a warp at your current location");
	}
	
	@Default
	public static void warp(Player player, @AStringArgument String warpName) {
		player.teleport(warps.get(warpName));
	}
	
	@Subcommand("create")
	@Permission("warps.create")
	public static void createWarp(Player player, @AStringArgument String warpName) {
		warps.put(warpName, player.getLocation());
		new IntegerArgument("");
	}
	
}
CommandAPI.registerCommand(WarpCommand.class);

As we can see, the code certainly looks very different to the normal registration method. Let's take it apart piece by piece to see what exactly is going on here.

Command declaration

@Command("warp")	
public class WarpCommand {

Firstly, we declare our command warp. To do this, we use the @Command annotation and simply state the name of the command in the annotation. This annotation is attached to the class WarpCommand, which basically indicates that the whole class WarpCommand will be housing our command.

The annotation framework is designed in such a way that an entire command is represented by a single class. This provides a more modular approach to command declaration that allows you to easily contain the methods of a command in one location.

Default command

@Default
public static void warp(CommandSender sender) {
	sender.sendMessage("--- Warp help ---");
	sender.sendMessage("/warp - Show this help");
	sender.sendMessage("/warp <warp> - Teleport to <warp>");
	sender.sendMessage("/warp create <warpname> - Creates a warp at your current location");
}

Here, declare the main command implementation using the @Default annotation. The @Default annotation basically informs the CommandAPI that the method it is attached to does not have any subcommands. This is basically the same as registering a regular command without using .withSubcommand().

Here, we simply write what happens when no arguments are run (i.e. the user just runs /warp on its own). As such, we don't include any parameters to our method.

Default command (again!)

@Default
public static void warp(Player player, @AStringArgument String warpName) {
	player.teleport(warps.get(warpName));
}

We also have a second @Default annotated method, which handles our /warp <warp> command. Because this isn't a subcommand (the warp to teleport to is not a subcommand, it's an argument), we still using the @Default annotation. In this method, we include an argument with this command by using the @AStringArgument annotation. This argument uses the StringArgument class, and the name of this argument is "warpName", which is extracted from the name of the variable. Simply put, the Annotation for an argument is A followed by the name of the argument. This is synonymous with using the following:

new StringArgument("warp")

It's also very important to note the parameters for this method. The first parameter is a Player object, which represents our command sender. The CommandAPI's annotation system uses the fact that the command sender is a Player object and automatically ensures that anyone using the command must be a Player. In other words, non-players (such as the console or command blocks), would be unable to execute this command.

The second argument is a String object, which represents the result of our argument "warp". The CommandAPI's annotation system can also infer the return type of the argument that is provided to it (in this case, a StringArgument will produce a String) and will automatically cast and provide the result to that parameter.

Subcommand

@Subcommand("create")
@Permission("warps.create")
public static void createWarp(Player player, @AStringArgument String warpName) {
	warps.put(warpName, player.getLocation());
}

Lastly, we declare a subcommand to allow us to run /warp create <name>. To do this, we simply use the @Subcommand annotation. In this example, we also apply a permission node that is required to run the command by using the @Permission annotation. The rest is fairly straight forward - we declare an argument, in this case it's another StringArgument , so we use @AStringArgument and then declare everything else in a similar fashion to the default command executor.

Registering the command

Registering the command is fairly simple and is a one liner:

CommandAPI.registerCommand(WarpCommand.class);

This line can be placed in your onEnable() or onLoad() method like you were registering a normal command.

Annotations

This page outlines in detail the list of all annotations that the CommandAPI's annotation-based command system includes.


Annotations that go on classes

@Command("commandName")

The @Command annotation is used to declare a command. The parameter is the name of the command that will be registered.

@Command("warp")	
public class WarpCommand {

@Alias({...})

The @Alias annotation is used to declare a list of aliases for a command. The parameter is a list of aliases which can be used for the command.

@Command("teleport")	
@Alias({"tp", "tele"})
public class TeleportCommand {

@Permission("permissionNode")

The @Permission annotation is used to add a permission node to a command. Users that want to run this command must have this permission. The parameter is the permission node required to run the command.

@Command("teleport")	
@Permission("myplugin.tp")
class TeleportCommand {

@NeedsOp

The @NeedsOp annotation is used to indicate that a command needs to have operator privileges to run it. This annotation has no parameters.

@Command("teleport")	
@NeedsOp
class TeleportCommand {

Annotations that go on methods

To use annotations on methods, methods must be static.

@Default

The @Default annotation indicates that the method is not a subcommand. This acts in a similar way to regular Bukkit commands. Commands with the @Default annotation can be used to run the main code when the command named with the @Command annotation is stated, such as the following:

@Default
public static void warp(CommandSender sender) {
	sender.sendMessage("--- Warp help ---");
	sender.sendMessage("/warp - Show this help");
	sender.sendMessage("/warp <warp> - Teleport to <warp>");
	sender.sendMessage("/warp create <warpname> - Creates a warp at your current location");
}

The @Default annotation does not mean that the command can't have arguments! Arguments can still be used and declared as shown:

@Default
public static void warp(Player player, @AStringArgument String warpName) {
	player.teleport(warps.get(warpName));
}

@Subcommand

The @Subcommand simply tells the CommandAPI that the declared method is a subcommand. This acts in a similar way to the regular CommandAPI's .withSubcommand() method. The subcommand annotation can take in a single string which is the name of the subcommand:

@Subcommand("create")
@Permission("warps.create")
public static void createWarp(Player player, @AStringArgument String warpName) {
	warps.put(warpName, player.getLocation());
}

Or, it can take in a list of strings which represent the aliases that can also be used for the declared subcommand:

@Subcommand({"teleport", "tp"})
public static void teleport(Player player, @APlayerArgument Player target) {
	player.teleport(target);
}

@Permission

The @Permission annotation can also be used on methods to indicate that a permission is required to execute a command.

@Subcommand("create")
@Permission("warps.create")
public static void createWarp(Player player, @AStringArgument String warpName) {
	warps.put(warpName, player.getLocation());
}

@NeedsOp

The @NeedsOp annotation can also be used on methods to indicate that the user must be an operator to run the command.


Annotations that go on parameters

The annotations for arguments are really simple, there's just two things you need to know:

  • To use an annotation argument, just add the letter A (for 'annotation') at the beginning of it! For example:

    \begin{align} \texttt{StringArgument}&\xrightarrow{A}\texttt{@AStringArgument}\\ \texttt{PlayerArgument}&\xrightarrow{A}\texttt{@APlayerArgument}\\ \texttt{AdvancementArgument}&\xrightarrow{A}\texttt{@AAdvancementArgument}\\ &\hspace{0.75em}\vdots \end{align}

    For example, we use @AStringArgument to indicate that this command takes a StringArgument as its first parameter:

@Default
public static void warp(Player player, @AStringArgument String warpName) {
	player.teleport(warps.get(warpName));
}
  • The name of the argument (referred to as "nodeName" in the normal CommandAPI system) is the name of the variable assigned to the parameter. In the above code, this means that the name of the argument is warpName.

Special argument annotations

Certain argument annotations have extra parameters that can be supplied to provide additional customization:

Numerical arguments

The following numerical arguments can take both a min and max value. Both of these are completely optional. This indicates the range of values (inclusive) that is valid for this argument. For example:

@Default
public static void command(CommandSender sender, 
	@ADoubleArgument(min = 0.0, max = 10.0) double someDouble,
	@AFloatArgument(min = 5.0f, max = 10.0f) float someFloat,
	@AIntegerArgument(max = 100) int someInt,
	@ALongArgument(min = -10) long someLong
) {
	// Command implementation here
}

Literal arguments

Both the LiteralArgument and MultiLiteralArgument can be used. When these are used, the name of the variable assigned to the parameter is ignored and not used as the argument's name.

For the @ALiteralArgument annotation, the parameter is the literal to be used for the command. For the @AMultiLiteralArgument, the parameter can be an array of multiple literals to use:

@Default
public static void command(CommandSender sender, 
	@ALiteralArgument("myliteral") String literal,
	@AMultiLiteralArgument({"literal", "anotherliteral"}) String multipleLiterals
) {
	// Command implementation here
}

Other arguments

The LocationArgument, Location2DArgument, EntitySelectorArgument and ScoreHolderArgument can all take an extra parameter in their constructors. As a result, the annotation-equivalent of these arguments also allow you to provide the parameter in the annotation:

@Default
public static void command(CommandSender sender, 
	@ALocationArgument(LocationType.BLOCK_POSITION) Location location,
	@ALocation2DArgument(LocationType.PRECISE_POSITION) Location location2d,
	@AEntitySelectorArgument(EntitySelector.MANY_ENTITIES) Collection<Entity> entities,
	@AScoreHolderArgument(ScoreHolderType.MULTIPLE) Collection<String> scoreHolders
) {
	// Command implementation here
}

Registering annotation-based commands

Registering annotation-based commands is really simple. To do this, we use the following method, where className is the name of a class with a @Command annotation:

CommandAPI.registerCommand(className)

Example: Registering a Warp command

Say we have a simple command /warp that is defined as follows:

@Command("warp")	
public class WarpCommand {
	
	// List of warp names and their locations
	static Map<String, Location> warps = new HashMap<>();
	
	@Default
	public static void warp(CommandSender sender) {
		sender.sendMessage("--- Warp help ---");
		sender.sendMessage("/warp - Show this help");
		sender.sendMessage("/warp <warp> - Teleport to <warp>");
		sender.sendMessage("/warp create <warpname> - Creates a warp at your current location");
	}
	
	@Default
	public static void warp(Player player, @AStringArgument String warpName) {
		player.teleport(warps.get(warpName));
	}
	
	@Subcommand("create")
	@Permission("warps.create")
	public static void createWarp(Player player, @AStringArgument String warpName) {
		warps.put(warpName, player.getLocation());
		new IntegerArgument("");
	}
	
}

We can register this in our onLoad() method so we can use this command from within Minecraft functions:

class MyPlugin extends JavaPlugin {
	
	@Override
	public void onLoad() {
		CommandAPI.registerCommand(WarpCommand.class);
	}
	
}

Command conversion

Developer's Note:

If you're a server owner, you're probably lost! This section is for developer command conversion. If you're looking for how to convert plugins with the config.yml file, you want 2. Configuration for server owners.

The CommandAPI has the ability to convert plugin commands to vanilla Minecraft commands using its config.yml's plugins-to-convert option. Nevertheless, the API for command conversion is not hidden and you're free to use it as you see fit!


Before you continue, let's clear up a few naming conventions which is used in the following sections!

  • Target plugin - This refers to a non-CommandAPI plugin which registers normal Bukkit commands. This typically uses the old boolean onCommand(CommandSender ... ) method
  • Your plugin - This refers to your plugin, the one that uses the CommandAPI and wants to add compatibility to a target plugin

Entire plugins

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

Example - Converting commands for a target plugin

Say you have some plugin.yml file for a target plugin that adds some basic functionality to a server. The target plugin in this example is called "TargetPlugin":

name: TargetPlugin
main: some.random.package.Main
version: 1.0
commands:
  gmc:
    aliases: gm1
  gms:
  i:
    permission: item.permission

As you can see, it declares 3 commands: /gmc, /gms and /i. We can now begin writing your plugin that uses the CommandAPI converter. We will call this plugin "YourPlugin":

public class YourPlugin extends JavaPlugin {
    
    @Override
    public void onEnable() {
        Converter.convert(Bukkit.getPluginManager().getPlugin("TargetPlugin"));
        //Other code goes here...
    }
    
}

When this is run, the commands /gmc, /gm1, /gms and /i will all be registered by the CommandAPI.


Only specific commands

In addition to converting all commands from a target plugin, the CommandAPI allows you to convert single commands at a time using the following methods from the Converter class:

public static convert(Plugin plugin, String cmdName);
public static convert(Plugin plugin, String cmdName, List<Argument> arguments);
public static convert(Plugin plugin, String cmdName, Argument... arguments);

In these commands, the plugin refers to the plugin which has the command you want to convert and cmdName is the name of the command declared in the target plugin's plugin.yml file (just the main command, not the aliases!).

The List<Argument> or Argument... can be used to provide argument checks that lets you apply the command UI to a bukkit command.

Example - Converting EssentialsX's speed command

Say we want to convert EssentialsX's /speed command using the CommandAPI. The plugin.yml entry for the /speed command is the following:

  speed:
    description: Change your speed limits.
    usage: /<command> [type] <speed> [player]
    aliases: [flyspeed,eflyspeed,fspeed,efspeed,espeed,walkspeed,ewalkspeed,wspeed,ewspeed]

From this, we can determine that there are the following commands, where "walk" and "fly" are the different types that the command can take:

/speed <speed>
/speed <speed> <target>
/speed <walk/fly> <speed>
/speed <walk/fly> <speed> <target>

With the EssentialsX plugin, the <speed> value can only take numbers between 0 and 10. As such, we'll ensure to apply these limits using the IntegerArgument. In addition, since the speed type can only be "walk" or "fly", we'll add that to our converter as well using a MultiLiteralArgument:

Plugin essentials = Bukkit.getPluginManager().getPlugin("Essentials");

// /speed <speed>
Converter.convert(essentials, "speed", new IntegerArgument("speed", 0, 10));

// /speed <target>
Converter.convert(essentials, "speed", new PlayerArgument("target"));

// /speed <walk/fly> <speed>
Converter.convert(essentials, "speed", 
	new MultiLiteralArgument("walk", "fly"), 
	new IntegerArgument("speed", 0, 10)
	);

// /speed <walk/fly> <speed> <target>
Converter.convert(essentials, "speed", 
	new MultiLiteralArgument("walk", "fly"), 
	new IntegerArgument("speed", 0, 10), 
	new PlayerArgument("target")
	);

Internal CommandAPI

The CommandAPI does a lot of stuff "behind the scenes". This internal CommandAPI section will go into detail about what the CommandAPI does, how it's implemented and why it has been implemented like that.


Argument identifiers

The CommandAPI's arguments are basically representations of the different arguments that the Minecraft Command Data protocol handles. These are outlined in the table below:

IdentifierCommandAPI argument
brigadier:boolBooleanArgument
brigadier:doubleDoubleArgument
brigadier:floatFloatArgument
brigadier:integerIntegerArgument
brigadier:longLongArgument
brigadier:stringStringArgument
TextArgument
GreedyStringArgument
CustomArgument<T>
minecraft:angleAngleArgument
minecraft:block_posLocationArgument
(LocationType.BLOCK_POSITION)
minecraft:block_predicateBlockPredicateArgument
minecraft:block_stateBlockStateArgument
minecraft:colorChatColorArgument
minecraft:column_posLocation2DArgument
(LocationType.BLOCK_POSITION)
minecraft:componentChatComponentArgument
minecraft:dimensionEnvironmentArgument
minecraft:entityEntitySelectorArgument
minecraft:entity_anchor
minecraft:entity_summonEntityTypeArgument
minecraft:float_rangeFloatRangeArgument
minecraft:functionFunctionArgument
minecraft:game_profilePlayerArgument
minecraft:int_rangeIntegerRangeArgument
minecraft:item_enchantmentEnchantmentArgument
minecraft:item_predicateItemStackPredicateArgument
minecraft:item_slot
minecraft:item_stackItemStackArgument
minecraft:messageChatArgument
minecraft:mob_effectPotionEffectArgument
minecraft:nbt
minecraft:nbt_compound_tagNBTCompoundArgument
minecraft:nbt_path
minecraft:nbt_tag
minecraft:objectiveObjectiveArgument
minecraft:objective_criteriaObjectiveCriteriaArgument
minecraft:operationMathOperationArgument
minecraft:particleParticleArgument
minecraft:resource_locationAdvancementArgument
BiomeArgument
CustomArgument<T>
LootTableArgument
RecipeArgument
SoundArgument
minecraft:rotationRotationArgument
minecraft:score_holderScoreHolderArgument
minecraft:scoreboard_slotScoreboardSlotArgument
minecraft:swizzleAxisArgument
minecraft:teamTeamArgument
minecraft:timeTimeArgument
minecraft:uuidUUIDArgument
minecraft:vec2Location2DArgument
(LocationType.PRECISE_POSITION)
minecraft:vec3LocationArgument
(LocationType.PRECISE_POSITION)

There are a few arguments that aren't implemented. Here's why:

  • minecraft:entity_anchor - This argument only has two values: eyes and feet. It's incredibly unnecessary for any other purpose and is easier to implement with a MultiLiteralArgument.

  • minecraft:item_slot - Bukkit's implementation of item slot numbers differs very wildly to Minecraft's implementation of item slot numbers. This difference makes it near-impossible to have a suitable middle-ground for item slot numbers that ensures that invalid numbers cannot be passed to the wrong inventory type. An implementation of this would require a rewrite of the current system to maintain proper inventory slot access safety.

  • minecraft:nbt, minecraft:nbt_path, minecraft:nbt_tag - You've got the NBTCompoundArgument, that's good enough, right? ¯\_(ツ)_/¯


Reloading datapacks

During the initialization of Minecraft 1.16+ servers, the CommandAPI uses a custom datapack reloading sequence as opposed to the normal Vanilla Minecraft datapack reloading method. The CommandAPI's method uses the server's current command dispatcher object as opposed to a new one, which allows datapacks to use commands registered by the CommandAPI. This can be invoked using the following method:

CommandAPI.reloadDatapacks();

Brigadier + CommandAPI

So far, we've been using only the CommandAPI to register commands. As a result, this makes the CommandAPI's features limited by whatever the CommandAPI has implemented. To push past these limits, the CommandAPI includes some extra methods to help with invoking brigadier methods. Of course, to use these methods, brigadier is required. The brigadier dependency's installation instructions can be found here.

Developer's Note:

For those that are unaware, brigadier is Mojang's command parser and dispatching framework. This is basically what the CommandAPI wraps around and is the main underlying source of its functionality.

The CommandAPI has been designed in such a way that you shouldn't have to access NMS in order to make use of the more "advanced" arguments and features - if you find that NMS is required to do something, please make a new issue!


Brigadier support functions

The CommandAPI offers the following methods in the dev.jorel.commandapi.Brigadier class:

public static CommandDispatcher getCommandDispatcher();
public static RootCommandNode getRootNode();
public static LiteralArgumentBuilder fromLiteralArgument(LiteralArgument literalArgument);
public static RedirectModifier fromPredicate(BiPredicate<CommandSender, Object[]> predicate, List<Argument> args);
public static Command fromCommand(CommandAPICommand command);
public static RequiredArgumentBuilder fromArgument(List<Argument> args, String nodeName);
public static RequiredArgumentBuilder fromArgument(Argument argument);
public static SuggestionProvider toSuggestions(String nodeName, List<Argument> args);

Briefly, here's what each of these functions do (you can view the JavaDocs for more information):

MethodDescription
getCommandDispatcherReturns the Minecraft command dispatcher graph
getRootNodeReturns the root node of the command dispatcher.
This is equivalent to using
getCommandDispatcher().getRoot();
fromLiteralArgumentCreates a LiteralArgumentBuilder from a LiteralArgument
fromPredicateConverts a predicate and some arguments into a RedirectModifier. This can be used for the fork method in brigadier's ArgumentBuilder
fromCommandConverts a CommandAPICommand into a brigadier Command object
fromArgumentConverts an argument, or a list of arguments, into a RequiredArgumentBuilder
toSuggestionsConverts an argument's suggestions into brigadier's SuggestionProvider, with a list of previously declared arguments

Examples

I hope these examples help understand how the CommandAPI can help with registering more "powerful" commands with the use of brigadier as well! Please bear with with it - these examples can be long, but I'm certain that they've been explained well and will be useful!

Example - Adding a predicate to the 'execute' command

Say we wanted to add a predicate to the /execute command. In this example, we'll create a predicate which handles random chances. To illustrate this, we want to be able to run commands such as:

/execute if randomchance 1 4 run say Hello!

In this scenario, if we ran this command, we would expect "Hello!" to appear in the chat with a \(\frac{1}{4}\) chance. In particular, this is what we're trying to achieve:

  • We want to create a predicate (true/false value) for the following syntax:
    randomchance <numerator> <denominator>
    
  • We also want this predicate to come after execute if:

    \[ \texttt{execute}\\ \downarrow\\ \texttt{if}\\ \downarrow\\ \texttt{randomchance <numerator}\texttt{> <denominator}\texttt{>} \]

  • After entering our predicate, we want to route back to execute (because the argument after execute is run, which is used in our example command above):

    \[ \texttt{execute}\\ \downarrow\\ \texttt{if}\\ \downarrow\\ \texttt{randomchance <numerator}\texttt{> <denominator}\texttt{>}\\ \downarrow\\ \texttt{execute} \]


Writing the code

Now that we've established what we want, we can finally begin writing the code! First we want to create a literal randomchance. It's a literal because literal values don't change (similar to say run or if from the /execute command). To create a literal, we'll use the fromLiteralArgument method described above, and then build it using the .build() method:

//Register literal "randomchance"
LiteralCommandNode randomChance = Brigadier.fromLiteralArgument(new LiteralArgument("randomchance")).build();

With that completed, we can now create our "argument" to this predicate. To do this, we'll use the regular declaration of arguments that we would normally use for commands. In this example, because we're computing \(\frac{numerator}{denominator}\), we want our numerator to be 0 or greater and our denominator to be 1 or greater (we don't want any negative numbers or division by zero!):

//Declare arguments like normal
List<Argument> arguments = new ArrayList<>();
arguments.add(new IntegerArgument("numerator", 0));
arguments.add(new IntegerArgument("denominator", 1));

Now we're going to get into the very nitty-gritty part - the predicate declaration. First, we'll create some variables numerator and denominator to represent the brigadier instances of these arguments. This can be handled by using the Brigadier.argBuildOf function:

ArgumentBuilder numerator = Brigadier.fromArgument(arguments, "numerator");
ArgumentBuilder denominator = Brigadier.fromArgument(arguments, "denominator")

Now we'll define our predicate. Since this is sort of a "meta-command" (it directly affects the outcome of the run command), we need to use the ArgumentBuilder's fork method. Remember that after we run this predicate, we want to link back to execute again, so our first argument is the CommandNode for execute, which we can get using Brigadier.getRootNode().getChild("execute"). Then, we can simply use Brigadier.fromPredicate to finish our declaration:

ArgumentBuilder denominator = Brigadier.fromArgument(arguments, "denominator")
    //Fork redirecting to "execute" and state our predicate
    .fork(Brigadier.getRootNode().getChild("execute"), Brigadier.fromPredicate((sender, args) -> {
        //Parse arguments like normal
        int num = (int) args[0];
        int denom = (int) args[1];
        
        //Return boolean with a num/denom chance
        return Math.ceil(Math.random() * (double) denom) <= (double) num;
    }, arguments));

Finally, we can now link everything up. We know that numerator comes first, then denominator, so we have to have numerator.then(denominator). We also know that these arguments are the children of the randomChance literal, so we use the following code to state all of this:

//Add <numerator> <denominator> as a child of randomchance
randomChance.addChild(numerator.then(denominator).build());

Finally, we "register" the command. In this case, we're actually just adding the randomChance node under \(\texttt{execute}\rightarrow\texttt{if}\), which we can add using the following code:

//Add (randomchance <numerator> <denominator>) as a child of (execute -> if)
Brigadier.getRootNode().getChild("execute").getChild("if").addChild(randomChance);

Code summary

So, hopefully that wasn't too confusing! If you're still lost, here's the whole code that we wrote:

//Register literal "randomchance"
LiteralCommandNode randomChance = Brigadier.fromLiteralArgument(new LiteralArgument("randomchance")).build();

//Declare arguments like normal
List<Argument> arguments = new ArrayList<>();
arguments.add(new IntegerArgument("numerator", 0));
arguments.add(new IntegerArgument("denominator", 1));

//Get brigadier argument objects
ArgumentBuilder numerator = Brigadier.fromArgument(arguments, "numerator");
ArgumentBuilder denominator = Brigadier.fromArgument(arguments, "denominator")
    //Fork redirecting to "execute" and state our predicate
    .fork(Brigadier.getRootNode().getChild("execute"), Brigadier.fromPredicate((sender, args) -> {
        //Parse arguments like normal
        int num = (int) args[0];
        int denom = (int) args[1];
        
        //Return boolean with a num/denom chance
        return Math.ceil(Math.random() * (double) denom) <= (double) num;
    }, arguments));

//Add <numerator> <denominator> as a child of randomchance
randomChance.addChild(numerator.then(denominator).build());

//Add (randomchance <numerator> <denominator>) as a child of (execute -> if)
Brigadier.getRootNode().getChild("execute").getChild("if").addChild(randomChance);

Predicate tips

In our example for creating a party system, we ended up having lots of code repetition. In our party creation command, we had the following code:

List<Argument> arguments = new ArrayList<>();

// The "create" literal, with a requirement that a player must have a party
arguments.add(new LiteralArgument("create")
	.withRequirement(sender -> {
		
		return !partyMembers.containsKey(((Player) sender).getUniqueId());
		
	}));

arguments.add(new StringArgument("partyName"));

And for our party teleportation command, we had the following code:

arguments = new ArrayList<>();
arguments.add(new LiteralArgument("tp")
	.withRequirement(sender -> {
		
		return partyMembers.containsKey(((Player) sender).getUniqueId());
        
	}));

We can simplify this code by declaring the predicate:

Predicate<CommandSender> testIfPlayerHasParty = sender -> {
    return partyMembers.containsKey(((Player) sender).getUniqueId());
};

Now, we can use the predicate testIfPlayerHasParty in our code for creating a party. Since we want to apply the "not" (!) operator to this predicate, we can use .negate() to invert the result of our predicate:

List<Argument> arguments = new ArrayList<>();
arguments.add(new LiteralArgument("create").withRequirement(testIfPlayerHasParty.negate()));
arguments.add(new StringArgument("partyName"));

And we can use it again for our code for teleporting to party members:

arguments = new ArrayList<>();
arguments.add(new LiteralArgument("tp").withRequirement(testIfPlayerHasParty));

Upgrading guide

From version 4.x to 5.0

Argument registration

LinkedHashMap is no longer used for argument registration. Instead, use a List, and put the argument's "prompt" as the first parameter in the argument's constructor. For example:

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("target", new PlayerArgument())
arguments.put("location", new LocationArgument(LocationType.BLOCK_POSITION));

new CommandAPICommand("teleport")
    .withArguments(arguments)
    .executes((sender, args) -> {
        //Teleport <target> to <location>
    })
    .register();

\[\downarrow\]

List<Argument> arguments = new ArrayList<>();
arguments.add(new PlayerArgument("target"));
arguments.add(new LocationArgument("location", LocationType.BLOCK_POSITION));

new CommandAPICommand("teleport")
    .withArguments(arguments)
    .executes((sender, args) -> {
        //Teleport <target> to <location>
    })
    .register();

Alternatively, you can declare them directly in the command's declaration so you don't have to construct a list:

new CommandAPICommand("teleport")
    .withArguments(new PlayerArgument("target"))
    .withArguments(new LocationArgument("location", LocationType.BLOCK_POSITION))
    .executes((sender, args) -> {
        //Teleport <target> to <location>
    })
    .register();

Alternatively, you can declare it in one line:

new CommandAPICommand("teleport")
    .withArguments(new PlayerArgument("target"), new LocationArgument("location", LocationType.BLOCK_POSITION))
    .executes((sender, args) -> {
        //Teleport <target> to <location>
    })
    .register();

Method changes

Some of the Brigadier methods were changed:

LiteralCommandNode registerNewLiteral(String name);
RequiredArgumentBuilder argBuildOf(LinkedHashMap<String, Argument> args, String value);
RequiredArgumentBuilder argBuildOf(String prompt, Argument argument);

\[\downarrow\]

LiteralArgumentBuilder fromLiteralArgument(LiteralArgument literalArgument);
RequiredArgumentBuilder fromArgument(List<Argument> args, String nodeName);
RequiredArgumentBuilder fromArgument(Argument argument);

In particular, the fromLiteralArgument now takes in a LiteralArgument and returns a LiteralArgumentBuilder. To convert from a LiteralArgumentBuilder to the LiteralCommandNode, you can run the .build() method.


From version 3.x to 4.0

The maven repository url has changed:

Instead of being:

https://raw.githubusercontent.com/JorelAli/1.13-Command-API/mvn-repo/1.13CommandAPI/

You must now use:

https://raw.githubusercontent.com/JorelAli/CommandAPI/mvn-repo/

This information can be viewed in section 3. Setting up your development environment. (Don't worry if you forget, it should work as normal nonetheless!)


From version 2.3 to 3.0

The CommandAPI's upgrade from version 2.3 to 3.0 is very intense and various refactoring operations took place, which means that plugins that implement the CommandAPI version 2.3 or will not to work with the CommandAPI version 3.0. This page outlines the few major changes and points you to the various pages in the documentation that covers how to use version 3.0.


Imports & Renaming

The default package name has been changed. Instead of being registered under the io.github.jorelali package, the CommandAPI has been moved to the dev.jorel package:

\[\texttt{io.github.jorelali.commandapi.api}\rightarrow\texttt{dev.jorel.commandapi}\]

To organise classes with other classes of similar functions, new packages have been introduced. These can be fully explored using the new JavaDocs


Removed classes & Alternatives

To reduce redundancies, the CommandAPI removed a few classes:

Removed classAlternative
SuggestedStringArgumentUse .overrideSuggestions(String[]) for the relevant argument, as described here
DefinedCustomArguments for ObjectivesUse ObjectiveArgument
DefinedCustomArguments for TeamsUse TeamArgument

Command registration

The way that commands are registered has been completely changed. It is highly recommended to switch to the new system, which is described here.

The following methods have been removed:

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

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

Additionally, the CommandAPI is no longer accessed by using CommandAPI.getInstance(). This has been replaced with static methods that can be accessed without an instance of the CommandAPI, so you can use the following:

CommandAPI.fail(String command);
CommandAPI.canRegister();
CommandAPI.unregister(String command);
CommandAPI.unregister(String command, boolean force);

Incompatible version information

There are a few arguments that are incompatible with various versions of Minecraft. This page outlines the full list of incompatibilities that the CommandAPI has with what versions of Minecraft.


Argument changes with respect to Minecraft version

AngleArgument

Incompatible with Minecraft versions less than 1.16.2 (1.13.x, 1.14.x, 1.15.x, 1.16, 1.16.1)

BiomeArgument

Incompatible with Minecraft versions less than 1.16 (1.13.x, 1.14.x, 1.15.x)

ChatArgument

Incompatible with Minecraft version 1.16.1 (Works on 1.16.2)

EnvironmentArgument

Incompatible with Minecraft version 1.13 (Works on 1.13.1 and 1.13.2)

LocationArgument2D

If you're using LocationArgument2D with LocationType.PRECISE_POSITION, then it is incompatible with Minecraft version 1.13 (Works on 1.13.1 and 1.13.2)

RecipeArgument

If you use Minecraft versions less than 1.15, this argument will return a Recipe. If you are using Minecraft version 1.15 or greater, this argument will return a ComplexRecipe (which is a subclass of Recipe).

TimeArgument

Incompatible with Minecraft versions less than 1.14 (1.13.x)

UUIDArgument

Incompatible with Minecraft versions less than 1.16 (1.13.x, 1.14.x, 1.15.x)


CommandAPI behavior with respect to Minecraft version

Minecraft version 1.16 and beyond

In Minecraft version 1.16, the way datapacks were loaded changed in such a way that the CommandAPI had to put in additional countermeasures to provide full support to it. To illustrate this, this was the previous loading sequence for Bukkit servers in Minecraft 1.15:

\[\texttt{Server loads}\rightarrow\texttt{Plugins load}\rightarrow\texttt{Datapacks load}\rightarrow\texttt{Server finishes loading}\]

Instead however, Minecraft 1.16 changed the loading sequence to the following:

\[\texttt{Server loads}\rightarrow\texttt{Datapacks load}\rightarrow\texttt{Plugins load}\rightarrow\texttt{Server finishes loading}\]

Because the CommandAPI used to register vanilla Minecraft commands before datapacks (and thus, custom Minecraft functions), it was possible to register custom commands that can be used in functions. With this new loading sequence change in Minecraft 1.16, this meant that datapacks load first before the CommandAPI does, so custom commands are not registered and functions with custom commands would fail to load.

To resolve this, the CommandAPI reloads datapacks and recipes at the end:

\begin{align} &\quad\texttt{Server loads} \\ \rightarrow&\quad\texttt{Datapacks load} \\ \rightarrow&\quad\texttt{Plugins load} \\ \rightarrow&\quad\texttt{Server finishes loading} \\ \rightarrow&\quad\texttt{Datapacks are reloaded} && \texttt{(by the CommandAPI)} \\ \rightarrow&\quad\texttt{Recipes are reloaded} && \texttt{(by the CommandAPI)} \end{align}

By doing this, this means:

  • Custom functions from datapacks are loaded twice
  • Recipes are reloaded twice, including recipes defined by other plugins

Although this sounds pretty bad (since reloading these things twice can be time consuming, thus contributing to the server start-up time), it is the only way to make custom functions work in Minecraft 1.16 and beyond.

Troubleshooting

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

My suggestions don't work or update

The suggestions need to be deferred so they are evaluated when the user requests it rather than during server start up. See Argument suggestion deferral which describes this in more detail.

I encounter a NullPointerException when using Bukkit's scoreboard

Shove the scoreboard access inside a lambda, so it is evaluated when commands are executed rather than when the server loads. For example, use:

List<Argument> arguments = new ArrayList<>();
            	
arguments.add(new TeamArgument("team").safeOverrideSuggestions(s ->
    Bukkit.getScoreboardManager().getMainScoreboard().getTeams().toArray(new Team[0]))
);

as opposed to:

List<Argument> arguments = new ArrayList<>();
            	
arguments.add(new TeamArgument("team").safeOverrideSuggestions(
    Bukkit.getScoreboardManager().getMainScoreboard().getTeams().toArray(new Team[0]))
);

Server errors when loading datapacks in 1.16+

If you get an error at the very start of the server's startup sequence along the lines of:

[15:57:29] [Worker-Main-5/ERROR]: Failed to load function mycustomnamespace:test
java.util.concurrent.CompletionException: java.lang.IllegalArgumentException: Whilst parsing command on line 2: Unknown or incomplete command, see below for error at position 0: <--[HERE]
    at java.util.concurrent.CompletableFuture.encodeThrowable(Unknown Source) ~[?:1.8.0_261]
    at java.util.concurrent.CompletableFuture.completeThrowable(Unknown Source) [?:1.8.0_261]
    at java.util.concurrent.CompletableFuture$AsyncSupply.run(Unknown Source) [?:1.8.0_261]
    at java.util.concurrent.CompletableFuture$AsyncSupply.exec(Unknown Source) [?:1.8.0_261]
    at java.util.concurrent.ForkJoinTask.doExec(Unknown Source) [?:1.8.0_261]
    at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(Unknown Source) [?:1.8.0_261]
    at java.util.concurrent.ForkJoinPool.runWorker(Unknown Source) [?:1.8.0_261]
    at java.util.concurrent.ForkJoinWorkerThread.run(Unknown Source) [?:1.8.0_261]
Caused by: java.lang.IllegalArgumentException: Whilst parsing command on line 2: Unknown or incomplete command, see below for error at position 0: <--[HERE]
    at net.minecraft.server.v1_16_R1.CustomFunction.a(SourceFile:62) ~[spigot-1.16.1.jar:git-Spigot-758abbe-8dc1da1]
    at net.minecraft.server.v1_16_R1.CustomFunctionManager.a(SourceFile:84) ~[spigot-1.16.1.jar:git-Spigot-758abbe-8dc1da1]
    ... 6 more

You can safely ignore it - the CommandAPI fixes this later. This is described in more detail here.

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.

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

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

Afterword

A message from the CommandAPI's author

Congratulations on making it to the end of the documentation! It's really long, but I did my best to make it the best (Bukkit/Spigot plugin) documentation in existence.


My name is Jorel, commonly known by my Minecraft username Skepter. I started the CommandAPI in the summer holidays between my first and second year at university. On the 19th August, 2018 I made my first commit to the CommandAPI project - just a month and a day after Minecraft 1.13 was released.

At the time, I just decided to call it "The 1.13 Command API" - it wasn't the catchiest name out there, but it sort of said what I wanted it to - it's a Command API for Minecraft 1.13, which was the update when the big overhaul to the command system was introduced.

It all started as a simple idea that can be summarized in 3 bullet points:

  • Create an API to use the new command UI that was introduced in Minecraft 1.13
  • Make it so the developers don't have to understand/use Mojang's brigadier
  • Make it similar to Bukkit's existing API

After the release of version 1.2, two days after the initial release, I received my first GitHub issue. This was quite a shock to me - version 1.2 only had 11 total downloads so it seemed odd that someone managed to stumble upon it despite the fact that I did nothing to promote the CommandAPI. Little did I know that that one issue was the main motivation to keep this API alive after its initial release.

I would never have possible imagined in my wildest dreams that 2 years later, I would still be in contact with them and know that if I had not chosen to create this API, their server would not have survived beyond Minecraft 1.13, let alone Minecraft 1.15, two major Minecraft versions later.


This project has been insane. Absolutely, utterly insane. At over 570 commits and over 450,000 additions (that includes things such as lines of code, lines of generated HTML documentation etc.), I can say without a doubt that this is indeed my biggest project ever.


Anyway, I digress. I'd like to give credit to all of the people that have opened issues on the CommandAPI GitHub, for without these people, the CommandAPI would have only remained a shadow of what it is now. I'd also like to give credit to the people that have starred the CommandAPI on its GitHub page.

I would like to personally give thanks to the following people - these are people that have made a significant contribution to the project in terms of ideas or support:

  • Combustible, who kickstarted the project by creating the CommandAPI's first issue. From this issue, this allowed the CommandAPI to have interoperability with Minecraft commands and functions which is by far the CommandAPI's most admirable feature. Additionally, Combustible helped raise awareness of the CommandAPI via the Spigot forums and Spigot issue tracker.
  • Draycia, who suggested implementing lazy evaluation for argument suggestions. This has been extended to provide the CommandAPI's context-aware system for argument suggestions based on previously filled arguments.
  • HielkeMinecraft, who made three outstanding contributions to the CommandAPI. They created the suggestion of setting the result and success values of commands which improves the interoperability between commands registered with the CommandAPI and vanilla Minecraft commands. They also influenced the implementation of the requirements system to have more powerful command constraints and helped start the CommandAPI Discord server.
  • Minenash, who was the driving force for the CommandAPI's 3.0 release, which added a plethora of new arguments to the CommandAPI. Minenash's research, code prototypes, documentation examples, bug testing and code review was a tremendous help to make the 3.0 release such a feature-rich version.
  • Michael-Ziluck, who created an amazing pull request that helped greatly improve the performance of the CommandAPI as well as structure the entire CommandAPI project into a multi-module Maven project which significantly improved the maintainability of the CommandAPI for the future.

I'd also like to give a special mention to the following people that have helped find bugs or have supported the project in some way: aianlinb, Baka-Mitai, Checkium, Comeza, DerpNerb, DogeBogey, endrdragon, EnragedRabisu, i509VCB, KieranGateley, lifespan, Loapu, Marvmallow, MatrixTunnel, portlek, Ricky12Awesome, SHsuperCM, SpaceCheetah, The_Gavster, Tinkot, vladfrangu, zedwick


I never really expected more than 5 or so people to use this API, so it was truly a pleasure to see everyone's responses, issues and suggestions that has made the CommandAPI what it is today.

Thank you so much for using the CommandAPI!