Documentation for v1.2

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

Installation (For server owners)

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

Project set up (For developers)

  • Download the v1.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])

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

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 DoubleArgument()double
new EnchantmentArgument()✔️Enchantment
new EntityTypeArgument()✔️EntityType
new FloatArgument()float
new IntegerArgument()int
new ItemStackArgument()✔️ItemStackReturns an ItemStack with amount 1
new LocationArgument()✔️Location
new ParticleArgument()✔️Particle
new PlayerArgument()✔️PlayerAlways returns 1 player
new PotionEffectArgument()✔️PotionEffectType
new StringArgument()StringAlways consists of 1 word
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

Strings and Text arguments

The StringArgument is used to represent a single word, such as apple or fly. The TextArgument is used to represent multiple words and can be entered using quotation marks:

/mycommand "this is a message"

In an example, you'd use TextArgument for messages between two players

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) -> {
    //Code here to change the player's speed
    (float) args[0]; //speed argument
});

//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) -> {
    //Code here to change the target's speed
    (float) args[0]; //speed argument
    (Player) args[1]; //target player
});

Permissions

Permissions are created using the CommandPermission class.

ConstructorOutcome
new CommandPermissions("my.permission")Requires my.permission to run the command
new CommandPermissions("my.perm1", "my.perm2")Requires my.perm1 and my.perm2 to run the command
new CommandPermissions(PermissionNode.OP)Requires sender to be an OP to run the command
new CommandPermissions(PermissionNode.NONE)Anyone can run the command

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.

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", new CommandPermission(PermissionNode.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", new CommandPermission(PermissionNode.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", new CommandPermission("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 TextArgument()); //args[1]

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

FAQ

Why is there no support for lists as arguments?

Q: For example, the /gamemode command has the options survival, creative, spectator and adventure - why can't we make our own?

A: Quick answer: It's too complicated. A: Long answer: It's possible, however I chose not to allow it. There are two methods of creating list arguments, each with their own issues:

  • Create a custom ArgumentType to handle things from a list
    • Support for doing so is very limited
    • Requires registration using NMS and serialization to be sent to a client
    • I've not been able to get this to work successfully, despite all of my efforts
  • Register a command multiple times for each element in the list
    • This is the method /gamemode uses, however it's only effective for small lists
    • Large lists (for example, the Material enum) creates a large amount of entries in the command registry
    • If a command had two large lists, the space growth rate is O(n^2), which could prevent a client from logging into a server

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

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. Scoreboards are complicated (if someone wants to help, I'm open to collaboration)