User Tools

Site Tools


zh_cn:tutorial:commands

This is an old revision of the document!


条款:本文中的代码适用于“Creative Commons Zero v1.0 Universial”条款,允许您将文中的代码示例用于自己的模组中。

注: 本文翻译自英文版commands,翻译未完成,且部分翻译可能不准确。

创建命令

创建命令允许模组开发者添加可以使用命令实现的功能。本教程将会教你如何注册命令,以及Brigadier的基本命令结构。

注意:本文的所有代码都是用于1.14.4的。部分yarn映射可能改变了,不过代码应该可用。

Brigadier是什么?

Brigadier是由Mojang写的用于Minecraft的命令解析器和派发器。Brigadier是基于树的命令库,可以建立参数与命令的数。

这是Brigadier的源代码:https://github.com/Mojang/brigadier

命令是什么?

Brigadier需要你指定Command以运行(run)。“命令”在brigadier中是个松散的属于,但通常指的是命令树的退出点(exit point),即代码执行命令的地方。一个Command是个函数的/功能的接口。命令有通用类型S,定理命令源(command source)的类型。命令源提供一些语境(context),命令在该语境中运行。在Minecraft中,这个语境尤其是指ServerCommandSource,代表着一个服务器、命令方块、rcon connection、玩家或实体。

命令Command中的单个方法,run(CommandContext<S>)有一个仅有的参数CommandContext<S>,并返回整数。命令语境(command context)持有S的命令源,并允许你获取参数、查看解析的命令节点并查看用于此命令的输入。

命令可以通过以下方式实现:

作为匿名函数

  1. Command<Object> command = context -> {
  2. return 0;
  3. };

作为匿名类

  1. Command<Object> command = new Command<Object>() {
  2. @Override
  3. public int run(CommandContext<Object> context) {
  4. return 0;
  5. }
  6. }

作为类来实现

  1. final class XYZCommand implements Command<Object> {
  2. @Override
  3. public int run(CommandContext<Object> context) {
  4. return 0;
  5. }
  6. }

作为方法引用

  1. void registerCommand() {
  2. // 暂时忽略这里,后面会解释。
  3. dispatcher.register(CommandManager.literal("foo"))
  4. .executes(this::execute); // 指下方的"execute"方法。
  5. }
  6.  
  7. private int execute(CommandContext<Object> context) {
  8. return 0;
  9. }

run(CommandContext)方法可能会抛出错误CommandSyntaxException,本教程后面会讲。

可以认为命令的结果是个整数。在Minecraft中,这个结果代表红石中继器从命令方块得到的红石信号强度,或者连锁型命令方块传递给其面向的方块的值。一般地,负值意味着命令失败,不执行操作。结果为0表示命令被忽略(pass)。正值表示命令成功执行。

基本命令

下面是个不含参数的命令:

  1. dispatcher.register(CommandManager.literal("foo").executes(context -> {
  2. System.out.println("调用了foo,没有参数");
  3.  
  4. return 1;
  5. }));

CommandManager.literal(“foo”)会告诉bridagier,这个命令有一个节点,即称为foo文字(literal)。执行此命令只能输入,/foo,输入/Foo/FoO/FOO/fOO或者/fooo都不会执行命令。

静态导入

每次输入CommandManager.literal(“foo”)你都需要创建一个可能多余的文字。你可以通过静态导入参数以清理你的代码库。<!– You can clean up your codebase by use of static imports for the arguments. –>对于文字,这会将语句缩短到literal(“foo”)。这也可以用于获取参数的值,会将StringArgumentType.getString(ctx, “string”)缩短到getString(ctx, “string”)。这也适用于Minecraft自身的参数类型。

下面是静态导入的一些例子:

  1. // getString(ctx, "string")
  2. import static com.mojang.brigadier.arguments.StringArgumentType.getString;
  3. // word()
  4. import static com.mojang.brigadier.arguments.StringArgumentType.word;
  5. // literal("foo")
  6. import static net.minecraft.server.command.CommandManager.literal;
  7. // argument("bar", word())
  8. import static net.minecraft.server.command.CommandManager.argument;
  9. // Import everything
  10. import static net.minecraft.server.command.CommandManager.*;

注意:请确保你用的是CommandManager中的literalargument,否则尝试编译时可能会出问题。

Brigadier的默认参数位于com.mojang.brigadier.arguments

Minecraft的默认参数位于net.minecraft.command.arguments。CommandManager是net.minecraft.server.command中的包。

子命令

要添加子命令(sub command),你需要先注册命令的第一个文字节点。

  1. dispatcher.register(CommandManager.literal("foo")

要有子命令,需要将下一个节点附着于已有的节点之后。通过then(ArgumentBuilder)方法完成,which takes in an ArgumentBuilder.

创建如下所示的命令foo <bar>

  1. dispatcher.register(literal("foo")
  2. .then(literal("bar"))
  3. );

建议你为命令添加节点时将代码缩进。通常缩进意味着命令树的节点有多深。新行也可以使得另一个节点加入时清晰明了。本教程后面会展示的命令中有不同的节点风格。

现在,尝试运行命令

Most likely if you typed /foo bar in game, the command will fail to run. This is because there is no code for the game to execute when all the required arguments have been met. To fix this, you need to tell the game what to run when the command is being executed using the executes(Command) method. Below is how the command should look as an example.

  1. dispatcher.register(literal("foo")
  2. .then(literal("bar")
  3. .executes(context -> {
  4. System.out.println("Called foo with bar");
  5.  
  6. return 1;
  7. })
  8. )
  9. );

注册命令

可以使用CommandRegistrationCallback来注册一个callback从而完成注册命令。关于注册callback的信息,请参考callbacks的文章

事件需要在mod的初始化器(initializer)中注册。callback有两个参数:CommmandDispatcher<S>用于注册、解析和执行命令,S是命令派发器(command dispatcher)支持的命令源的类型。第二个参数是布尔值,标识着命令注册的服务器的类型,是一个dedicated还是integrated的服务器。

  1. public class ExampleCommandMod implements ModInitializer {
  2. @Override
  3. public void onInitialize() {
  4. CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
  5. ...
  6. });
  7. }
  8. }

在你的匿名函数中,方法引用或者不管你选的什么,你都会注册命令。

  1. public class ExampleCommandMod implements ModInitializer {
  2. @Override
  3. public void onInitialize() {
  4. CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
  5. dispatcher.register(literal("foo").executes(context -> {
  6. System.out.println("foo");
  7. return 1;
  8. }));
  9. });
  10. }
  11. }

如有需要,可以通过检查dedicated来确保命令只在一个专用服务器注册。

  1. public class ExampleCommandMod implements ModInitializer {
  2. @Override
  3. public void onInitialize() {
  4. CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
  5. if (dedicated) {
  6. TestDedicatedCommand.register(dispatcher);
  7. }
  8. });
  9. }
  10. }

类似地,

  1. public class ExampleCommandMod implements ModInitializer {
  2. @Override
  3. public void onInitialize() {
  4. CommandRegistrationCallback.EVENT.register((dispatcher, dedicated) -> {
  5. if (!dedicated) {
  6. TestIntegratedCommand.register(dispatcher);
  7. }
  8. });
  9. }
  10. }

参数

Brigadier中的参数会解析并检查输入的参数中的错误。Minecraft创建了一些特殊的参数用于其自身的使用,比如EntityArgumentType代表游戏内的实体选择器@a, @r, @p, @e[type=!player, limit=1, distance=..2],或者NbtTagArgumentType用于解析字符串化的nbt(snbt)并且确认输入的语法是正确的。

关于参数的更多叙述,请关于英文页面中的有关内容。

高级概念

Below are links to the articles about more complex concepts used in brigadier.

Page Description
Requirements Only allow users to execute commands in certain scenarios.
Exceptions Fail execution of a command with a descriptive message and in certain contexts.
Suggestions Suggesting command input for the client.
Redirects (Aliases) Allow use of aliases to execute commands.
Redirects (Chaining) Allow commands to have repeating elements and flags.
Custom Argument Types Parse your own arguments into your own objects.

TODO: Sections are being moved to sub categories and will be added to their respective articles as they are migrated.

常见问题

为什么我的命令不编译

There are two immediate possibilities for why this could occur.

Catch or throw a CommandSyntaxException

The solution to this issue is to make the run or suggest methods throw a CommandSyntaxException. Brigadier will handle the checked exceptions and forward the proper error message in game for you.

通用问题

You may have an issue with generic types once in a while. Verify you are using CommandManager.literal(…) or CommandManager.argument(…) instead LiteralArgumentBuilder or RequiredArgumentBuilder in your static imports.

可以注册客户端命令吗?

Fabric doesn't currently support client side commands. There is a third-party mod by the Cotton team that adds this functionality. There is an open pull request to fabric api which adds this. That will be documented on this page in the future.

不建议的操作

A few things we don't recommend, but are possible.

可以在运行时注册命令吗?

You can do this but it is not recommended. You would get the CommandManager from the server and add anything commands you wish to it's CommandDispatcher.

After that you need to send the command tree to every player again using CommandManager.sendCommandTree(ServerPlayerEntity). This is required because the client locally caches the command tree it receives during login (or when operator packets are sent) for local completions rich error messages.

可以在运行时取消注册命令吗?

You can also do this, however it is much less stable than registering commands and could cause unwanted side effects. To keep things simple, you need to use reflection on brigadier and remove the nodes. After this, you need to send the command tree to every player again using sendCommandTree(ServerPlayerEntity). If you don't send the updated command tree, the client may think a command still exists, even though the server will fail execution.


Sorry for the mess

Currently this article is being migrated, so things may be a mess. Below is are the parts of the article that are yet to be migrated to the new format.

Licensing from below onwards is available under the “CC Attribution-Noncommercial-Share Alike 4.0 International” license. This is the current license of the other wiki articles.

Requirements

Lets say you have a command you only want operators to be able to execute. This is where the requires method comes into play. The requires method has one argument of a Predicate<ServerCommandSource> which will supply a ServerCommandSource to test with and determine if the CommandSource can execute the command.

For example this may look like the following:

  1. dispatcher.register(literal("foo")
  2. .requires(source -> source.hasPermissionLevel(4))
  3. .executes(ctx -> {
  4. ctx.getSource().sendFeedback(new LiteralText("You are an operator"), false);
  5. return 1;
  6. });

This command will only execute if the Source of the command is a level 4 operator at minimum. If the predicate returns false, then the command will not execute. Also this has the side effect of not showing this command in tab completion to anyone who is not a level 4 operator.

Nothing prevents someone from specifying calls to permissions implementations within the requires block. Just note that if permissions change, you need to re send the command tree.

Exceptions

Brigadier supports command exceptions which can be used to end a command such as if an argument didn't parse properly or the command failed to execute, as well as including richer details of the failure.

All the exceptions from Brigadier are based on the CommandSyntaxException. The two main types of exceptions Brigadier provides are Dynamic and Simple exception types, of which you must create() the exception to throw it. These exceptions also allow you to specify the context in which the exception was thrown using createWithContext(ImmutableStringReader), which builds the error message to point to where on the inputted command line the error occured. Below is a coin flip command to show an example of exceptions in use.

  1. dispatcher.register(CommandManager.literal("coinflip")
  2. .executes(ctx -> {
  3. Random random = new Random();
  4.  
  5. if(random.nextBoolean()) { // If heads succeed.
  6. ctx.getSource().sendMessage(new TranslateableText("coin.flip.heads"))
  7. return Command.SINGLE_SUCCESS;
  8. }
  9.  
  10. throw new SimpleCommandExceptionType(new TranslateableText("coin.flip.tails")).create(); // Oh no tails, you lose.
  11. }));

Though you are not just limited to a single type of exception as Brigadier also supplies Dynamic exceptions which take additional parameters for context.

  1. DynamicCommandExceptionType used_name = new DynamicCommandExceptionType(name -> {
  2. return new LiteralText("The name: " + (String) name + " has been used");
  3. });

There are more Dynamic exception types which each take a different amount of arguments into account (Dynamic2CommandExceptionType, Dynamic3CommandExceptionType, Dynamic4CommandExceptionType, DynamicNCommandExceptionType). You should remember that the Dynamic exceptions takes an object as an argument so you may have to cast the argument for your use.

Redirects (Aliases)

Redirects are Brigadier's form of aliases. Below is how Minecraft handles /msg have an alias of /tell and /w.

  1. public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
  2. LiteralCommandNode node = registerMain(dispatcher); // Registers main command
  3. dispatcher.register(literal("tell")
  4. .redirect(node)); // Alias 1, redirect to main command
  5. dispatcher.register(literal("w")
  6. .redirect(node)); // Alias 2, redirect to main command
  7. }
  8.  
  9. public static LiteralCommandNode registerMain(CommandDispatcher<ServerCommandSource> dispatcher) {
  10. return dispatcher.register(literal("msg")
  11. .then(argument("targets", EntityArgumentType.players())
  12. .then(argument("message", MessageArgumentType.message())
  13. .executes(ctx -> {
  14. return execute(ctx.getSource(), getPlayers(ctx, "targets"), getMessage(ctx, "message"));
  15. }))));
  16. }

The redirect tells brigadier to continue parsing the command at another command node.

Redirects (Chainable Commands)

Commands such as /execute as @e[type=player] in the_end run tp ~ ~ ~ are possible because of redirects. Below is an example of a chainable command:

  1. LiteralCommandNode<ServerCommandSource> root = dispatcher.register(literal("fabric_test"));
  2. LiteralCommandNode<ServerCommandSource> root1 = dispatcher.register(literal("fabric_test")
  3. // You can register under the same literal more than once, it will just register new parts of the branch as shown below if you register a duplicate branch an error will popup in console warning of conflicting commands but one will still work.
  4. .then(literal("extra")
  5. .then(literal("long")
  6. .redirect(root, this::lengthen)) // Return to root for chaining
  7. .then(literal("short")
  8. .redirect(root, this::shorten))) // Return to root for chaining
  9. .then(literal("command")
  10. .executes(ctx -> {
  11. ctx.getSource().sendFeedback(new LiteralText("Chainable Command"), false);
  12. return Command.SINGLE_SUCCESS;
  13. })));

The redirect can also modify the CommandSource by use of a redirect modifier which can be used for builder commands.

  1. .redirect(rootNode, context -> {
  2. return ((ServerCommandSource) context.getSource()).withLookingAt(Vec3ArgumentType.getVec3(context, "pos"));
  3. })

What can the ServerCommandSource do?

A server command source provides some additional implementation specific context when a command is run. This includes the ability to get the entity that executed the command, the world the command was ran in or the server the command was run on.

  1. final ServerCommandSource source = ctx.getSource();
  2. // Get the source. This will always work.
  3.  
  4. final Entity sender = source.getEntity();
  5. // Unchecked, may be null if the sender was the console.
  6.  
  7. final Entity sender2 = source.getEntityOrThrow();
  8. // Will end the command if the source of the command was not an Entity.
  9. // The result of this could contain a player. Also will send feedback telling the sender of the command that they must be an entity.
  10. // This method will require your methods to throw a CommandSyntaxException.
  11. // The entity options in ServerCommandSource could return a CommandBlock entity, any living entity or a player.
  12.  
  13. final ServerPlayerEntity player = source.getPlayer();
  14. // Will end the command if the source of the command was not explicitly a Player. Also will send feedback telling the sender of the command that they must be a player. This method will require your methods to throw a CommandSyntaxException
  15.  
  16. source.getPosition();
  17. // Get's the sender's position as a Vec3 when the command was sent. This could be the location of the entity/command block or in the case of the console, the world's spawn point.
  18.  
  19. source.getWorld();
  20. // Get's the world the sender is within. The console's world is the same as the default spawn world.
  21.  
  22. source.getRotation();
  23. // Get's the sender's rotation as a Vec2f.
  24.  
  25. source.getMinecraftServer();
  26. // Access to the instance of the MinecraftServer this command was ran on.
  27.  
  28. source.getName();
  29. // The name of the command source. This could be the name of the entity, player, the name of a CommandBlock that has been renamed before being placed down or in the case of the Console, "Console"
  30.  
  31. source.hasPermissionLevel(int level);
  32. // Returns true if the source of the command has a certain permission level. This is based on the operator status of the sender. (On an integrated server, the player must have cheats enabled to execute these commands)

Some example commands examples

Broadcast a message

  1. public static void register(CommandDispatcher<ServerCommandSource> dispatcher){
  2. dispatcher.register(literal("broadcast")
  3. .requires(source -> source.hasPermissionLevel(2)) // Must be a game master to use the command. Command will not show up in tab completion or execute to non operators or any operator that is permission level 1.
  4. .then(argument("color", ColorArgumentType.color())
  5. .then(argument("message", greedyString())
  6. .executes(ctx -> broadcast(ctx.getSource(), getColor(ctx, "color"), getString(ctx, "message")))))); // You can deal with the arguments out here and pipe them into the command.
  7. }
  8.  
  9. public static int broadcast(ServerCommandSource source, Formatting formatting, String message) {
  10. final Text text = new LiteralText(message).formatted(formatting);
  11.  
  12. source.getMinecraftServer().getPlayerManager().broadcastChatMessage(text, MessageType.CHAT, source.getPlayer().getUuid());
  13. return Command.SINGLE_SUCCESS; // Success
  14. }

/giveMeDiamond

First the basic code where we register “giveMeDiamond” as a literal and then an executes block to tell the dispatcher which method to run.

  1. public static LiteralCommandNode register(CommandDispatcher<ServerCommandSource> dispatcher) { // You can also return a LiteralCommandNode for use with possible redirects
  2. return dispatcher.register(literal("giveMeDiamond")
  3. .executes(ctx -> giveDiamond(ctx)));
  4. }

Then since we only want to give to players, we check if the CommandSource is a player. But we can use getPlayer and do both at the same time and throw an error if the source is not a player.

  1. public static int giveDiamond(CommandContext<ServerCommandSource> ctx) throws CommandSyntaxException {
  2. final ServerCommandSource source = ctx.getSource();
  3.  
  4. final PlayerEntity self = source.getPlayer(); // If not a player than the command ends

Then we add to the player's inventory, with a check to see if the inventory is full:

  1. if(!player.inventory.insertStack(new ItemStack(Items.DIAMOND))){
  2. throw new SimpleCommandExceptionType(new TranslatableText("inventory.isfull")).create();
  3. }
  4.  
  5. return 1;
  6. }

Antioch

…lobbest thou thy Holy Hand Grenade of Antioch towards thy foe. who being naughty in My sight, shall snuff it.

Aside from the joke this command summons a primed TNT to a specified location or the location of the sender's cursor.

First create an entry into the CommandDispatcher that takes a literal of antioch with an optional argument of the location to summon the entity at.

  1. public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
  2. dispatcher.register(literal("antioch")
  3. .then(required("location", BlockPosArgumentType.blockPos()
  4. .executes(ctx -> antioch(ctx.getSource(), BlockPosArgument.getBlockPos(ctx, "location")))))
  5. .executes(ctx -> antioch(ctx.getSource(), null)));
  6. }

Then the creation and messages behind the joke.

  1. public static int antioch(ServerCommandSource source, BlockPos blockPos) throws CommandSyntaxException {
  2. if(blockPos == null) {
  3. // For the case of no inputted argument we calculate the cursor position of the player or throw an error if the nearest position is too far or is outside of the world.
  4. // This class is used as an example and actually doesn't exist yet.
  5. blockPos = LocationUtil.calculateCursorOrThrow(source, source.getRotation());
  6. }
  7.  
  8. final TntEntity tnt = new TntEntity(source.getWorld(), blockPos.getX(), blockPos.getY(), blockPos.getZ(), null);
  9. tnt.setFuse(3);
  10.  
  11. source.getMinecraftServer().getPlayerManager().broadcastChatMessage(new LiteralText("...lobbest thou thy Holy Hand Grenade of Antioch towards thy foe").formatting(Formatting.RED), false);
  12. source.getMinecraftServer().getPlayerManager().broadcastChatMessage(new LiteralText("who being naughty in My sight, shall snuff it.").formatting(Formatting.RED), false);
  13. source.getWorld().spawnEntity(tnt);
  14. return 1;
  15. }

More examples coming soon

Custom Argument Types

Brigadier has support for custom argument types and this section goes into showing how to create a simple argument type.

Warning: Custom arguments require client mod installation to be registered correctly! If you are making a server plugin, consider using existing argument type and a custom suggestions provider instead.

For this example we will create a UuidArgumentType.

First create a class which extends ArgumentType. Note that ArgumentType is a generic, so the generic will define what type the ArgumentType will return

  1. public class UuidArgumentType implements ArgumentType<UUID> {

ArgumentType requires you to implement the parse method, the type it returns will match with the Generic type.

@Override
public UUID parse(StringReader reader) throws CommandSyntaxException {

This method is where all of your parsing will occur. Either this method will return the object based on the arguments provided in the command line or throw a CommandSyntaxException and parsing will fail.

Next you will store the current position of the cursor, this is so you can substring out only the specific argument. This will always be at the beginning of where your argument appears on the command line.

  1. int argBeginning = reader.getCursor(); // The starting position of the cursor is at the beginning of the argument.
  2. if (!reader.canRead()) {
  3. reader.skip();
  4. }

Now we grab the entire argument. Depending on your argument type, you may have a different criteria or be similar to some arguments where detecting a { on the command line will require it to be closed. For a UUID we will just figure out what cursor position the argument ends at.

  1. while (reader.canRead() && reader.peek() != ' ') { // peek provides the character at the current cursor position.
  2. reader.skip(); // Tells the StringReader to move it's cursor to the next position.
  3. }

Then we will ask the StringReader what the current position of the cursor is an substring our argument out of the command line.

  1. String uuidString = reader.getString().substring(argBeginning, reader.getCursor());

Now finally we check if our argument is correct and parse the specific argument to our liking, and throwing an exception if the parsing fails.

  1. try {
  2. UUID uuid = UUID.fromString(uuidString); // Now our actual logic.
  3. return uuid; // And we return our type, in this case the parser will consider this argument to have parsed properly and then move on.
  4. } catch (Exception ex) {
  5. // UUIDs can throw an exception when made by a string, so we catch the exception and repackage it into a CommandSyntaxException type.
  6. // Create with context tells Brigadier to supply some context to tell the user where the command failed at.
  7. // Though normal create method could be used.
  8. throw new SimpleCommandExceptionType(new LiteralText(ex.getMessage())).createWithContext(reader);
  9. }

The ArgumentType is done, however your client will refuse the parse the argument and throw an error. This is because the server will tell the client what argument type the command node is. And the client will not parse any argument types it does not know how to parse. To fix this we need to register an ArgumentSerializer. Within your ModInitializer. For more complex argument types, you may need to create your own ArgumentSerializer.

  1. ArgumentTypes.register("mymod:uuid", UuidArgumentType.class, new ConstantArgumentSerializer(UuidArgumentType::uuid));
  2. // The argument should be what will create the ArgumentType.

And here is the whole ArgumentType:

UuidArgumentType.java
  1. import com.mojang.brigadier.StringReader;
  2. import com.mojang.brigadier.arguments.ArgumentType;
  3. import com.mojang.brigadier.context.CommandContext;
  4. import com.mojang.brigadier.exceptions.CommandSyntaxException;
  5. import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
  6. import net.minecraft.text.LiteralText;
  7. import net.minecraft.util.SystemUtil;
  8.  
  9. import java.util.ArrayList;
  10. import java.util.Collection;
  11. import java.util.UUID;
  12.  
  13. /**
  14.  * Represents an ArgumentType that will return a UUID.
  15.  */
  16. public class UuidArgumentType implements ArgumentType<UUID> {
  17. public static UuidArgumentType uuid() {
  18. return new UuidArgumentType();
  19. }
  20.  
  21. public static <S> UUID getUuid(String name, CommandContext<S> context) {
  22. // Note that you should assume the CommandSource wrapped inside of the CommandContext will always be a generic type.
  23. // If you need to access the ServerCommandSource make sure you verify the source is a server command source before casting.
  24. return context.getArgument(name, UUID.class);
  25. }
  26.  
  27. private static final Collection<String> EXAMPLES = SystemUtil.consume(new ArrayList<>(), list -> {
  28. list.add("765e5d33-c991-454f-8775-b6a7a394c097"); // i509VCB: Username The_1_gamers
  29. list.add("069a79f4-44e9-4726-a5be-fca90e38aaf5"); // Notch
  30. list.add("61699b2e-d327-4a01-9f1e-0ea8c3f06bc6"); // Dinnerbone
  31. });
  32.  
  33. @Override
  34. public UUID parse(StringReader reader) throws CommandSyntaxException {
  35. int argBeginning = reader.getCursor(); // The starting position of the cursor is at the beginning of the argument.
  36. if (!reader.canRead()) {
  37. reader.skip();
  38. }
  39.  
  40. // Now we check the contents of the argument till either we hit the end of the command line (When canRead becomes false)
  41. // Otherwise we go till reach reach a space, which signifies the next argument
  42. while (reader.canRead() && reader.peek() != ' ') { // peek provides the character at the current cursor position.
  43. reader.skip(); // Tells the StringReader to move it's cursor to the next position.
  44. }
  45.  
  46. // Now we substring the specific part we want to see using the starting cursor position and the ends where the next argument starts.
  47. String uuidString = reader.getString().substring(argBeginning, reader.getCursor());
  48. try {
  49. UUID uuid = UUID.fromString(uuidString); // Now our actual logic.
  50. return uuid; // And we return our type, in this case the parser will consider this argument to have parsed properly and then move on.
  51. } catch (Exception ex) {
  52. // UUIDs can throw an exception when made by a string, so we catch the exception and repackage it into a CommandSyntaxException type.
  53. // Create with context tells Brigadier to supply some context to tell the user where the command failed at.
  54. // Though normal create method could be used.
  55. throw new SimpleCommandExceptionType(new LiteralText(ex.getMessage())).createWithContext(reader);
  56. }
  57. }
  58.  
  59. @Override
  60. public Collection<String> getExamples() { // Brigadier has support to show examples for what the argument should look like, this should contain a Collection of only the argument this type will return. This is mainly used to calculate ambiguous commands which share the exact same
  61. return EXAMPLES;
  62. }
  63. }
zh_cn/tutorial/commands.1631854832.txt.gz · Last modified: 2021/09/17 05:00 by 127.0.0.1