Documentation for v1.8

An API to use the new command UI introduced in Minecraft 1.13

Contents

Installation (For server owners)

  • Download the v1.8.2 CommandAPI.jar from the download page here
  • Place the CommandAPI.jar file in your server's /plugins/ folder
  • That's it!

Config.yml

Default config.yml:

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

Using the CommandAPI in your projects (For developers)

Manual Installation with .jar

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

Maven

  • Add the maven repository:

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

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

Basic usage (For developers)

  • Generate a LinkedHashMap<String, Argument> to store your arguments for your command. The insertion order IS IMPORTANT.

    LinkedHashMap<String, Argument> args = new LinkedHashMap<>();
    args.put("time", new IntegerArgument());
    
  • Register your command using the CommandAPI instance

    CommandAPI.getInstance().register("mycommand", arguments, (sender, args) -> {
        if(sender instanceof Player) {
         	Player player = (Player) sender;
            player.getWorld().setTime((int) args[0]);
        }
    });
    

Command registration

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

To see more about CommandPermission, check out the link here

To see more about CommandExecutor, check out the link here

Command loading order

Commands must be registered before the server finishes loading. The CommandAPI will prevent command registration after the server has loaded.

When to loadWhat to do
Plugin onLoad() methodRegister commands to be used in Minecraft functions (see Function section for more info)
Plugin 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!

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

Example:

MethodOutcome
CommandAPI.getInstance().unregister("gamemode")Unregisters the /gamemode command from the game
CommandAPI.getInstance().unregister("gamemode", true)Force unregisters all /gamemode commands from all plugins, Minecraft, Bukkit and Spigot. This includes /minecraft:gamemode, in addition to any other plugins which register /gamemode

Arguments

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

Argument classBukkit data typeData typeNotes
new BooleanArgument()boolean
new ChatColorArgument()✔️ChatColor
new ChatComponentArgument()✔️*BaseComponent[]*Requires Spigot, see below for usage notes
new DoubleArgument()double
new DynamicSuggestedStringArgument(DynamicSuggestions)StringSee below for usage
new EnchantmentArgument()✔️Enchantment
new EntitySelectorArgument(EntitySelector)✔️Entity, Player, Collection<Entity>, Collection<Player>See below for usage
new EntityTypeArgument()✔️EntityType
new FloatArgument()float
new FunctionArgument()❌*FunctionWrapper[]*Quite complex, see below for usage notes
new GreedyStringArgument()StringCan have any length
new IntegerArgument()int
new ItemStackArgument()✔️ItemStackReturns an ItemStack with amount 1
new LiteralArgument(String)N/ASee below for usage
new LocationArgument()
new LocationArgument(LocationType)
✔️LocationSee below for info on LocationType
new ParticleArgument()✔️Particle
new PlayerArgument()✔️PlayerAlways returns 1 player
new PotionEffectArgument()✔️PotionEffectType
new StringArgument()StringAlways consists of 1 word
new SuggestedStringArgument(String[])StringSee below for usage
new TextArgument()StringCan have spaces (used for text)

Argument Casting

To access arguments, they are casted in the order of declaration.

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

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

Ranged Arguments

Numerical arguments (int, float and double) can have ranged values.

ConstructorExpression
new IntegerArgument()int
new IntegerArgument(2)2 ≤ int
new IntegerArgument(2, 10)2 ≤ int ≤ 10

Location Arguments

As of 1.8.2, the LocationArgument now has a new optional constructor: LocationArgument(LocationType type). The LocationType has two options:

  • LocationType.BLOCK_POSITION - Integer block coordinates. The suggested location is the coordinates of block you are looking at when you type the command. BLOCK_POSITION
  • LocationType.PRECISE_POSITION - Exact coordinates. The suggested location is the exact coordinates of where your cursor is pointing at when you type the command. PRECISE_POSITION

By default, the LocationArgument will use PRECISE_POSITION.

Example:

/break <target block> - 'breaks' the block with the specified coordinates
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
//We want to target blocks in particular, so use BLOCK_POSITION
arguments.put("block", new LocationArgument(LocationType.BLOCK_POSITION));
CommandAPI.getInstance().register("break", arguments, (sender, args) -> {
	((Location) args[0]).getBlock().setType(Material.AIR);
});

Entity Selector Arguments

Target selectors are implemented using the EntitySelectorArgument class. This allows you to select specific entities based on certain attributes.

The EntitySelectorArgument class 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.

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

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

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

You can view an example of using the EntitySelectorArgument here

Chat Component Arguments

The ChatComponentArgument uses Spigot's BaseComponent class to accept raw JSON text as a valid input. When raw JSON is inputted, the ChatComponentArgument will return a BaseComponent[] which can be used for books and raw messages. You can read more about raw JSON here, and Spigot's Chat Component API here. You can view some examples with the ChatComponentArgument in the Examples section.

If used on a non-spigot server (e.g. a server running CraftBukkit), the CommandAPI will throw a SpigotNotFoundException.

Literal Arguments, Suggested String Arguments & Dynamic Suggested String Arguments

Literal Arguments

Introduced in v1.3 is the LiteralArgument class. This allows you to basically create "lists" in your commands, or specific commands where a desired text option is required.

LiteralArgument(String literal) takes a String input which is the text which the argument represents.

For example, take the /gamemode command. It takes an argument which is the game mode, which is picked from a list: adventure, creative, spectator or survival.

example

Literal arguments are "technically" not arguments, however they are declared as you would a regular argument.

//Used as a regular argument, in your LinkedHashMap
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("gamemodearg", new LiteralArgument("adventure"));

CommandAPI.getInstance().register("gamemode", arguments, (sender, args) -> {
    if(sender instanceof Player) {
        Player player = (Player) sender;
        player.setGameMode(GameMode.ADVENTURE);
    }
});

Since Literal arguments are not "technically" arguments, LITERAL ARGUMENTS ARE NOT DEFINED IN args. So, with the example above, args is an empty array.

To use lists, you can iterate over a list/map to generate multiple commands at once:

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

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

Be aware that large nested lists are HIGHLY discouraged, as described in this comment

Failing to define a value for a literal (either by using null or an empty String) will result in a BadLiteralException

SuggestedStringArguments

As of v1.5, the CommandAPI now includes the SuggestedStringArgument class. This is similar to the LiteralArgument class, however its usage is very different. The SuggestedStringArgument uses an array of Strings which are displayed to the user when inputting values.

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

//Populate a list of suggestions with the name of each Material in the Material enum
/* This doesn't have the same effect that LiteralArgument has where each value is
 * stored in the command dispatcher's JSON file. The suggestion list is generated
 * at runtime and therefore doesn't impact on server memory or cause potential client issues. */
List<String> suggestions = Arrays.stream(Material.values()).map(element -> element.name()).collect(Collectors.toList());

//Create a SuggestedStringArgument
arguments.put("material", new SuggestedStringArgument(suggestions));

//Output the value submitted by the player
CommandAPI.getInstance().register("suggest", arguments, (sender, args) -> {
	if (sender instanceof Player) {
		Player player = (Player) sender;
		player.sendMessage((String) args[0]);
	}
});

The SuggestedStringArgument is a suggestion. This means that the command sender MAY NOT enter a desired value stated in the list. For example:

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("examples", new SuggestedStringArgument("hello", "world", "blah"));
CommandAPI.getInstance().register("mycommand", arguments, (sender, args) -> {
	String result = (String) args[0]; //String argument from /mycommand [examples]
});

This code will not guarantee that the user has entered hello, world or blah. The user can type whatever they want, these are just suggestions.

LiteralArgumentSuggestedStringArgument
Forces the user to select a word from the listThe user doesn't have to select a word from the list
Bloated and uses up more memory to store argumentsArgument list is determined at runtime and doesn't use up as much memory
Input sanitization is performed before the command is runRequires extra handling to deal with unintended inputs. This is implemented by you.

Dynamic Suggested String Arguments

As of version 1.7, the CommandAPI now includes the DynamicSuggestedStringArgument class. This basically lets you take the SuggestedStringArgument class to a new level by allowing the server to automatically update the list whenever you want. This is done via a functional interface:

For example, say we register the command dynsuggest which dynamically updates from a list dynamicList:

//Create a list to hold the information of out dynamic list
List<String> dynamicList = new ArrayList<>();

//Create a Dynamic SuggestedStringArgument
DynamicSuggestedStringArgument dynSSArg = new DynamicSuggestedStringArgument(() -> {
    //Returns a String[]
	return dynamicList.toArray(new String[dynamicList.size()]);
});

//Register the command as normal
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("suggested", dynSSArg);
CommandAPI.getInstance().register("dynsuggest", arguments, (sender, args) -> {
	System.out.println((String) args[0]);
});

Later on in our program, we decide to add things to the list:

dynamicList.add("John");
dynamicList.add("Smith");
dynamicList.add("Will");

When the command is being typed by the player, the options John, Smith and Will will be suggested to them. Say we then add a new thing to the list:

dynamicList.add("Bob");

Now when the player types the command, the options John, Smith, Will and Bob will be suggested to them.

Strings, Greedy Strings and Text arguments

StringArgument

The StringArgument 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:

/mycommand hello
/mycommand 123
/mycommand hello123

Rejected StringArgument values:

/mycommand hello@gmail.com
/mycommand yesn't

Potential Uses

  • Entering Strings to identify offline players

TextArgument

The TextArgument acts similar to any String in Java. These can be single words, like to the StringArgument, or have additional values such as special characters if quoted. To type quotation marks, you can use \" (as similar to Java) to escape these special characters.

Accepted TextArgument values:

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

Rejected TextArgument values:

/mycommand hello world
/mycommand 私
/mycommand "speech marks: ""

Potential Uses

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

GreedyStringArgument

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

For example, say we have a command /msg <target> <message>

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("target", new PlayerArgument());
arguments.put("message", new GreedyStringArgument());
CommandAPI.getInstance().register("msg", arguments, (sender, args) -> {
	((Player) args[0]).sendMessage((String) args[1]);
});

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

/msg Skepter hello how are you? I'm doing great! Last week I sent an email to blah@mail.com

would send Skepter a message saying hello how are you? I'm doing great! Last week I sent an email to blah@mail.com.

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

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

Potential Uses

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

Multiple command arguments

Sometimes, you'll want one command to have different arguments. For example:

  • /walkspeed <speed> to change your walk speed
  • /walkspeed <speed> <target> to change the walk speed of another player

To accommodate for this, just register the command twice, each with different arguments:

LinkedHashMap<String, ArgumentType> arguments = new LinkedHashMap<>();
arguments.put("speed", new FloatArgument(0.0, 1.0)); //args[0]

//Register the command
CommandAPI.getInstance().register("walkspeed", arguments, (sender, args) -> {
	float speed = (float) args[0]; //speed argument
	//Code here to change the player's speed
});

//We can still use the arguments variable to add new arguments as the first command has already been registered
arguments.put("target", new PlayerArgument()); //args[1]

//Register the command
CommandAPI.getInstance().register("walkspeed", arguments, (sender, args) -> {
	float speed = (float) args[0]; //speed argument
	Player target = (Player) args[1]; //target player
	//Code here to change the target's speed
});

Overriding Argument Suggestions

As of v1.8, the CommandAPI allows you to override suggestions for arguments.

Syntax:

new Argument().overrideSuggestions(String... suggestions);

This can be useful for if you want a specific argument type, but want the user to input a specific value for that argument. For example, a recipe command which takes an ItemStackArgument, but you want to limit the choices to only iron, gold and diamonds:

LinkedHashMap<String, ArgumentType> arguments = new LinkedHashMap<>();
arguments.put("item", new ItemStackArgument().overrideSuggestions("minecraft:iron_ingot", "minecraft:gold_ingot", "minecraft:diamond"));

CommandAPI.getInstance().register("mycommand", arguments, (sender, args) -> {
	//code here
});

Example usage:

Say you wrote a command block which uses the following absolute entity selector:

@a[x=120,y=100,z=-120,dx=10,dy=10,dz=10]

Say you wanted to write this command in code rather than a command block to handle more complex operations, but still want to use this specific entity selector. Handling that entity selector in code is difficult, but easy for an argument for a command.

Using the CommandAPI, this argument can be represented as:

new EntitySelectorArgument(EntitySelector.MANY_PLAYERS).overrideSuggestions("@a[x=120,y=100,z=-120,dx=10,dy=10,dz=10]")

The user entering this command can then tab-complete this exact string and when parsed, it returns a Collection<Player>.

Argument Permissions

As of v1.8, the CommandAPI allows you to register permissions to arguments. This allows you to register subcommands to commands which require specific permissions in order to execute them. It uses a similar syntax to overriding suggestions:

Syntax:

new Argument().withPermission(CommandPermission permission)

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 usage:

A simple /kill command, where the second command requires OP:

/kill - kills yourself
/kill <target> - kills the target
LinkedHashMap<String, ArgumentType> arguments = new LinkedHashMap<>();
CommandAPI.getInstance().register("kill", arguments, (sender, args) -> {
	((Player) sender).setHealth(0);
});

arguments.clear();	
arguments.put("target", new PlayerArgument().withPermission(CommandPermission.OP));
CommandAPI.getInstance().register("kill", arguments, (sender, args) -> {
	((Player) args[0]).setHealth(0);
});

Functions & Function Arguments

As of v1.6, the CommandAPI now supports Minecraft's new functions. The FunctionArgument class is used to parse functions into your own plugin. This then looks up the function and returns a FunctionWrapper[], which is also included with the CommandAPI. This is a list of all functions which were retrieved by the user - either an array containing a single function or an array containing a list of functions (for example, from a tag)

The FunctionWrapper class

The FunctionWrapper class has two main methods:

MethodResult on execution
run()Executes the Minecraft function(s)
runAs(Entity e)Executes the Minecraft function as a specific Entity

Running the function from a FunctionWrapper will execute the function as declared in the respective .mcfunction file.

Function command registration

In order to allow a command from your plugin to be used in a .mcfunction file, you must register your command in your plugin's onLoad() method, instead of the onEnable() method. This is due to the loading order for Minecraft functions, which takes place after the onLoad() method, but before the onEnable() method.

Permissions

Permissions are created using the CommandPermission class.

MethodOutcome
CommandPermission.OPRequires sender to be an OP to run the command
CommandPermission.NONEAnyone can run the command
CommandPermission.fromString("my.perm")Requires the permission my.perm to run the command

Note: The permission syntax changed as of v1.7. Please use this syntax from now on!

For example, a simple invincibility command with these parameters:

/god
/god <target>

This is implemented like this:

LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
CommandAPI.getInstance().register("god", CommandPermission.fromString("command.god"), arguments, (sender, args) -> {
    if(sender instanceof Player) {
		((Player) sender).setInvulnerable(true);
	}
});

arguments.put("target", new EntitySelectorArgument(EntitySelector.ONE_PLAYER));
CommandAPI.getInstance().register("god", CommandPermission.fromString("command.god"), arguments, (sender, args) -> {
	((Player) args[0]).setInvulnerable(true);
});

CommandExecutor

The CommandExecutor class (not to be confused with Bukkit's CommandExecutor class) is a functional interface which executes when the command is performed. It consists of two parameters, CommandSender sender and Object[] args.

CommandExecutor format (Java 8 lambda)

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

CommandExecutor format (Java 7)

CommandExecutor executor = new CommandExecutor() {
	@Override
	public void run(CommandSender sender, Object[] args) {
		//Code here
	}
};

Command name checking and permission checks aren't required as these are checked when the player types the command.

ResultingCommandExecutors

As of v1.7, the CommandAPI now has support for the /execute command's store tag. This allows your command to return a success value (either 1 if successful or 0 if failure) and a result value (any integer).

The syntax for using a ResultingCommandExecutor is exactly the same as using a regular CommandExecutor when using the Java 8 lambda format:

ResultingCommandExecutor executor = (sender, args) -> {
    //code here
    return 5; //result value is returned here
};

Your ResultingCommandExecutor must return an integer - this is the result value. Default commands (ones that don't return a result value) will automatically return a result value of 0.

By default, all commands return a success value of 1. This is changed by using the CommandAPI.fail(String errorMessage) method, which instantly returns a success value of 0.

For example:

ResultingCommandExecutor executor = (sender, args) -> {
    int randInt = new Random().nextInt();
    if(randInt < 0) {
        CommandAPI.fail("The random number was negative :(");
    }
    return randInt; //result value is a random integer
};

The result value is a random integer. The success value depends on whether that number is positive or negative. If the success value is 0, the result value is assigned 0 as well.

In short:

Command works normallyCommand uses CommandAPI.fail(String)
resultWhatever int you return0
success10

ProxiedCommandSenders

As of v1.3, the CommandAPI now has some support for the /execute command, which is implemented using the ProxiedCommandSender

When using a command generated by the CommandAPI, it will modify the CommandSender when run depending of the CommandSender was changed in execution.

For example:

  • Running /mycommand as a player in game will return a regular CommandSender, which can be cast to a Player object. (Player player = (Player) sender)
  • Running /execute as @e[type=cow] run mycommand as a player in game will return a ProxiedCommandSender, with callee as a cow and caller as a player.

Command Converter

As of v1.6, the CommandAPI now includes the Converter class! This class can be used to convert commands from other plugins into commands which are compatible with Minecraft 1.13's /execute commands

MethodOutcome
Converter.convert(Plugin p)Converts all commands stated in Plugin's plugin.yml file
Converter.convert(Plugin p, String cmdName)Converts the specific command (cmdName) from Plugin's plugin.yml file

This converter is to be used cautiously and only for commands from other plugins that you need to use.

Examples examples examples!

Give Command

/give <item> <amount>
/give <target> <item> <amount>
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("item", new ItemStackArgument()); //args[0]
arguments.put("amount", new IntegerArgument(1, 64)); //args[1]

CommandAPI.getInstance().register("give", CommandPermission.OP, new String[] {"i", "item"}, arguments, (sender, args) -> {
	if(sender instanceof Player) {
		Player player = (Player) sender;
		ItemStack is = (ItemStack) args[0];
		is.setAmount((int) args[1]);
		player.getInventory().addItem(is);
	}
});

arguments = new LinkedHashMap<>();
arguments.put("target", new PlayerArgument()); //args0[0]
arguments.put("item", new ItemStackArgument()); //args[1]
arguments.put("amount", new IntegerArgument(1, 64)); //args[2]

CommandAPI.getInstance().register("give", CommandPermission.OP, new String[] {"i", "item"}, arguments, (sender, args) -> {
	Player target = (Player) args[0];
	ItemStack is = (ItemStack) args[1];
	is.setAmount((int) args[2]);
	target.getInventory().addItem(is);
});

Enchant Command

/enchant <level> <force enchant>
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("level", new IntegerArgument()); //args[0]
arguments.put("force enchant", new BooleanArgument()); //args[1]

CommandAPI.getInstance().register("enchant", CommandPermission.fromString("plugin.enchant"), arguments, (sender, args) -> {
	if(sender instanceof Player) {
		Player player = (Player) sender;
		if((boolean) args[1]) {
			player.getInventory().getItemInMainHand().addUnsafeEnchantment((Enchantment) args[1], (int) args[0]);
		} else {
			player.getInventory().getItemInMainHand().addEnchantment((Enchantment) args[1], (int) args[0]);
		}
	}
});

SetBlock Command

/setblock <location> <type>
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("location", new LocationArgument()); //args[0]
arguments.put("type", new ItemStackArgument()); //args[1]

CommandAPI.getInstance().register("setblock", arguments, (sender, args) -> {
	if(sender instanceof Player) {
		Player player = (Player) sender;
		Material type = ((ItemStack) args[1]).getType();
		player.getWorld().getBlockAt((Location) args[0]).setType(type);
	}
});

Message Command

/message <target> <message>
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("target", new PlayerArgument()); //args[0]
arguments.put("message", new GreedyStringArgument()); //args[1]

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

Using the ChatComponentArgument:

Send a message with raw JSON:

/raw <target> <raw message>
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("target", new PlayerArgument()); //args[0]
arguments.put("rawText", new ChatComponentArgument()); //args[1]

CommandAPI.getInstance().register("raw", arguments, (sender, args) -> {
    Player target = (Player) args[0];
    //Retrieve BaseComponent[] and send to player via spigot() method
    BaseComponent[] arr = (BaseComponent[]) args[1];
    target.spigot().sendMessage(arr);
});

Create a book with raw JSON:

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

DynamicSuggestedString Example

For example, say we have a plugin that keeps track of griefers. If a moderator of the server notices a griefer, they add the griefer to their "watch list". If they notice that player griefing again, they can ban that player easily as their name is suggested in the /bangriefer command:

/addgriefer <player> - add griefer to watch list
/bangriefer <player> - ban griefer from watch list
public class DynSugExample extends JavaPlugin {

	//List of griefers
	private List<Player> griefers;

	@Override
	public void onEnable() {

		//Initialise list
		griefers = new ArrayList<>();

		//Create arguments
		LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
		
		//Argument to store a player
		arguments.put("griefer", new EntitySelectorArgument(EntitySelector.ONE_PLAYER));
		
		// /addgriefer command adds the player to the list
		CommandAPI.getInstance().register("addgriefer", arguments, (sender, args) -> {
			griefers.add((Player) args[0]);
		});
		
		//Reset arguments and create a DynamicSuggestedStringArgument
		arguments = new LinkedHashMap<>();
		arguments.put("griefer", new DynamicSuggestedStringArgument(() -> {
			//Convert the griefers list into a String array with the player's names
			String[] array = new String[griefers.size()];
			for (int i = 0; i < griefers.size(); i++) {
				Player player = griefers.get(i);
				array[i] = player.getName();
			}
			
			//Return the String[]
			return array;
		}));
			
		// /bangriefer command bans the player.
		CommandAPI.getInstance().register("bangriefer", arguments, (sender, args) -> {
			Bukkit.getBanList(BanList.Type.NAME).addBan((String) args[0], "Being a griefer", new Date(), sender.getName());
		});
		
	}
}

Functions

Registering commands for Minecraft function support

/killall - kills all players on the server
/fly - enables flight

JavaPlugin file:

public class Main extends JavaPlugin {

    //Commands which will be used in Minecraft functions
	@Override
	public void onLoad() {
		LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
		CommandAPI.getInstance().register("killall", arguments, (sender, args) -> {
            //Lambda to kill all enemies in all worlds
        	Bukkit.getWorlds().forEach(w -> w.getLivingEntities().forEach(e -> e.setHealth(0)));
        });
	}
    
    //All other commands
    @Override
    public void onEnable() {
        //Other commands
        LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
		CommandAPI.getInstance().register("fly", arguments, (sender, args) -> {
			if(sender instanceof Player) {
				((Player) sender).setFlying(true);
			}
		});
    }
    
}

Example function hierarchy:

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

test.mcfunction

Note how the /killall command works here. Trying to register /fly will cause an error as it was loaded in the onEnable() method instead of the onLoad() method.

killall
say Killed all living entities on the server

mytag.json

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

In-game command usage

/function mycustomnamespace:test
/function #mycustomnamespace:mytag

Using a pre-existing function in your code

/runfunction <function/tag>
LinkedHashMap<String, Argument> arguments = new LinkedHashMap<>();
arguments.put("functionarg", new FunctionArgument());
CommandAPI.getInstance().register("runfunction", arguments, (sender, args) -> {
    FunctionWrapper[] func = (FunctionWrapper[]) args[0];
    //Run all functions in the FunctionWrapper[]
    for(FunctionWrapper function : func) {
        function.run();
    }
});

Converter

Given a plugin's plugin.yml file:

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

Running the converter on the plugin name produces:

//Convert all commands declared in myPlugin:
//Creates a command /gmc, /gm1, /gms and /i
Converter.convert(Bukkit.getPluginManager().getPlugin("myPlugin"));

FAQ

Why does the PlayerArgument only produce one player when @a is an option?

A: Simplicity. The old command system which involved using Bukkit.getPlayer() and parsing a username only produces one player. To make it as simple to use, this was the best option. Alternatively, you can use the new EntitySelectorArgument to select multiple players.

What about the other argument types?

Q: There's loads more argument types, including NBT tags, rotational position (i.e. pitch/yaw), inventory slots, scoreboards.... why aren't they implemented?

A: Again, for simplicity. NBT tags would require lots of NMS code to convert (and isn't majorly supported by Bukkit on its own). Rotational position isn't a majorly important argument - floating point arguments work fine. I don't really know much about how scoreboards work (if someone wants to help, I'm open to collaboration)