Entity & player arguments

Entity selector argument

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

The EntitySelectorArgument constructor requires an EntitySelector argument to determine what type of data to return. There are 4 types of entity selections which are available:

  • EntitySelector.ONE_ENTITY - A single entity, which returns a Entity object.
  • EntitySelector.MANY_ENTITIES - A collection of many entities, which returns a Collection<Entity> object.
  • EntitySelector.ONE_PLAYER - A single player, which returns a Player object.
  • EntitySelector.MANY_PLAYERS - A collection of players, which returns a Collection<Player> object.

The return type is the type to be cast when retrieved from the Object[] 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 EntitySelector.MANY_ENTITIES value in our 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<Collection<Entity>>("entities", EntitySelector.MANY_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[0];
        
        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:
    /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, with the EntitySelector.ONE_PLAYER. 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 EntitySelector.ONE_PLAYER


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

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, Object[] args) -> {
        for(int i = 0; i < (int) args[1]; i++) {
            player.getWorld().spawnEntity(player.getLocation(), (EntityType) args[0]);
        }
    })
    .register();

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