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

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

/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:

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();
CommandAPICommand("remove")
    // Using a collective entity selector to select multiple entities
    .withArguments(EntitySelectorArgument.ManyEntities("entities"))
    .executes(CommandExecutor { sender, args ->
        // Parse the argument as a collection of entities (as stated above in the documentation)
        val entities = args["entities"] as Collection<Entity>

        sender.sendMessage("Removed ${entities.size} entities")
        for (e in entities) {
            e.remove()
        }
    })
    .register()
commandAPICommand("remove") {
    // Using a collective entity selector to select multiple entities
    entitySelectorArgumentManyEntities("entities")
    anyExecutor { sender, args ->
        // Parse the argument as a collection of entities (as stated above in the documentation)
        val entities = args["entities"] as Collection<Entity>

        sender.sendMessage("Removed ${entities.size} entities")
        for (e in entities) {
            e.remove()
        }
    }
}

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

  • To remove all cows:

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

    /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

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:

/warp <player>

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

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

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

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

And there we have it! One thing to note is that entity selectors are still a valid input, they are 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 StringArgument and suggesting a list of existing offline players).

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


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

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:

/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 \( 1 \le \textit{amount} \le 100 \):

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();
CommandAPICommand("spawnmob")
    .withArguments(EntityTypeArgument("entity"))
    .withArguments(IntegerArgument("amount", 1, 100)) // Prevent spawning too many entities
    .executesPlayer(PlayerCommandExecutor { player, args ->
        for (i in 0 until args["amount"] as Int) {
            player.world.spawnEntity(player.location, args["entity"] as EntityType)
        }
    })
    .register()
commandAPICommand("spawnmob") {
    entityTypeArgument("entity")
    integerArgument("amount", 1, 100) // Prevent spawning too many entities
    playerExecutor { player, args ->
        for (i in 0 until args["amount"] as Int) {
            player.world.spawnEntity(player.location, args["entity"] as EntityType)
        }
    }
}

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.