Durch das Erstellen von Befehlen kann ein Mod-Entwickler Funktionen hinzufügen, die durch einen Befehl verwendet werden können. Dieses Tutorial wird dir erklären, wie man Befehle registriert und die allgemeine Befehlsstruktur von Brigadier.
INFO
Brigadier ist ein Befehlsparser und Dispatcher, der von Mojang für Minecraft entwickelt wurde. Es ist eine baumbasierte Befehlsbibliothek, in der du einen Baum von Befehlen und Argumenten aufbaust.
Brigadier ist Open Source: https://github.com/Mojang/brigadier
Das Interface Command
com.mojang.brigadier.Command ist ein funktionales Interface, das einen bestimmten Code ausführt und in bestimmten Fällen eine CommandSyntaxException auslöst. Er hat einen generischen Typ S, der den Typ der Befehlsquelle definiert. Die Befehlsquelle liefert einen Kontext, in dem ein Befehl ausgeführt wurde. In Minecraft ist die Befehlsquelle normalerweise ein CommandSourceStack, der einen Server, einen Befehlsblock, eine Remote-Verbindung (RCON), einen Spieler oder eine Entität darstellen kann.
Die einzige Methode in Command, run(CommandContext<S>), nimmt einen CommandContext<S> als einzigen Parameter und gibt eine ganze Zahl zurück. Der Befehlskontext enthält die Befehlsquelle von S und ermöglicht es dir, Argumente zu erhalten, die geparsten Befehlsknoten zu betrachten und die in diesem Befehl verwendete Eingabe zu sehen.
Wie andere funktionale Interfaces wird es in der Regel als Lambda oder als Methodenreferenz verwendet:
java
Command<CommandSourceStack> command = context -> {
return 0;
};1
2
3
2
3
Die Ganzzahl kann als Ergebnis des Befehls betrachtet werden. Normalerweise bedeuten Werte kleiner oder gleich Null, dass ein Befehl fehlgeschlagen ist und nichts machen wird. Positive Werte bedeuten, dass der Befehl erfolgreich war und etwas gemacht hat. Brigadier bietet eine Konstante zur Anzeige von Erfolg; Befehl#SINGLE_SUCCESS.
Was kann der CommandSourceStack machen?
Ein CommandSourceStack liefert einen zusätzlichen implementierungsspezifischen Kontext, wenn ein Befehl ausgeführt wird. Dazu gehört die Möglichkeit, die Entität, die den Befehl ausgeführt hat, die Welt, in der der Befehl ausgeführt wurde, oder den Server, auf dem der Befehl ausgeführt wurde, zu ermitteln.
Du kannst auf die Befehlsquelle von einem Befehlskontext aus zugreifen, indem du getSource() für die Instanz CommandContext aufrufst.
java
Command<CommandSourceStack> command = context -> {
CommandSourceStack source = context.getSource();
return 0;
};1
2
3
4
2
3
4
Registrieren eines einfachen Befehls
Befehle werden innerhalb des CommandRegistrationCallback registriert, der von der Fabric API bereitgestellt wird.
INFO
Informationen zur Registrierung von Callbacks findest du in der Anleitung Events.
Das Event sollte in deinem Mod Initialisierer registriert werden.
Der Callback hat drei Parameter:
CommandDispatcher<CommandSourceStack> dispatcher- Wird genutzt, um Befehle zu registrieren, parsen und auszuführen.Sist der Typ der Befehlsquelle, die der Command Dispatcher unterstützt.CommandBuildContext registryAccess- Bietet eine Abstraktion zu Registrys, die an bestimmte Befehlsargumente übergeben werden können Argument-MethodenCommands.Commands environment- Identifiziert den Typ des Servers, auf dem die Befehle registriert werden.
Im Mod-Initialisierer registrieren wir nur einen einfachen Befehl:
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
In der Methode sendSuccess() ist der erste Parameter der zu sendende Text, der ein Supplier<Component> ist, um zu vermeiden, dass Component-Objekte instanziert werden, wenn sie nicht benötigt werden.
Der zweite Parameter bestimmt, ob die Rückmeldung an andere Moderatoren gesendet werden soll. Im Allgemeinen sollte der Befehl false sein, wenn er etwas abfragen soll, ohne die Welt tatsächlich zu beeinflussen, wie zum Beispiel die aktuelle Zeit oder den Punktestand eines Spielers. die Zeit zu ändern oder den Spielstand einer Person zu ändern, sollte er true sein.
Wenn der Befehl fehlschlägt, kannst du, anstatt sendSuccess() aufzurufen, direkt eine beliebige Ausnahme auslösen, die vom Server oder Client entsprechend behandelt wird.
Die CommandSyntaxException wird im Allgemeinen ausgelöst, um Syntaxfehler in Befehlen oder Argumenten aufzuzeigen. Du kannst auch deine eigene Exception implementieren.
Um diesen Befehl auszuführen, musst du /test_command eingeben, wobei Groß- und Kleinschreibung zu beachten sind.
INFO
Ab diesem Punkt werden wir die Logik, die in den an .executes()-Builder übergebenen Lambda-Ausdrücken geschrieben ist, in einzelne Methoden extrahieren. Wir können dann eine Methodenreferenz an .executes() übergeben. Dies dient der Übersichtlichkeit.
Umgebung der Registrierung
Falls gewünscht, kannst du auch dafür sorgen, dass ein Befehl nur unter bestimmten Umständen registriert wird, zum Beispiel nur in der dedizierten Umgebung:
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
5
2
3
4
5
Befehlsanforderungen
Angenommen, du hast einen Befehl, den nur Moderatoren ausführen können sollen. An dieser Stelle kommt die Methode requires() ins Spiel. Die Methode requires() hat ein Argument vom Typ Predicate<S>, das einen CommandSourceStack bereitstellt, mit dem getestet und festgestellt wird, ob die CommandSource den Befehl ausführen kann.
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
5
2
3
4
5
Dieser Befehl wird nur ausgeführt, wenn die Quelle des Befehls mindestens ein Moderator ist, einschließlich Befehlsblöcke. Andernfalls ist der Befehl nicht registriert.
Dies hat den Nebeneffekt, dass dieser Befehl in der Tab-Vervollständigung für alle, die nicht Moderator sind, nicht angezeigt wird. Das ist auch der Grund, warum du die meisten Befehle nicht mit Tab vervollständigen kannst, wenn du keine Cheats aktivierst.
Unterbefehle
Um einen Unterbefehl hinzuzufügen, registriere den ersten buchstäblichen Knoten des Befehls ganz normal. Um einen Unterbefehl zu haben, musst du den nächsten buchstäblichen Knoten an den bestehenden Knoten anhängen.
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
5
2
3
4
5
Ähnlich wie die Argumente können auch die Unterbefehlsknoten auf optional gesetzt werden. Im folgenden Fall sind sowohl /command_two als auch /command_two sub_command_two gültig.
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
10
2
3
4
5
6
7
8
9
10
Client-Befehle
Die Fabric API verfügt über einen ClientCommandManager im Paket net.fabricmc.fabric.api.client.command.v2, der zur Registrierung clientseitiger Befehle verwendet werden kann. Der Code sollte nur im clientseitigen Code vorhanden sein.
java
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
dispatcher.register(ClientCommandManager.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
Befehlsumleitungen
Befehlsumleitungen - auch bekannt als Aliase - sind eine Möglichkeit, die Funktionalität eines Befehls auf einen anderen umzuleiten. Dies ist nützlich, wenn du den Namen eines Befehls ändern möchtest, aber den alten Namen beibehalten willst.
WARNING
Brigadier wird nur Befehlsknoten mit Argumenten umleiten. Wenn du einen Befehlsknoten ohne Argumente umleiten willst, gib einen .executes() Builder mit einem Verweis auf die gleiche Logik wie im Beispiel beschrieben an.
java
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
var 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
5
2
3
4
5
FAQ
Warum kompiliert mein Code nicht?
Abfangen oder Auslösen einer
CommandSyntaxException-CommandSyntaxExceptionist keineRuntimeException. Wenn du sie auslöst, sollte sie in Methoden ausgelöst werden, dieCommandSyntaxExceptionin den Methodensignaturen auslösen, oder sie sollte abgefangen werden. Brigadier wird die checked Exceptions behandeln und die entsprechende Fehlermeldung im Spiel für dich weiterleiten.Probleme mit generischen Typen - Es kann sein, dass du hin und wieder ein Problem mit generischen Typen hast. Wenn du Serverbefehle registrierst (was in den meisten Fällen der Fall ist), stelle sicher, dass du
Commands.literaloderCommands.argumentanstelle vonLiteralArgumentBuilder.literaloderRequiredArgumentBuilder.argumentbenutzt.Überprüfe die Methode
sendSuccess()– Möglicherweise hast du vergessen, einen booleschen Wert als zweites Argument anzugeben. Denke auch daran dass seit Minecraft 1.20 der erste ParameterSupplier<Component>anstelle einesComponentist.Ein Befehl sollte eine ganze Zahl zurückgeben - Bei der Registrierung von Befehlen akzeptiert die Methode
executes()einCommandObjekt, das normalerweise ein Lambda ist. Das Lambda sollte eine ganze Zahl zurückgeben, anstelle anderen Typen.
Kann ich Befehle zur Laufzeit registrieren?
WARNING
Du kannst dies machen, aber es ist nicht empfohlen. Du würdest die Commands vom Server bekommen und fügst alle gewünschten Befehle zu seinem CommandDispatcher hinzu.
Danach musst du den Befehlsbaum erneut an jeden Spieler mit Commands.sendCommands(ServerPlayer) senden.
Dies ist erforderlich, da der Client den Befehlsbaum, den er bei der Anmeldung (oder beim Senden von Moderator-Paketen) erhält, lokal zwischenspeichert, um Fehlermeldungen zu vervollständigen.
Kann ich die Registrierung von Befehlen während der Laufzeit aufheben?
WARNING
Du kannst dies auch tun, allerdings ist es viel weniger stabil als die Registrierung von Befehlen zur Laufzeit und könnte unerwünschte Nebenwirkungen haben.
Um die Dinge einfach zu halten, musst du Reflection auf Brigadier anwenden und Knoten entfernen. Danach musst du den Befehlsbaum erneut an jeden Spieler mit sendCommands(sendCommands) senden.
Wenn du den aktualisierten Befehlsbaum nicht sendest, kann es sein, dass der Client denkt, dass der Befehl noch existiert, obwohl die Ausführung am Server fehlschlägt.













