Home Creating the CommandAPI
Post
Cancel

Creating the CommandAPI

I decide to create an API to allow Bukkit developers to use the new command UI which was introduced in Minecraft 1.13 (Aquatic Update). This post describes my process of creating it.

Research

The first step for nearly any project I create is to carry out some in-depth research. There’s no point spending time and energy creating an API if a better solution already exists. Upon searching for anything relating to Minecraft 1.13’s new Command interface, I found one existing project: commodore, created by lucko.

Now I’m not saying that their solution to the problem is terrible, and indeed, it does solve the problem. However, there are a few things that I don’t like about their solution:

  • The programmer has to import the com.mojang.brigadier package.
  • The programmer needs to know how to use the contents of the com.mojang.brigadier package.
  • There’s no existing support for advanced arguments (for example, choosing a player) and neither is there any evidence that such a feature would be implemented in the future.

In addition to this, I found a year old thread discussing the new command API on spigot’s forums.

Diving into code

Despite the fact that doing further research into how to use the brigadier would be beneficial, I decide to just go straight to coding. I start exploring the classes for the new command system and discover a variable called commandDispatcher inside the main MinecraftServer class. Upon accessing that, I discover the brigadier and quickly learn how to access brigadier’s CommandDispatcher class to generate my own commands, by viewing examples from the default Minecraft commands (for example, /list uuids).

I learn the basic pattern for how commands are created and I play around in a testing file where I create a tonne of random commands and observe their effects. I discover ranged arguments and single word arguments and begin to formulate a wrapper to allow anyone to use this feature without getting caught up in the complexities of the new system.

Creating a new system for people that only know an old system

My aim for creating this system was to have something that seemed very lightweight and intuitive. The player registers a command name, a list of arguments (of varying types), any permissions and aliases that are required and then a command executor.

Old system

The old system was complicated. You had to link a command to your plugin, declare it in a separate file and deal with the command execution in a separate file.

1
getCommand("command").setExecutor(new MyCommandExecutor());

After that, you have to write your implementation for a class with only one method.

1
2
3
4
5
6
7
8
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
    if(label.equalsIgnoreCase("command")) {
        // Code goes here
        return true;
    }
    return false;
}

What does that sound like? A functional interface! (an interface with only one abstract method). Considering that over 98% of Minecraft servers run Java 8 (from metrics carried out on ~55,000 current Minecraft servers), it’s about time to create a system which uses the more intuitive lambdas.

In addition, this old system used a string array to represent the different arguments, which required programmers to parse the arguments manually (which would always cause issues). For example, say you have a command to change the time in the game.

1
2
/time 1000
/time day

The first command sets the world time to a number (between 0 and 18000). The second command sets the world time to “day” (which is numerically 1000). This leads to a minor problem - you have to check if the argument is an integer or a string, and parse those independently. With the new system, if you are using an integral argument, it runs the integral command and if you are using a string argument, it runs the string command.

New system

There’s 3 main components:

1
2
3
4
5
6
7
8
9
//Instance of API
CommandAPI commandRegister = new CommandAPI();

//Mapping of arguments to their respective data types
LinkedHashMap<String, ArgumentType> arguments = new LinkedHashMap<>();

//Command registration (with lambda)
commandRegister.register("COMMAND_NAME", arguments, (sender, args) -> {/* Code */});

Simple, intuitive and easy to use, given that the system is similar to the old one. Sure, it looks different and may take a while to get used to, but I believe it’s a sufficient solution to the problem.

Conclusion

In general, it’s a little wrapper to tie over the time until the spigot/bukkit developer team create their own wrapper for the new command UI.

This post is licensed under CC BY 4.0 by the author.

The Hiroshima Peace Memorial Park

Celeste's Strawberry Collection