Skip to content

Entity & Player arguments

Entity selector argument

An image of an entity selector argument with a list of suggestions including entity selectors and a player name

Minecraft's target selectors (e.g. @a or @e) are implemented using the subclasses of the EntitySelectorArgument class. This allows you to select specific entities based on certain attributes.

There are four EntitySelectorArgument subclasses that determine what type of data to return:

  • EntitySelectorArgument.OneEntity - A single entity, which returns a Entity object.
  • EntitySelectorArgument.ManyEntities - A collection of many entities, which returns a Collection<Entity> object.
  • EntitySelectorArgument.OnePlayer - A single player, which returns a Player object.
  • EntitySelectorArgument.ManyPlayers - A collection of players, which returns a Collection<Player> object.

The return type is the type to be cast when retrieved from the CommandArguments args in the command declaration.

Example - Remove entities command

Example - Remove entities command

Say we want a command to remove certain types of entities. Typically, this would be implemented using a simple command like:

mccmd
/remove <player>
/remove <mob type>
/remove <radius>

Instead, we can combine all of these into one by using the EntitySelectorArgument. We want to be able to target multiple entities at a time, so we want to use the EntitySelectorArgument.ManyEntities constructor. We can simply retrieve the Collection<Entity> from this argument and iteratively remove each entity:

java
new CommandAPICommand("remove")
    // Using a collective entity selector to select multiple entities
    .withArguments(new EntitySelectorArgument.ManyEntities("entities"))
    .executes((sender, args) -> {
        // Parse the argument as a collection of entities (as stated above in the documentation)
        @SuppressWarnings("unchecked")
        Collection<Entity> entities = (Collection<Entity>) args.get("entities");

        sender.sendMessage("Removed " + entities.size() + " entities");
        for (Entity e : entities) {
            e.remove();
        }
    })
    .register();

We could then use this to target specific entities, for example:

  • To remove all cows:

    mccmd
    /remove @e[type=cow]
  • To remove the 10 furthest pigs from the command sender:

    mccmd
    /remove @e[type=pig,limit=10,sort=furthest]

Player argument

The PlayerArgument class is very similar (almost identical) to EntitySelectorArgument.OnePlayer. It returns a Player object and requires the player to be online.

Developer's Note:

The PlayerArgument internally uses the GameProfile class from Mojang's authlib, which means that this argument has a slight performance overhead compared to using EntitySelectorArgument.OnePlayer

Example – PlayerArgument without entity selectors

Example – PlayerArgument without entity selectors

When registering a PlayerArgument you might notice that it includes Entity Selectors (@a, @e, @r, etc.). If you want to avoid those, you can use argument suggestions to only suggest the player names. For this example, let us create a /warp command:

mccmd
/warp <player>

To get a PlayerArgument which only suggests the actual names, we can define it like this:

java
Argument<?> noSelectorSuggestions = new PlayerArgument("target")
    .replaceSafeSuggestions(SafeSuggestions.suggest(info ->
        Bukkit.getOnlinePlayers().toArray(new Player[0])
    ));

Now we can define the rest of the command and include our suggestion inside it like this:

java
new CommandAPICommand("warp")
    .withArguments(noSelectorSuggestions)
    .executesPlayer((player, args) -> {
        Player target = (Player) args.get("target");
        player.teleport(target);
    })
    .register();

And there we have it! One thing to note is that entity selectors are still a valid input; they’re just not included in the suggestions. WarpCommand

OfflinePlayer argument

The OfflinePlayerArgument class is identical to the PlayerArgument class, but instead of returning a Player object, it returns an OfflinePlayer object. Internally, this argument makes calls to Mojang servers (via Mojang's authlib), meaning it can be slightly slower than alternative methods such as using a AsyncOfflinePlayerArgument, which runs the API call asynchronously, or using a StringArgument and suggesting a list of existing offline players.

The OfflinePlayerArgument should be able to retrieve players that have never joined the server before.

AsyncOfflinePlayer argument

The AsyncOfflinePlayerArgument class is identical to the OfflinePlayerArgument class, but instead of making the API call synchronously, it makes the API call asynchronously. This means that the command will not block the main thread while waiting for the API call to complete.

Developer's Note:

The AsyncOfflinePlayerArgument returns a CompletableFuture<OfflinePlayer> object, which can be used to retrieve the OfflinePlayer object when the API call is complete.

Example - Checking if a player has joined before

Example - Checking if a player has joined before

Say we want to create a command that tells us if a player has joined the server before. We can use the AsyncOfflinePlayerArgument to fetch the OfflinePlayer object asynchronously. That way we simply wait for the request to complete, and once it does, we can check if the player has joined the server before. We want to create a command of the following form:

mccmd
/playedbefore <player>

We now want to get the CompletableFuture<OfflinePlayer> object from the AsyncOfflinePlayerArgument and then use it to get the OfflinePlayer object. We can define it like this:

java
new CommandAPICommand("playedbefore")
    .withArguments(new AsyncOfflinePlayerArgument("player"))
    .executes((sender, args) -> {
        CompletableFuture<OfflinePlayer> player = (CompletableFuture<OfflinePlayer>) args.get("player");

        // Directly sends a message to the sender, indicating that the command is running to prevent confusion
        sender.sendMessage("Checking if the player has played before...");

        player.thenAccept(offlinePlayer -> {
            if (offlinePlayer.hasPlayedBefore()) {
                sender.sendMessage("Player has played before");
            } else {
                sender.sendMessage("Player has never played before");
            }
        }).exceptionally(throwable -> {
            // We have to partly handle exceptions ourselves, since we are using a CompletableFuture
            Throwable cause = throwable.getCause();
            Throwable rootCause = cause instanceof RuntimeException ? cause.getCause() : cause;

            sender.sendMessage(rootCause.getMessage());
            return null;
        });
    })
    .register();

We now successfully ran a command that asynchronously checks if a player has joined the server before without blocking the main thread despite making an API call.

Entity type argument

An image of an entity argument displaying a list of entity type suggestions

The EntityTypeArgument class is used to retrieve a type of entity as defined in the EntityType enum. In other words, this is an entity type, for example, a pig or a zombie.

Example - Spawning entities

Example - Spawning entities

Say we want a command to spawn a specific type of entity, similar to the /summon command in Vanilla Minecraft, with the addition of specifying how many entities to spawn. We want to create a command of the following form:

mccmd
/spawnmob <entity> <amount>

Since we're trying to specify an entity type, we will use the EntityTypeArgument as our argument type for <entity>. We combine this with the IntegerArgument class with a specified range of 1amount100:

java
new CommandAPICommand("spawnmob")
    .withArguments(new EntityTypeArgument("entity"))
    .withArguments(new IntegerArgument("amount", 1, 100)) // Prevent spawning too many entities
    .executesPlayer((Player player, CommandArguments args) -> {
        for (int i = 0; i < (int) args.get("amount"); i++) {
            player.getWorld().spawnEntity(player.getLocation(), (EntityType) args.get("entity"));
        }
    })
    .register();

Note how in this example above, we have to explicitly state Player player, CommandArguments args. This is due to a limitation of Java's type inference system which is discussed here.