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 method | Outcome |
---|---|
CommandAPI.getInstance().register(String, LinkedHashMap, CommandExecutor) | Basic command registration |
CommandAPI.getInstance().register(String, String[], LinkedHashMap, CommandExecutor) | Register command with an array of aliases |
CommandAPI.getInstance().register(String, CommandPermission, LinkedHashMap, CommandExecutor) | Register command which need certain permissions |
CommandAPI.getInstance().register(String, CommandPermission, String[], LinkedHashMap, CommandExecutor) | Register command with aliases and permission requirements |
Arguments
Arguments are found in the io.github.jorelali.commandapi.api.arguments
package.
Argument class | Bukkit data type | Data type | Notes |
---|---|---|---|
new BooleanArgument() | ❌ | boolean | |
new ChatColorArgument() | ✔️ | ChatColor | |
new DoubleArgument() | ❌ | double | |
new EnchantmentArgument() | ✔️ | Enchantment | |
new EntityTypeArgument() | ✔️ | EntityType | |
new FloatArgument() | ❌ | float | |
new IntegerArgument() | ❌ | int | |
new ItemStackArgument() | ✔️ | ItemStack | Returns an ItemStack with amount 1 |
new LocationArgument() | ✔️ | Location | |
new ParticleArgument() | ✔️ | Particle | |
new PlayerArgument() | ✔️ | Player | Always returns 1 player |
new PotionEffectArgument() | ✔️ | PotionEffectType | |
new StringArgument() | ❌ | String | Always consists of 1 word |
new TextArgument() | ❌ | String | Can 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.
Constructor | Expression |
---|---|
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.
Constructor | Outcome |
---|---|
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
- This is the method
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)