Парсинг аргументов

API конструктора команд предоставляет мощный парсер аргументов. Он преобразует введённые данные как в базовые типы Java (целые числа, логические значения, строки), так и в игровые объекты (игроки, миры, типы блоков и т.д.). Парсер поддерживает необязательные аргументы и флаги. Также он поддерживает автозавершение аргументов при нажатии клавиши TAB.

Прошедшие парсинг аргументы сохраняются в объекты типа CommandContext. Если парсер возвращает один объект, то можно получить его при помощи args.<T>getOne(String key) (вместо T введите тип значения). Необязательные и слабые аргументы могут возвращать Optional.empty().

Многие парсеры могут возвращать более одного объекта (например, несколько игроков с соответствующим именем). В этом случае, Вам необходимо использовать метод args.<T>getAll(String key) для получения коллекции Collection возможных вариантов. Если этого не сделать, будет сгенерировано исключение!

При создании команды, учтите, может ли аргумент возвращать множество значений, например, можно ли в качестве аргумента типа Player использовать селектор для выбора нескольких игроков. В таком случае игроки понадобится ввести всего лишь одну простую команду. Например: /tell @a Who took the cookies?.

Совет

Вы можете использовать элемент GenericArguments#onlyOne(CommandElement) для ограничения количества возвращаемых значений до одного, и тогда вы сможете безопасно использовать args.<T>getOne(String). Однако пользователь всё равно получит сообщение, если попытается ввести более одного значения.

Для создания новых CommandElement (аргументы команды), используете фабричный класс GenericArguments. Многие элементы команды требуют короткий текстовый ключ, который отображается в ошибках и в сообщениях помощи.

Используя метод CommandSpec.Builder#arguments(CommandElement…), можно добавить CommandElement в конструктор команды. Можно добавить несколько CommandElement в конструктор, связав таким образом аргументы в цепочку (например, /msg <player> <msg>). Аналогичного эффекта можно добиться, обернув объекты CommandElement в объект GenericArguments#seq(CommandElement…).

Пример: Создание команды с несколькими аргументами

import org.spongepowered.api.Sponge;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.entity.living.player.Player;
import org.spongepowered.api.command.CommandException;
import org.spongepowered.api.command.CommandResult;
import org.spongepowered.api.command.CommandSource;
import org.spongepowered.api.command.args.CommandContext;
import org.spongepowered.api.command.args.GenericArguments;
import org.spongepowered.api.command.spec.CommandExecutor;
import org.spongepowered.api.command.spec.CommandSpec;

PluginContainer plugin = ...;

CommandSpec myCommandSpec = CommandSpec.builder()
        .description(Text.of("Send a message to a player"))
        .permission("myplugin.command.message")

        .arguments(
                GenericArguments.onlyOne(GenericArguments.player(Text.of("player"))),
                GenericArguments.remainingJoinedStrings(Text.of("message")))

        .executor((CommandSource src, CommandContext args) -> {

            Player player = args.<Player>getOne("player").get();
            String message = args.<String>getOne("message").get();

            player.sendMessage(Text.of(message));

            return CommandResult.success();
        })
        .build();

Sponge.getCommandManager().register(plugin, myCommandSpec, "message", "msg", "m");

Overview of the GenericArguments command elements

Элемент команды

Описание

Количество и тип значения

none

Не требует аргументов. Это обычное поведение CommandSpec.

Базовые типы Java

string

Требует аргумент типа String.

один String

remainingJoinedStrings

Соединяет все оставшиеся аргументы разделенные пробелами (может быть использовано для команд-сообщений).

один String

bool

Требует аргумент типа boolean.

один Boolean

integer

Требует аргумент типа integer.

один Integer

doubleNum

Требует аргумент типа double.

один Double

Игровые объекты

player

Ожидает аргумент в виде онлайн-игрока. Может возвращать несколько игроков!

несколько экземпляров Player

playerOrSource

Как player, но возвращает отправителя команды, если соответствующий игрок не был найден.

несколько экземпляров Player

userOrSource

Как playerOrSource, но возвращает пользователя, а не игрока.

несколько экземпляров User

world

Ожидает аргумент в виде мира (также включает выгруженные миры).

несколько WorldProperties

dimension

Ожидает аргумент в виде измерения (END, NETHER, OVERWORLD).

несколько экземпляров DimensionType

location

Ожидает аргумент в виде Location.

один Location

vector3d

Ожидает аргумент в виде Vector3d.

один Vector3d

catalogedElement

Expect an argument that is a member of the specified CatalogType.

multiple matching elements of the specified catalog type

Шаблоны

choices

Return an argument that allows selecting from a limited set of values.

one specified value

literal

Expect a literal sequence of arguments (e.g. "i", "luv", "u": /cmd i luv u). Throws an error if the arguments do not match.

one specified value

enumValue

Require the argument to be a key under the provided enum.

multiple matching elements of the specified enum

Утилиты

Can be wrapped around command elements. The value type is inherited from the wrapped element.

seq

Builds a sequence of command elements (e.g. /cmd <arg1> <arg2> <arg3>).

унаследованное

repeated

Require a given command element to be provided a certain number of times.

несколько унаследованных

allOf

Require all remaining args to match the provided command element.

несколько унаследованных

optional

Make the provided command element optional. Throws an error if the argument is of invalid format and there are no more args.

унаследованное

optionalWeak

Сделайте предоставленный командный элемент необязательным. Не выдает ошибку, если аргумент имеет недопустимый формат и больше нет аргументов.

унаследованное

firstParsing

Returns a command element that matches the first of the provided elements that parses (useful for command overloading, e.g. /settime <day|night|<number>).

унаследованное

onlyOne

Restricts the given command element to only insert one value into the context at the provided key.

унаследованное

flags

Returns a builder for command flags (e.g. /cmd [-a] [-b <value>]).

См. Флаги команд

Short Flag: one Boolean

Long Flag: one String

Value Flag: inherited

requiringPermission

Requires the command sender to have the specified permission in order to use the given command argument

унаследованное

requiringPermissionWeak

Requires the command sender to have the specified permission in order to use the given command argument. Does not throw an error if the user does not have the permission.

унаследованное

Совет

Больше информации доступно в Javadoc: GenericArguments.

Предупреждение

Не следует ожидать, что объекты CommandElement вернут одно значение: многие из них поддерживают возврат множества значения, а некоторые - регулярные выражения и селекторы. Это сделано намеренно, т.к. это упрощает использование команд. Например: /tell @a BanditPlayer has the cookies!. Чтобы быть уверенным в том, что будет возвращено только одно значение, используйте GenericArguments#onlyOne(CommandElement).

Пользовательские CommandElement’ы

Можно создать пользовательские командные элементы (например, URL-парсер или элемент Vector2i), расширив абстрактный класс CommandElement.

Метод CommandElement#parseValue(CommandSource, CommandArgs) должен получить необработанный аргумент в виде строки с помощью CommandArgs#next() и конвертировать его в объект. Если парсинг окажется неудачным, метод должен сгенерировать исключение ArgumentParseException.

Метод The CommandElement#complete(CommandSource, CommandArgs, CommandContext) должен использовать CommandArgs#peek() для прочтения следующего исходного аргумента. Этот метод возвращает список аргументов для автозавершения при нажатии клавиши TAB.

Пример: определение командного элемента Vector2i

В этом примере парсер читает два входных аргумента и преобразует их в вектор.

import com.flowpowered.math.vector.Vector2i;
import org.spongepowered.api.command.args.ArgumentParseException;
import org.spongepowered.api.command.args.CommandArgs;
import org.spongepowered.api.text.Text;
import org.spongepowered.api.command.args.CommandElement;

import java.util.Collections;
import java.util.List;

public class Vector2iCommandElement extends CommandElement {

    protected Vector2iCommandElement(Text key) {
        super(key);
    }

    @Override
    protected Object parseValue(CommandSource source, CommandArgs args) throws ArgumentParseException {

        String xInput = args.next();
        int x = parseInt(xInput, args);

        String yInput = args.next();
        int y = parseInt(yInput, args);

        return new Vector2i(x, y);
    }

    private int parseInt(String input, CommandArgs args) throws ArgumentParseException {
        try {
            return Integer.parseInt(input);
        } catch(NumberFormatException e) {
            throw args.createError(Text.of("'" + input + "' is not a valid number!"));
        }
    }

    @Override
    public List<String> complete(CommandSource src, CommandArgs args, CommandContext context) {
        return Collections.emptyList();
    }

    @Override
    public Text getUsage(CommandSource src) {
        return Text.of("<x> <y>");
    }
}

Пример: использование командного элемента Vector2i`

// /plottp <x> <y>
CommandSpec myCommandSpec = CommandSpec.builder()
        .description(Text.of("Teleport to a plot"))
        .permission("myplugin.command.plot.tp")

        .arguments(new Vector2iCommandElement(Text.of("coordinates")))

        .executor(new MyCommandExecutor())
        .build();

Совет

Просмотрите исходный код класса GenericArguments, чтобы увидеть больше примеров.

Использование селектора в пользовательских командных элементах

Sponge предоставляет поддержку парсинга селекторов, т.е. вы можете использовать их в собственных CommandElements. Использование селекторов состоит из двух частей: парсинга (получения Selector из строки) и получения (resolving) множества объектов Entity, выбранных селектором.

Для парсинга строки, содержащей селектор, используйте метод Selector#parse(String), передав ему весь селектор, включая символ @. Таким образом, вы превратите строку с объект типа Selector, с которым сможете вести дальнейшую работу. Учтите, что если строка не представляет собой допустимый селектор, будет сгенерировано исключение IllegalArgumentException.

Для получения данных из селектора, используйте метод Selector#resolve(CommandSource). Он вернет множество объектов Entity, выбранных селектором.

В примере ниже метод parseValue класса CommandElement пытается пропарсить селектор и возвращает список сущностей, основанный на расположении источника команды. Если строка не начинается с символа @, будет сгенерировано исключение, показывающее, что аргумент не является селектором.

@Override
protected Object parseValue(CommandSource source, CommandArgs args) throws ArgumentParseException {
    String nextArg = args.next();
    if (nextArg.startsWith("@")) {
        Set<Entity> selectedEntities;
        try {
            selectedEntities = Selector.parse(nextArg).resolve(source);
        } catch (IllegalArgumentException e) {
            throw args.createError(Text.of("Could not parse selector."));
        }

        if (selectedEntities.isEmpty()) {
            throw args.createError(Text.of("No entities selected."));
        }

        return selectedEntities;
    }

    throw args.createError(Text.of("Not a selector."));
}

Совет

В исходном коде SelectorCommandElement вы можете посмотреть, каким образом происходит парсинг селектора в стандартном CommandElements.