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();
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]

PlayerProfile argument

The PlayerProfileArgument can serve a similar purpose as the EntitySelectorArgument.OnePlayer or EntitySelectorArgument.ManyPlayers if you only reference online players, but it can also be used to reference players that are offline or have never logged into your server.

Because of this, it has a performance overhead even when the input is an online player or an entity selector.

The PlayerProfileArgument returns a List<com.destroystokyo.paper.profile.PlayerProfile>.

The PlayerProfileArgument returns a List<org.bukkit.profile.PlayerProfile>.

Example – PlayerProfileArgument without entity selectors

Example – PlayerProfileArgument without entity selectors

When registering a PlayerProfileArgument 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 PlayerProfileArgument which only suggests the actual names, we can define it like this:

java
Argument<?> noSelectorSuggestions = new PlayerProfileArgument("target")
    .replaceSafeSuggestions(SafeSuggestions.suggest(info ->
        Bukkit.getOnlinePlayers().stream().map(Player::getPlayerProfile).toArray(PlayerProfile[]::new)
    ));
java
Argument<?> noSelectorSuggestions = new PlayerProfileArgument("target")
    .replaceSafeSuggestions(SafeSuggestions.suggest(info ->
        Bukkit.getOnlinePlayers().stream().map(Player::getPlayerProfile).toArray(PlayerProfile[]::new)
    ));

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();
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

AsyncPlayerProfile argument

The AsyncPlayerProfileArgument class is identical to the PlayerProfileArgument 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 AsyncPlayerProfileArgument returns a CompletableFuture<List<com.destroystokyo.paper.profile.PlayerProfile>> object, which can be used to retrieve the List<com.destroystokyo.paper.profile.PlayerProfile> object when the API call is complete.

Developer's Note:

The AsyncPlayerProfileArgument returns a CompletableFuture<List<org.bukkit.profile.PlayerProfile>> object, which can be used to retrieve the List<org.bukkit.profile.PlayerProfile> 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 AsyncPlayerProfileArgument to fetch the List<PlayerProfile> 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<List<PlayerProfile>> object from the AsyncPlayerProfileArgument and then use it to get the List<PlayerProfile> object. We can define it like this:

java
new CommandAPICommand("playedbefore")
    .withArguments(new AsyncPlayerProfileArgument("player"))
    .executes((sender, args) -> {
        CompletableFuture<List<PlayerProfile>> profiles = (CompletableFuture<List<PlayerProfile>>) 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...");

        profiles.thenAccept(profileList -> {
            if (Bukkit.getOfflinePlayer(profileList.getFirst().getId()).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();
java
new CommandAPICommand("playedbefore")
    .withArguments(new AsyncPlayerProfileArgument("player"))
    .executes((sender, args) -> {
        CompletableFuture<List<PlayerProfile>> profiles = (CompletableFuture<List<PlayerProfile>>) 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...");

        profiles.thenAccept(profileList -> {
            if (Bukkit.getOfflinePlayer(profileList.getFirst().getUniqueId()).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();
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.