Создание команд позволяет разработчику мода добавлять функционал, который может быть использован при вызове команд. Это руководство научит вас регистрировать команды и общую структуру команд Brigadier.
INFO
Brigadier is a command parser and dispatcher written by Mojang for Minecraft. It is a tree-based command library where you build a tree of commands and arguments.
Исходный код библиотеки Brigadier: https://github.com/Mojang/brigadier
Интерфейс Command
com.mojang.brigadier.Command это функциональный интерфейс, который запускает конкретный код, и исключает CommandSyntaxException в определённых случаях. Он имеет общий тип S, который определяет тип источник команды. Источник команды предоставляет контекст, в котором была запущена команда. В Майнкрафт, источником команды является ServerCommandSource который может представлять сервер, командному блоку, удалённому соединению(RCON), игроку или сущности.
Единственный метод в Command, это run(CommandContext<S>) он берёт CommandContext<S> в качестве единственного аргумента и возвращает целое число. Командный контекст содержит источник вашей команды как S и позволяет получить аргументы, посмотрите на разобранные командные ноды и увидите вводные данные, используемые в этой команде.
Как и другие функциональные интерфейсы, этот используется постоянно как лямбда или ссылка на метод:
java
Command<ServerCommandSource> command = context -> {
return 0;
};1
2
3
2
3
Целое число может быть результатом команды. Обычно значения меньше или равные нулю означают, что команда не выполнена и ничего не сделает. Позитивные значения означают, что команда успешно выполнилась и что-то выполнила. Brigadier предоставляет константу для обозначения успеха; Command#SINGLE_SUCCESS.
Что может делать ServerCommandSource?
ServerCommandSource предоставляет дополнительный контекст когда команда выполняется. Это добавляет возможность получить сущность которая выполнила команду, мир в котором команда выполнилась команда или сервер на котором запустилась команда.
Вы можете получить командный источник из командного контекста при вызове getSource() в экземпляре CommandContext.
java
Command<ServerCommandSource> command = context -> {
ServerCommandSource source = context.getSource();
return 0;
};1
2
3
4
2
3
4
Регистрация основной команды
Команды регистрируются в CommandRegistrationCallback, предоставляемый Fabric API.
INFO
Сведения о регистрации обратных вызовов смотрите в События.
Событие должно быть зарегистрировано в вашем инициализаторе мода.
Обратный вызов имеет три аргумента:
CommandDispatcher<ServerCommandSource> dispatcher- используется для регистрации, парсинга и выполнения команд.S- это тип источника команд, который поддерживает диспатчер команд.CommandRegistryAccess registryAccess- предоставляет абстракцию которую можно передать определённой команде методы аргументовCommandManager.RegistrationEnvironment environment- Определяет тип сервера, на котором регистрируются команды.
В инициализаторе мода мы просто регистрируем простую команду:
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("test_command").executes(context -> {
context.getSource().sendFeedback(() -> Text.literal("Called /test_command."), false);
return 1;
}));
});1
2
3
4
5
6
2
3
4
5
6
В методеsendFeedback(), первый аргумент это текст для отправки, который является Supplier<Text>, чтобы избежать создание экземпляров текстовых объектов когда они не нужны.
Второй аргумент определяет, следует ли транслировать обратную связь с другими операторами. Обычно, если команда предназначена для запроса чего-либо без затрагивания мира, например запросить текущее время или счёт игрока, оно должно быть false. Если команда делает что-либо, например изменение времени или изменение чего-либо счёт, оно должно быть true.
Если команда не выполняется, вместо вызова sendFeedback(), вы напрямую можете выбросить любое исключение и сервер или клиент справится с этим соответствующим образом.
CommandSyntaxException обычно выбрасывается для обозначения синтаксических ошибок в команде или в аргументах. Вы можете так же имплементировать своё исключение.
Чтобы выполнить эту команду, необходимо ввести /test_command, при этом регистр символов имеет значение.
INFO
С этого момента мы будем извлекать логику, написанную в лямбде, передаваемой в сборщики .execute(), в отдельные методы. Затем мы можем передать ссылку на метод в .execute(). Это сделано для ясности.
Регистрационная среда
При желании вы так же можете сделать так, чтобы когда команда регистрировалась с определёнными условиями, например только в выделенной среде:
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
if (environment.dedicated) {
dispatcher.register(CommandManager.literal("dedicated_command")
.executes(FabricDocsReferenceCommands::executeDedicatedCommand));
}
});1
2
3
4
5
6
2
3
4
5
6
java
private static int executeDedicatedCommand(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /dedicated_command."), false);
return 1;
}1
2
3
4
5
2
3
4
5
Требования к команде
Допустим, что у вас есть команда и вы хотите чтобы её могли выполнять только операторы. Здесь метод requires() вступает в игру. Метод requires() имеет один аргумент Predicate<S> который будет предоставлять ServerCommandSource для проверки возможности CommandSource выполнять команду.
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("required_command")
.requires(source -> source.hasPermissionLevel(1))
.executes(FabricDocsReferenceCommands::executeRequiredCommand));
});1
2
3
4
5
2
3
4
5
java
private static int executeRequiredCommand(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /required_command."), false);
return 1;
}1
2
3
4
5
2
3
4
5
Команда выполнится только, если источник команды имеет второй уровень минимально, включая команду блоков. Иначе, команда не зарегистрируется.
Побочным эффектом этого является то, что эта команда не показывается при завершение всем игрокам кто не имеет второй уровень оператора. Это также объясняет почему вы не можете выполнить большинство команда, когда не включены читы.
Подкоманды
Чтобы добавить подкоманду, вы должны зарегистрировать первый литеральный нод команды. Чтобы иметь подкоманду, вы должны добавить следующий литеральный нод к существующему ноду.
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("command_one")
.then(CommandManager.literal("sub_command_one").executes(FabricDocsReferenceCommands::executeSubCommandOne)));
});1
2
3
4
2
3
4
java
private static int executeSubCommandOne(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /command sub_command_one."), false);
return 1;
}1
2
3
4
5
2
3
4
5
Подобно аргументам, ноды подкоманд могут также быть опциональными. В следующем случае будут допустимы как /command_two, так и /command_two sub_command_two.
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("command_two")
.executes(FabricDocsReferenceCommands::executeCommandTwo)
.then(CommandManager.literal("sub_command_two").executes(FabricDocsReferenceCommands::executeSubCommandTwo)));
});1
2
3
4
5
2
3
4
5
java
private static int executeCommandTwo(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /command_two."), false);
return 1;
}
private static int executeSubCommandTwo(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /sub_command_two."), false);
return 1;
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
Клиентские команды
Fabric API имеет ClientCommandManager в пакетеnet.fabricmc.fabric.api.client.command.v2 который можно использовать для регистрации команд на клиентской стороне. Код должен существовать только в коде клиентской стороны.
java
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
dispatcher.register(ClientCommandManager.literal("clienttater").executes(context -> {
context.getSource().sendFeedback(Text.literal("Called /clienttater with no arguments."));
return 1;
}));
});1
2
3
4
5
6
2
3
4
5
6
Перенаправление команд
Перенаправление команд - также известное как псевдонимы - это способ перенаправить функционал одной команды к другой. Это полезно, если вы хотите изменить название команды, но всё равно хотите поддерживать старое название.
WARNING
Brigadier будет перенаправлять только командные узлы с аргументами. Если вы хотите перенаправить командный узел без аргументов, предоставьте конструктор .executes() со ссылкой на ту же логику, что описана в примере.
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
var redirectedBy = dispatcher.register(CommandManager.literal("redirected_by").executes(FabricDocsReferenceCommands::executeRedirectedBy));
dispatcher.register(CommandManager.literal("to_redirect").executes(FabricDocsReferenceCommands::executeRedirectedBy).redirect(redirectedBy));
});1
2
3
4
2
3
4
java
private static int executeRedirectedBy(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /redirected_by."), false);
return 1;
}1
2
3
4
5
2
3
4
5
ЧАВО
Почему мой код не компилируется?
Словите или выбросите
CommandSyntaxException-CommandSyntaxException- это неRuntimeException. Если вы выбросите это, то оно должно быть в методах, которые выбрасываютCommandSyntaxExceptionв сигнатурном методе, или его нужно будет поймать. Brigadier обработает проверенные исключение и отправит вам сообщение об ошибке в игре.Время от времени у вас могут возникать проблемы с джинериками. Если вы регистрируете серверные команды (в большинстве случаев), убедитесь, что вы используете
CommandManager.literalилиCommandManager.argumentвместоLiteralArgumentBuilder.literalилиRequiredArgumentBuilder.argument.Проверьте метод
sendFeedback()- Возможно вы забыли указать логическое значение как второй аргумент. Также помните что, начиная с версии Майнкрафта 1.20, первым аргументом должен бытьSupplier<Text>вместоText.Команда должна возвращать целое число - При регистрации команд, метод
executes()принимает объектCommand, обычно это лямбда. Лямбда должна возвращать только целое число.
Могу я зарегистрировать команды во время выполнения?
WARNING
You can do this, but it is not recommended. You would get the CommandManager from the server and add anything commands you wish to its CommandDispatcher.
После этого вам нужно опять отправить команду tree ко всем игрокам используя CommandManager.sendCommandTree(ServerPlayerEntity).
Это необходимо, поскольку клиент локально кэширует команду tree, которую он получает во время логина (или когда пакеты оператора отправлены) для локальных сообщений об ошибках с большим дополнением.
Могу я отменить регистрацию команд во время выполнения?
WARNING
You can also do this, however, it is much less stable than registering commands at runtime and could cause unwanted side effects.
Для простоты, вы должны использовать отражение Brigadier и удалить ноды. После этого, вам нужно отправить команду tree каждому игроку заново используя sendCommandTree(ServerPlayerEntity).
Если вы не будете отправлять обновления команде tree, клиент может всё ещё подумать, что команда существует, хотя сервер провалит его выполнение.












