🇩🇪 Deutsch (German)
🇩🇪 Deutsch (German)
Erscheinungsbild
🇩🇪 Deutsch (German)
🇩🇪 Deutsch (German)
Erscheinungsbild
This page is written for:
1.21
This page is written for:
1.21
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 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 ist Open Source: https://github.com/Mojang/brigadier
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 ServerCommandSource
, die 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:
Command<ServerCommandSource> command = context -> {
return 0;
};
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
.
ServerCommandSource
machen? Eine "ServerCommandSource" 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.
Command<ServerCommandSource> command = context -> {
ServerCommandSource source = context.getSource();
return 0;
};
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 im Initialisierer deines Mods registriert werden.
Der Callback hat drei Parameter:
CommandDispatcher<ServerCommandSource> dispatcher
- Dient zum Registrieren, Parsen und Ausführen von Befehlen. S
ist der Typ der Befehlsquelle, die der Command Dispatcher unterstützt.CommandRegistryAccess registryAccess
- Bietet eine Abstraktion zu Registrys, die an bestimmte Befehlsargumente übergeben werden können Argument-MethodenCommandManager.RegistrationEnvironment environment
- Identifiziert den Typ des Servers, auf dem die Befehle registriert werden.Im Mod-Initialisierer registrieren wir nur einen einfachen Befehl:
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;
}));
});
In der Methode sendFeedback()
ist der erste Parameter der zu sendende Text, der ein Supplier<Text>
ist, um zu vermeiden, dass Text-Objekte instanziert werden, wenn sie nicht benötigt werden.
Der zweite Parameter bestimmt, ob die Rückmeldung an andere Operatoren 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 sendFeedback()
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
Von diesem Punkt an werden wir die Logik extrahieren, die innerhalb des Lambdas an den .execute()
-Builder übergeben wird, in einzelne Methoden extrahieren. Wir können dann eine Methodenreferenz an .execute()
übergeben. Dies dient der Übersichtlichkeit.
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:
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
if (environment.dedicated) {
dispatcher.register(CommandManager.literal("dedicated_command")
.executes(FabricDocsReferenceCommands::executeDedicatedCommand));
}
});
private static int executeDedicatedCommand(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /dedicated_command."), false);
return 1;
}
Angenommen, du hast einen Befehl, den nur Operatoren ausführen können sollen. An dieser Stelle kommt die Methode requires()
ins Spiel. Die Methode requires()
hat ein Argument eines Predicate<S>
, das eine ServerCommandSource
liefert, mit der getestet werden kann, ob die CommandSource
den Befehl ausführen kann.
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("required_command")
.requires(source -> source.hasPermissionLevel(1))
.executes(FabricDocsReferenceCommands::executeRequiredCommand));
});
private static int executeRequiredCommand(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /required_command."), false);
return 1;
}
Dieser Befehl wird nur ausgeführt, wenn die Quelle des Befehls mindestens ein Operator der Ebene 2 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 Level 2 Operator sind, nicht angezeigt wird. Das ist auch der Grund, warum du die meisten Befehle nicht mit der Tabulatortaste vervollständigen kannst, wenn du keine Cheats aktivierst.
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.
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("command_one")
.then(CommandManager.literal("sub_command_one").executes(FabricDocsReferenceCommands::executeSubCommandOne)));
});
private static int executeSubCommandOne(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /command sub_command_one."), false);
return 1;
}
Ä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.
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
dispatcher.register(CommandManager.literal("command_two")
.executes(FabricDocsReferenceCommands::executeCommandTwo)
.then(CommandManager.literal("sub_command_two").executes(FabricDocsReferenceCommands::executeSubCommandTwo)));
});
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;
}
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.
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
dispatcher.register(ClientCommandManager.literal("clienttater").executes(context -> {
context.getSource().sendFeedback(Text.literal("Called /clienttater with no arguments."));
return 1;
}));
});
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.
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));
});
private static int executeRedirectedBy(CommandContext<ServerCommandSource> context) {
context.getSource().sendFeedback(() -> Text.literal("Called /redirected_by."), false);
return 1;
}
Abfangen oder Auslösen einer CommandSyntaxException
- CommandSyntaxException
ist keine RuntimeException
. Wenn du sie auslöst, sollte sie in Methoden ausgelöst werden, die CommandSyntaxException
in 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 CommandManager.literal
oder CommandManager.argument
anstelle von LiteralArgumentBuilder.literal
oder RequiredArgumentBuilder.argument
benutzt.
Überprüfe die Methode sendFeedback()
- Du hast vielleicht vergessen, einen booleschen Wert als zweites Argument anzugeben. Denke auch daran dass seit Minecraft 1.20 der erste Parameter Supplier<Text>
anstelle von Text
ist.
Ein Befehl sollte eine ganze Zahl zurückgeben - Bei der Registrierung von Befehlen akzeptiert die Methode executes()
ein Command
Objekt, das normalerweise ein Lambda ist. Das Lambda sollte eine ganze Zahl zurückgeben, anstelle anderen Typen.
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
.
Danach musst du den Befehlsbaum mit CommandManager.sendCommandTree(ServerPlayerEntity)
erneut an jeden Spieler senden.
Dies ist erforderlich, da der Client den Befehlsbaum, den er bei der Anmeldung (oder beim Senden von Operator-Paketen) erhält, lokal zwischenspeichert, um Fehlermeldungen zu vervollständigen.
WARNING
You can also do this, however, it is much less stable than registering commands at runtime and could cause unwanted side effects.
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 sendCommandTree(ServerPlayerEntity)
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.