Criando Comandos 26.2
Crie comandos com argumentos e ações complexas.
Criar comandos pode permitir que o desenvolvedor de mods adicione funcionalidade que pode ser utilizada através de um comando. Este tutorial lhe ensinará como registrar comandos e a estrutural geral de comandos do Brigadier.
INFO
Brigadier é um comando parser de código aberto e um dispatcher escrito pela Mojang para o Minecraft. Ele é uma biblioteca de comandos baseados em árvore onde você constrói uma árvore de comandos e argumentos.
A Interface do Command
com.mojang.brigadier.Command é uma interface funcional, que executa alguns códigos específicos e lança um CommandSyntaxException em certos casos. Ela possui um tipo genérico S, que define o tipo do command source. A origem do comando fornece algum contexto em que o comando foi executado. No Minecraft, a fonte do comando é tipicamente um CommandSourceStack que pode representar um servidor, um bloco de comando, uma conexão remota (RCON), um jogador ou uma entidade.
O único método em Command, run(CommandContext<S>) leva um CommandContext<S> como o único parametro e retorna um inteiro. O contexto do comando segura a fonte do seu comando do S e te permite obter argumentos, dê uma olhada nos nós de comando analisados e veja a entrada usada nesse comando.
Como outras interfaces funcionais, ele é normalmente usado como um lambda ou uma referência de método:
java
Command<CommandSourceStack> command = context -> {
return 0;
};1
2
3
2
3
O inteiro pode ser considerado o resultado do comando. Tipicamente, valores abaixo ou iguais a zero significam que um comando falhou ou não fará nada. Valores positivos significam que o comando foi executado com sucesso e fez alguma coisa. O Brigadier fornece uma constante para indicar sucesso; Command#SINGLE_SUCCESS.
O que o CommandSourceStack Consegue Fazer?
Um CommandSourceStack fornece algum contexto específico da implementação adicional quando um comando é executado. Isso inclui a capacidade de obter a entidade que executou o comando, o mundo onde o comando foi executado, ou o servidor onde o comando foi executado.
Você pode acessar a fonte do comando a partir de um contexto de comando chamado getSource() na instância de CommandContext.
java
Command<CommandSourceStack> command = context -> {
CommandSourceStack source = context.getSource();
return 0;
};1
2
3
4
2
3
4
Registrando um Comando Básico
Comandos são registrados dentro do CommandRegistrationCallback fornecido pela Fabric API.
INFO
Para informações sobre como registrar callbacks, por favor, consulte o guia de Eventos.
O evento deve ser registrado no inicializador do seu mod.
O callback possui três parâmetros:
CommandDispatcher<CommandSourceStack> dispatcher- Usado para registrar, analisar e executar comandos.Sé o tipo de fonte de comando que o command dispatcher suporta.CommandBuildContext registryAccess- Fornece uma abstração para registros que podem ser passados para certos métodos de argumento de comandoCommands.CommandSelection environment- Identifica o tipo de servidor onde os comandos estão sendo registrados.
No inicializador do mod, nós apenas registramos um comando simples:
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(Commands.literal("test_command").executes(context -> {
context.getSource().sendSuccess(() -> Component.literal("Called /test_command."), false);
return 1;
}));
});1
2
3
4
5
6
2
3
4
5
6
No método sendSuccess(), o primeiro parâmetro é o texto a ser enviado, que é um Supplier<Component> para evitar a instanciação de objetos Component quando não for necessário.
O segundo parâmetro determina se o feedback deve ser transmitido para outros moderadores. Geralmente, se o comando for para consultar algo sem realmente afetar o mundo, como verificar a hora atual ou a pontuação de algum jogador, ele deve ser false. Se o comando fizer algo, como mudar a hora, ou modificar a pontuação de um jogador, ele deve ser true.
Se o comando falhar, em vez de chamar sendSuccess(), você pode lançar diretamente qualquer exceção e o servidor ou cliente a tratará de forma apropriada.
CommandSyntaxException é geralmente lançada para indicar erros de sintaxe em comandos ou argumentos. Você também pode implementar sua própria exceção.
Para executar este comando, você deve digitar /test_command, que diferencia maiúsculas e minúsculas.
INFO
Deste ponto em diante, estaremos extraindo a lógica escrita no lambda passada para os construtores .executes() para métodos individuais. Poderemos então passar uma referência de método para .executes(). Isso é feito para maior clareza.
Ambiente de Registro
Se desejar, você também pode garantir que um comando seja registrado apenas sob circunstâncias específicas, por exemplo, apenas no ambiente dedicado:
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
if (environment.includeDedicated) {
dispatcher.register(Commands.literal("dedicated_command")
.executes(ExampleModCommands::executeDedicatedCommand));
}
});1
2
3
4
5
6
2
3
4
5
6
java
private static int executeDedicatedCommand(CommandContext<CommandSourceStack> context) {
context.getSource().sendSuccess(() -> Component.literal("Called /dedicated_command."), false);
return 1;
}1
2
3
4
2
3
4
Requisitos de Comando
Digamos que você tenha um comando que você quer que apenas moderadores possam executar. É aqui que o método requires() entra em jogo. O método requires() tem um argumento do tipo Predicate<S>, que fornecerá um CommandSourceStack para testar e determinar se a CommandSource pode executar o comando.
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(Commands.literal("required_command")
.requires(source -> source.permissions().hasPermission(Permissions.COMMANDS_MODERATOR))
.executes(ExampleModCommands::executeRequiredCommand));
});1
2
3
4
5
2
3
4
5
java
private static int executeRequiredCommand(CommandContext<CommandSourceStack> context) {
context.getSource().sendSuccess(() -> Component.literal("Called /required_command."), false);
return 1;
}1
2
3
4
2
3
4
Esse comando só será executado se a fonte do comando for no mínimo um moderador, incluindo blocos de comando. Caso contrário, o comando não é registrado.
Isso tem o efeito colateral de não mostrar este comando no preenchimento automático Tab para quem não for um moderador. É também por isso que você não consegue preencher a maioria dos comandos com a tecla Tab quando trapaças não forem habilitadas.
Subcomandos
Para adicionar um subcomando, você registra o primeiro nó literal do comando normalmente. Para ter um subcomando, você deve anexar o próximo nó literal ao nó existente.
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(Commands.literal("command_one")
.then(Commands.literal("sub_command_one").executes(ExampleModCommands::executeSubCommandOne)));
});1
2
3
4
2
3
4
java
private static int executeSubCommandOne(CommandContext<CommandSourceStack> context) {
context.getSource().sendSuccess(() -> Component.literal("Called /command sub_command_one."), false);
return 1;
}1
2
3
4
2
3
4
Similar aos argumentos, os nós de subcomando também podem ser definidos como opcionais. No caso a seguir, tanto /command_two quanto /command_two sub_command_two serão validos.
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(Commands.literal("command_two")
.executes(ExampleModCommands::executeCommandTwo)
.then(Commands.literal("sub_command_two").executes(ExampleModCommands::executeSubCommandTwo)));
});1
2
3
4
5
2
3
4
5
java
private static int executeCommandTwo(CommandContext<CommandSourceStack> context) {
context.getSource().sendSuccess(() -> Component.literal("Called /command_two."), false);
return 1;
}
private static int executeSubCommandTwo(CommandContext<CommandSourceStack> context) {
context.getSource().sendSuccess(() -> Component.literal("Called /sub_command_two."), false);
return 1;
}1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
Comandos do Cliente
Similarmente, a Fabric API fornece o evento ClientCommandRegistrationCallback no pacote net.fabricmc.fabric.api.client.command.v2 que pode ser usado para registrar comandos do lado do cliente, substituindo a classe vanilla Commands com o equivalente ClientCommands. O código deve existir apenas no código do lado do cliente.
java
ClientCommandRegistrationCallback.EVENT.register((dispatcher, commandBuildContext) -> {
dispatcher.register(ClientCommands.literal("clienttater").executes(context -> {
context.getSource().sendFeedback(Component.literal("Called /clienttater with no arguments."));
return 1;
}));
});1
2
3
4
5
6
2
3
4
5
6
Redirecionamentos de Comando
Redirecionamentos de Comando - também conhecidos como aliases, ou apelidos - são uma forma de redirecionar a funcionalidade de um comando para outro. Isso é útil quando você deseja alterar o nome de um comando, mas ainda quer manter o suporte ao nome antigo.
WARNING
O Brigadier só irá redirecionar nós de comando que possuam argumentos. Se você quiser redirecionar um nó de comando sem argumentos, forneça um construtor .executes() com uma referência à mesma lógica, conforme descrito no exemplo.
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
CommandNode<CommandSourceStack> redirectedBy = dispatcher.register(Commands.literal("redirected_by").executes(ExampleModCommands::executeRedirectedBy));
dispatcher.register(Commands.literal("to_redirect").executes(ExampleModCommands::executeRedirectedBy).redirect(redirectedBy));
});1
2
3
4
2
3
4
java
private static int executeRedirectedBy(CommandContext<CommandSourceStack> context) {
context.getSource().sendSuccess(() -> Component.literal("Called /redirected_by."), false);
return 1;
}1
2
3
4
2
3
4
Perguntas Frequentes
Por Que Meu Código Não Compila?
Capture ou lance uma
CommandSyntaxException-CommandSyntaxExceptionnão é umaRuntimeException. Se você a lança, isso deve ser feito em métodos que declaram aCommandSyntaxExceptionem métodos assinaturas, ou ela deve ser capturada. O Brigadier vai lidar com as exceções checadas e encaminhará a mensagem de erro apropriada no jogo para você.Problemas com generics - Você pode ter um problema com generics ocasionalmente. Se você estiver registrando comandos de servidor (o que é maioria dos casos), certifique-se de estar usando
Command.literalouCommands.argumentem vez deLiteralArgumentBuilder.literalouRequiredArgumentBuilder.argument.Verifique o método
sendSuccess()- Você pode ter esquecido de fornecer um booleano como segundo argumento. Lembre-se também que, desde o Minecraft 1.20, o primeiro parâmetro éSupplier<Component>em vez deComponent.Um Comando deve retornar um inteiro - Ao registrar comandos, o método
executes()aceita um objetoCommand, que geralmente é um lambda. O lambda deve retornar um valor inteiro, em vez de outros tipos.
Posso Registar Comandos em Tempo de Execução?
WARNING
Você pode fazer isso, mas não é recomendado. Você pegaria o Commands do servidor e adicionaria comandos a qualquer coisa que você deseja ao CommandDispatcher dele.
Depois disso, você precisa enviar a árvore de comandos para todos os jogadores novamente usando Commands.sendCommands(ServerPlayer).
Isso é necessário porque o cliente armazena em cache localmente a árvore de comandos que recebe durante o login (ou quando pacotes de moderador são enviados) para mensagens de erro ricas em preenchimentos locais.
Posso Desregistrar Comandos em Tempo de Execução?
WARNING
Você também pode fazer isso, no entanto, isso é muito menos estável do que registrar comandos durante o tempo de execução e poderia causar efeitos colaterais indesejados.
Para manter as coisas simples, você precisa usar reflection no Brigadier e remover nós. Depois disso, você precisa enviar a árvore de comandos para cada jogar novamente usando sendCommands(ServerPlayer).
Se você não enviar a árvore de comandos atualizada, o cliente pode pensar que um comando ainda existe, mesmo que o servidor falhe na execução.













