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>
/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 CommandArguments 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.get(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.get(0);
    })
    .register();
CommandAPICommand("mycommand")
    .withArguments(LiteralArgument("hello"))
    .withArguments(TextArgument("text"))
    .executes(CommandExecutor { _, args ->
        // This gives the variable "text" the contents of the TextArgument, and not the literal "hello"
        val text = args[0] as String
    })
    .register()
commandAPICommand("mycommand") {
    literalArgument("hello")
    textArgument("text")
    anyExecutor { _, args ->
        // This gives the variable "text" the contents of the TextArgument, and not the literal "hello"
        val text = args[0] as String
    }
}

The LiteralArgument class also provides the LiteralArgument.of() and LiteralArgument.literal() helper methods which can be used as an alternative way to declare literal arguments:

new CommandAPICommand("mycommand")
    .withArguments(LiteralArgument.of("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.get(0);
    })
    .register();

new CommandAPICommand("mycommand")
    .withArguments(LiteralArgument.literal("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.get(0);
    })
    .register();
CommandAPICommand("mycommand")
    .withArguments(LiteralArgument.of("hello"))
    .withArguments(TextArgument("text"))
    .executes(CommandExecutor { _, args ->
        val text = args[0] as String
    })
    .register()

CommandAPICommand("mycommand")
    .withArguments(LiteralArgument.literal("hello"))
    .withArguments(TextArgument("text"))
    .executes(CommandExecutor { _, args ->
        val text = args[0] as String
    })
    .register()
commandAPICommand("mycommand") {
    argument(LiteralArgument.of("hello"))
    textArgument("text")
    anyExecutor { _, args ->
        val text = args[0] as String
    }
}

commandAPICommand("mycommand") {
    argument(LiteralArgument.literal("hello"))
    textArgument("text")
    anyExecutor { _, args ->
        val text = args[0] as String
    }
}

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(Entry<String, GameMode> entry : gamemodes.entrySet()) {

    // Register the command as usual
    new CommandAPICommand("changegamemode")
        .withArguments(new LiteralArgument(entry.getKey()))
        .executesPlayer((player, args) -> {
            // Retrieve the object from the map via the key and NOT the args[]
            player.setGameMode(entry.getValue());
        })
        .register();
}
// Create a map of gamemode names to their respective objects
val gamemodes = mapOf(
    "adventure" to GameMode.ADVENTURE,
    "creative" to GameMode.CREATIVE,
    "spectator" to GameMode.SPECTATOR,
    "survival" to GameMode.SURVIVAL
)

// Iterate over the map
for ((key, _) in gamemodes) {

    // Register the command as usual
    CommandAPICommand("changegamemode")
        .withArguments(LiteralArgument(key))
        .executesPlayer(PlayerCommandExecutor { player, _ ->
            // Retrieve the object from the map via the key and NOT the args[]
            player.gameMode = gamemodes[key]!!
        })
        .register()
}
// Create a map of gamemode names to their respective objects
val gamemodes = mapOf(
    "adventure" to GameMode.ADVENTURE,
    "creative" to GameMode.CREATIVE,
    "spectator" to GameMode.SPECTATOR,
    "survival" to GameMode.SURVIVAL
)

// Iterate over the map
for ((key, _) in gamemodes) {

    // Register the command as usual
    commandAPICommand("changegamemode") {
        literalArgument(key)
        playerExecutor { player, args ->
            // Retrieve the object from the map via the key and NOT the args[]
            player.gameMode = gamemodes[key]!!
        }
    }

}

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 .replaceSuggestions() to create your own list of required arguments.