Пользовательские типы аргументов
Примечание: статья в разработке.
Brigadier поддерживает пользовательские типы аргументов, и в этом разделе показано, как создать простой тип аргумента.
Внимание: Пользовательские аргументы требуют правильной регистрации установки клиентского мода! Если вы создаете серверный плагин, рассмотрите возможность использования существующего типа аргумента и поставщика пользовательских предложений вместо этого.
Для этого примера мы создадим UuidArgumentType
.
Сначала создайте класс, который расширяет ArgumentType
. Обратите внимание, что ArgumentType
является дженериком, поэтому общий тип будет определять, какой тип будет возвращать ArgumentType
:
public class UuidArgumentType implements ArgumentType<UUID> {
ArgumentType
требует, чтобы вы реализовали метод parse
, тип, который он возвращает, будет соответствовать типу дженерика:
@Override public UUID parse(StringReader reader) throws CommandSyntaxException {
Именно в этом методе будет происходить весь ваш синтаксический анализ. Либо этот метод вернет объект на основе аргументов, предоставленных в командной строке, либо выдаст CommandSyntaxException
, и синтаксический анализ завершится ошибкой.
Далее вы сохраните текущее положение курсора, это делается для того, чтобы вы могли подстроить только определенный аргумент. Это всегда будет в начале того места, где ваш аргумент появляется в командной строке:
int argBeginning = reader.getCursor(); // Начальная позиция курсора находится в начале аргумента. if (!reader.canRead()) { reader.skip(); }
Теперь мы схватываем весь аргумент целиком. В зависимости от типа вашего аргумента у вас могут быть другие критерии или быть похожими на некоторые аргументы, где обнаружение {
в командной строке потребует его закрытия. Для UUID мы просто выясним, в какой позиции курсора заканчивается аргумент:
while (reader.canRead() && reader.peek() != ' ') { // "peek" предоставляет символ в текущем положении курсора. reader.skip(); // Сообщает StringReader переместить курсор в следующую позицию. }
Затем мы спросим у StringReader
, какая текущая позиция курсора является подстрокой нашего аргумента из командной строки:
Теперь, наконец, мы проверяем, верен ли наш аргумент, и анализируем конкретный аргумент по своему вкусу, а также выдаем исключение, если синтаксический анализ завершается неудачей:
try { UUID uuid = UUID.fromString(uuidString); // Теперь наша действительная логика. return uuid; // И мы возвращаем наш тип, в этом случае анализатор будет считать, что этот аргумент был проанализирован правильно, а затем двигаться дальше. // UUID могут выдавать исключение, когда они создаются строкой, поэтому мы перехватываем исключение и переупаковываем его в тип CommandSyntaxException. // Создание с контекстом сообщает Brigadier'у предоставить некоторый контекст, чтобы сообщить пользователю, где произошла ошибка команды. // Хотя можно было бы использовать обычный метод создания. throw new SimpleCommandExceptionType(new LiteralText(ex.getMessage())).createWithContext(reader); }
ArgumentType
выполнен, однако ваш клиент откажется анализировать аргумент и выдаст ошибку. Это происходит потому, что сервер сообщит клиенту, к какому типу аргумента относится командный узел. И клиент не будет анализировать какие-либо типы аргументов, которые он не знает, как анализировать. Чтобы исправить это, нам нужно зарегистрировать ArgumentSerializer
в вашем ModInitializer
. Для более сложных типов аргументов вам может потребоваться создать свой собственный ArgumentSerializer
:
ArgumentTypes.register("mymod:uuid", UuidArgumentType.class, new ConstantArgumentSerializer(UuidArgumentType::uuid)); // Аргумент должен быть тем, что создаст ArgumentType.
И вот весь ArgumentType
:
- UuidArgumentType.java
- import com.mojang.brigadier.StringReader;
- import com.mojang.brigadier.arguments.ArgumentType;
- import com.mojang.brigadier.context.CommandContext;
- import com.mojang.brigadier.exceptions.CommandSyntaxException;
- import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
- import net.minecraft.text.LiteralText;
- import net.minecraft.util.SystemUtil;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.UUID;
- /**
- * Представляет ArgumentType который будет возвращать UUID.
- */
- public class UuidArgumentType implements ArgumentType<UUID> {
- public static UuidArgumentType uuid() {
- return new UuidArgumentType();
- }
- // Обратите внимание, что вы должны предполагать, что CommandSource, заключенный внутри CommandContext, всегда будет универсальным типом.
- // Если вам нужно получить доступ к ServerCommandSource, убедитесь, что источник является источником команд сервера перед приведением.
- return context.getArgument(name, UUID.class);
- }
- private static final Collection<String> EXAMPLES = SystemUtil.consume(new ArrayList<>(), list -> {
- list.add("765e5d33-c991-454f-8775-b6a7a394c097"); // i509VCB: Username The_1_gamers
- list.add("069a79f4-44e9-4726-a5be-fca90e38aaf5"); // Notch
- list.add("61699b2e-d327-4a01-9f1e-0ea8c3f06bc6"); // Dinnerbone
- });
- @Override
- int argBeginning = reader.getCursor(); // Начальная позиция курсора находится в начале аргумента.
- if (!reader.canRead()) {
- reader.skip();
- }
- // Теперь мы проверяем содержимое аргумента до тех пор, пока либо не достигнем конца командной строки (когда canRead становится false).
- // В противном случае мы идем до тех пор, пока не достигнем пробела, что означает следующий аргумент
- while (reader.canRead() && reader.peek() != ' ') { // "peek" предоставляет символ в текущем положении курсора.
- reader.skip(); // Сообщает StringReader переместить курсор в следующую позицию.
- }
- // Теперь мы подстроим конкретную часть, которую мы хотим видеть, используя начальную позицию курсора и концы, с которых начинается следующий аргумент.
- try {
- UUID uuid = UUID.fromString(uuidString); // Теперь наша действительная логика.
- return uuid; // И мы возвращаем наш тип, в этом случае анализатор будет считать, что этот аргумент был проанализирован правильно, а затем двигаться дальше.
- // UUID могут выдавать исключение, когда они создаются строкой, поэтому мы перехватываем исключение и переупаковываем его в тип CommandSyntaxException.
- // Создание с контекстом сообщает Brigadier'у предоставить некоторый контекст, чтобы сообщить пользователю, где произошла ошибка команды.
- // Хотя можно было бы использовать обычный метод создания.
- throw new SimpleCommandExceptionType(new LiteralText(ex.getMessage())).createWithContext(reader);
- }
- }
- @Override
- public Collection<String> getExamples() { // У Brigadier есть поддержка, чтобы показать примеры того, как должен выглядеть аргумент, он должен содержать коллекцию только аргументов, которые вернет этот тип. Это в основном используется для вычисления неоднозначных команд, которые имеют одинаковые
- return EXAMPLES;
- }
- }