Please refer to https://github.com/falkreon/BrigadierAdvice for more tricks and tips!
So you may be familiar with some fluent command builder syntax for Brigadier, but let me tell you, it looks bad.
The brigadier tree is a tree. Just to name a subset, let's look at /weather
and /kill
, and
see what it would take to actually make a clean-looking command tree for them if they didn't exist.
"/"
/ \
kill weather
/ | \
clear rain thunder
(we'll talk about the optional arguments for kill
later.)
Most commands start with the slash (not part of brigadier) followed by the command name. This is
a LiteralCommandNode
. In fact, any bare token you see is probably a literal. The clear
, rain
,
and thunder
are also literal nodes, for example. So at least for now, we've got a whole tree that
just contains the root node and some literal nodes.
Some nodes on the tree have Command
objects hung on them. You have a lot of flexibility on where
to hang them. for kill
, you'd definitely need to hang the KillCommand directly on the kill
node,
because it's the only one. But for weather
, you could either hang one Command each onto clear
,
rain
, and thunder
, or you could hang a Command onto weather
that could handle all three. If
the "leaf" node doesn't contain an executes
clause, Brigadier will walk one level back up the tree
and check again, and keep doing so until it finds a command. If it walks all the way up to the root
without finding one, it'll grab the "command usage" for that command, and print out an error with it.
It happens that vanilla puts the executes on
clear
,rain
, andthunder
for the weather command, so that's what I'll show here.
How do we put these nodes together?
CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
//Make some new nodes
LiteralCommandNode<ServerCommandSource> killNode = CommandManager
.literal("kill")
.executes(new KillCommand())
.build();
LiteralCommandNode<ServerCommandSource> weatherNode = CommandManager
.literal("weather")
.build();
LiteralCommandNode<ServerCommandSource> clearNode = CommandManager
.literal("clear")
.executes(WeatherCommand::clear)
.build();
LiteralCommandNode<ServerCommandSource> rainNode = CommandManager
.literal("rain")
.executes(WeatherCommand::rain)
.build();
LiteralCommandNode<ServerCommandSource> thunderNode = CommandManager
.literal("thunder")
.executes(WeatherCommand::thunder)
.build();
//Now stitch them together
//usage: /kill
dispatcher.getRoot().addChild(killNode);
//usage: /weather [clear|rain|thunder]
dispatcher.getRoot().addChild(weatherNode);
weatherNode.addChild(clearNode);
weatherNode.addChild(rainNode);
weatherNode.addChild(thunderNode);
});
It's often most readable to just build out the individual nodes, trying to follow branches down as coherently as possible, and then at the end use addChild to tell Brigadier - and other programmers - how they're connected.
Command is a functional interface, so you could use a lambda, but I often find I'd rather have a class for either one command or for a group of related commands:
public class KillCommand implements Command<ServerCommandSource> {
@Override
public int run(CommandContext<ServerCommandSource> context) throws CommandSyntaxException {
context.getPlayer().kill(); //Good enough for now!
return 1; //positive numbers are success! Negative numbers are failure.
}
}
public class WeatherCommand {
public static int clear(CommandContext<ServerCommandSource> context) throws CommandSyntaxException {
ServerCommandSource source = context.getSource();
source.getWorld().getLevelProperties().setClearWeatherTime(6000);
source.getWorld().getLevelProperties().setRainTime(0);
source.getWorld().getLevelProperties().setThunderTime(0);
source.getWorld().getLevelProperties().setRaining(false);
source.getWorld().getLevelProperties().setThundering(false);
source.sendFeedback(new TranslatableText("commands.weather.set.clear"), true);
return 6000; //Often we return a positive number related to what we did,
//in this case the number of clear-weather ticks we set
}
public static int rain(CommandContext<ServerCommandSource> context) throws CommandSyntaxException {
/* impl goes here */
return 1;
}
public static int thunder(CommandContext<ServerCommandSource> context) throws CommandSyntaxException {
/* impl goes here */
return 1;
}
}
the int return is actually the execute store result or the redstone output of the comparator on the command block