zh_cn:tutorial:command_argument_types
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
zh_cn:tutorial:command_argument_types [2023/04/20 11:09] – 没有必要使用 SystemUtil solidblock | zh_cn:tutorial:command_argument_types [2024/04/15 07:24] (current) – solidblock | ||
---|---|---|---|
Line 3: | Line 3: | ||
Brigadier 支持自定义的参数类型,本页会展示如何创建一个简单的参数类型。 | Brigadier 支持自定义的参数类型,本页会展示如何创建一个简单的参数类型。 | ||
- | 注意:自定义的参数类型要求客户端也正确注册,如果是服务器的插件,请考虑使用已存在的参数类型和一个自定义的建议提供器。 | + | **注意:**自定义的参数类型要求客户端也正确注册,如果是服务器的插件,请考虑使用已存在的参数类型和一个自定义的建议提供器。 |
- | 本例中,我们将会创建一个 UuidArgumentType。 | + | ===== 解析 ===== |
+ | 本例中,我们将会创建一个 | ||
- | 首先创建一个类并继承 '' | + | 首先创建一个类并继承 '' |
<code java> | <code java> | ||
Line 21: | Line 22: | ||
所以的解析都发生在此方法中。此方法会根据命令行中提供的参数返回一个对象,或者抛出一个 '' | 所以的解析都发生在此方法中。此方法会根据命令行中提供的参数返回一个对象,或者抛出一个 '' | ||
- | 接下来你会存在当前指针(cursor)的位置,这样你可以截取子字符串,截出特定的参数。指针总是出现在参数在命令行中出现的开始的位置。 | + | 接下来你会存储当前指针(cursor)的位置,这样你可以截取子字符串,截出特定的参数。指针总是出现在参数在命令行中出现的开始的位置。 |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | int argBeginning = reader.getCursor(); | + | |
- | if (!reader.canRead()) { | + | if (!reader.canRead()) { |
- | reader.skip(); | + | reader.skip(); |
- | } | + | } |
</ | </ | ||
- | 现在抓取整个参数。你可能会有不同的标准,或像一些参数一样,检测到命令行中有 '' | + | 现在抓取整个参数。你可能会有不同的标准,或像一些参数一样,检测到命令行中有 '' |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | while (reader.canRead() && reader.peek() != ' ') { // “peek”提供了当前指针位置的字符。 | + | |
- | reader.skip(); | + | reader.skip(); |
- | } | + | } |
</ | </ | ||
接下来我们会从这个 '' | 接下来我们会从这个 '' | ||
- | <code java [enable_line_numbers=" | + | <code java [enable_line_numbers=" |
+ | | ||
+ | </ | ||
- | 最终,我们检票我们的参数是否正确,并解析我们的参数,如果解析失败则抛出异常。 | + | 最终,我们检查我们的参数是否正确,并解析我们的参数,如果解析失败则抛出异常。 |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | try { | + | public static final DynamicCommandExceptionType INVALID_UUID = new DynamicCommandExceptionType(o -> Text.literal(" |
- | UUID uuid = UUID.fromString(uuidString); | + | |
- | return uuid; // 我们返回我们的类型,在这个例子中,解析器会认为类型已经正确解析,并继续。 | + | @Override |
- | } catch (Exception | + | public UUID parse(StringReader reader) throws CommandSyntaxException { |
- | // UUID 在由字符串生成时,可能会抛出异常,因此我们捕获这个异常,并将其包装成 CommandSyntaxException 类型。 | + | // ... |
- | // 创建时会带有环境,告诉 Brigadier,根据这个环境来告诉玩家,命令在哪里解析失败了。 | + | |
- | // 尽管应该使用正常的 create 方法。 | + | UUID uuid = UUID.fromString(uuidString); |
- | throw new SimpleCommandExceptionType(Text.literal(ex.getMessage())).createWithContext(reader); | + | return uuid; // 我们返回我们的类型,在这个例子中,解析器会认为类型已经正确解析,并继续。 |
- | } | + | } catch (InvalidArgumentException |
+ | // UUID 在由字符串生成时,可能会抛出异常,因此我们捕获这个异常,并将其包装成 CommandSyntaxException 类型。 | ||
+ | // 创建时会带有环境,告诉 Brigadier,根据这个环境来告诉玩家,命令在哪里解析失败了。 | ||
+ | // 尽管也可以使用正常的 create 方法。 | ||
+ | | ||
+ | throw INVALID_UUID.createWithContext(reader, | ||
+ | } | ||
+ | // ... | ||
+ | | ||
</ | </ | ||
+ | |||
+ | ===== 定义参数示例 ===== | ||
+ | 有时候参数需要有一些示例,通常存储在作为静态常量字段的不可变集合中。示例是用于检测二义性的。 | ||
+ | <code java> | ||
+ | private static final Collection< | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ); | ||
+ | |||
+ | @Override | ||
+ | public Collection< | ||
+ | // Brigadier 支持显示示例,表示这个命令应该像是什么样子,这应该包含一个集合, | ||
+ | // 集合的内容是此类型能够返回的参数。 | ||
+ | // 这主要用于检测二义性,即一种参数可能会被作为另一种参数解析。 | ||
+ | return EXAMPLES; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== 注册参数类型 ===== | ||
'' | '' | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
- | ArgumentTypeRegistry.registerArgumentType(new Identifier(" | + | ArgumentTypeRegistry.registerArgumentType( |
- | // 这个参数会创建 ArgumentType。 | + | |
+ | | ||
+ | // 参数会创建 ArgumentType。 | ||
</ | </ | ||
- | 参数类型的完整代码会是这样: | + | ===== 完整参数类型示例 ===== |
<file java UuidArgumentType.java [enable_line_numbers=" | <file java UuidArgumentType.java [enable_line_numbers=" | ||
Line 88: | Line 121: | ||
public static <S> UUID getUuid(String name, CommandContext< | public static <S> UUID getUuid(String name, CommandContext< | ||
// 注意你应该假设 CommandContext 中包含的 CommandSource 是一个泛型类型。 | // 注意你应该假设 CommandContext 中包含的 CommandSource 是一个泛型类型。 | ||
- | // 如果你需要访问 ServerCommandSource,确保你在强转之前验证了源。 | + | // 如果你需要访问 ServerCommandSource,确保你在强转之前验证了命令源。 |
return context.getArgument(name, | return context.getArgument(name, | ||
} | } | ||
Line 129: | Line 162: | ||
// Brigadier 支持显示示例,表示这个命令应该像是什么样子,这应该包含一个集合, | // Brigadier 支持显示示例,表示这个命令应该像是什么样子,这应该包含一个集合, | ||
// 集合的内容是此类型能够返回的参数。 | // 集合的内容是此类型能够返回的参数。 | ||
- | // 这主要用于计算共享了同一个的难懂的命令。 | + | // 这主要用于检测二义性,即一种参数可能会被作为另一种参数解析。 |
return EXAMPLES; | return EXAMPLES; | ||
} | } | ||
} | } | ||
</ | </ | ||
+ | |||
+ | ===== 指定建议 ===== | ||
+ | |||
+ | 很多参数类型都支持建议。不像在注册过程中定义的自定义建议,当没有自定义建议时,始终会应用这些建议。建议是在客户端中计算的。例如,'' | ||
+ | |||
+ | 关于如何提供建议,请参见[[command suggestions|命令建议]]。在这个例子中,我们会建议服务器中的所有玩家的 UUID,以及当前客户端的准星目标实体。 | ||
+ | |||
+ | <code java> | ||
+ | @Override | ||
+ | public <S> CompletableFuture< | ||
+ | final String remaining = builder.getRemaining(); | ||
+ | if (context.getSource() instanceof FabricClientCommandSource clientCommandSource) { | ||
+ | if (clientCommandSource.getClient().crosshairTarget instanceof EntityHitResult entityHitResult) { | ||
+ | final UUID uuid = entityHitResult.getEntity().getUuid(); | ||
+ | if (CommandSource.shouldSuggest(remaining, | ||
+ | builder.suggest(uuid.toString()); | ||
+ | } | ||
+ | } | ||
+ | return CommandSource.suggestMatching(Collections2.transform(clientCommandSource.getClient().getNetworkHandler().getPlayerUuids(), | ||
+ | } | ||
+ | |||
+ | if (context.getSource() instanceof ServerCommandSource source) { | ||
+ | return CommandSource.suggestMatching(Lists.transform(source.getServer().getPlayerManager().getPlayerList(), | ||
+ | } | ||
+ | | ||
+ | return builder.buildFuture(); | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== 自定义参数序列化 ====== | ||
+ | 在上面的例子中,UUID 是简单的。如果参数是复杂的,如何让它能够正确地被客户端理解呢?这就是 '' | ||
+ | |||
+ | '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | 在这个例子中,我们创建一个新的参数类型,包含一个布尔值和一个整数。这个例子中,需要 '' | ||
+ | |||
+ | <code java> | ||
+ | public record ExampleArgumentType(CommandRegistryAccess commandRegistryAccess, | ||
+ | @Override | ||
+ | public String parse(StringReader reader) throws CommandSyntaxException { | ||
+ | ... | ||
+ | } | ||
+ | | ||
+ | public static class Serializer implements ArgumentSerializer< | ||
+ | @Override | ||
+ | public void writePacket(Properties properties, PacketByteBuf buf) { | ||
+ | // 将基本的属性写到数据包中,需要确保所有的参数都以适当方式存储在数据包中。 | ||
+ | buf.writeBoolean(properties.booleanValue).writeInt(properties.intValue); | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public Properties fromPacket(PacketByteBuf buf) { | ||
+ | // 从数据包中读取信息。返回的是 '' | ||
+ | return new Properties(buf.readBoolean(), | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void writeJson(Properties properties, JsonObject json) { | ||
+ | // 以 JSON 的形式呈现参数类型。 | ||
+ | json.addProperty(" | ||
+ | json.addProperty(" | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public Properties getArgumentTypeProperties(ExampleArgumentType argumentType) { | ||
+ | return new Properties(argumentType.booleanValue, | ||
+ | } | ||
+ | |||
+ | public record Properties(boolean booleanValue, | ||
+ | @Override | ||
+ | public ExampleArgumentType createType(CommandRegistryAccess commandRegistryAccess) { | ||
+ | // 只有在此方法中会提供 '' | ||
+ | // 会创建需要 '' | ||
+ | return new ExampleArgumentType(commandRegistryAccess, | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public ArgumentSerializer< | ||
+ | // 不要在这里创建新的 '' | ||
+ | return Serializer.this; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 现在你可以像这样注册: | ||
+ | <code java> | ||
+ | ArgumentTypeRegistry.registerArgumentType(new Identifier(" | ||
+ | </ | ||
+ | |||
+ | ==== 另一种可能的定义序列化的方式 ==== | ||
+ | |||
+ | 如果参数不需要 '' | ||
+ | |||
+ | <code java> | ||
+ | public record ExampleArgumentType(boolean booleanValue, | ||
+ | @Override | ||
+ | public String parse(StringReader reader) throws CommandSyntaxException { | ||
+ | ... | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public ExampleArgumentType createType(CommandRegistryAccess commandRegistryAccess) { | ||
+ | return this; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public ArgumentSerializer< | ||
+ | // 这里总是返回同一个对象,以避免无法序列化。 | ||
+ | return Serializer.INSTANCE; | ||
+ | } | ||
+ | |||
+ | public static class Serializer implements ArgumentSerializer< | ||
+ | public static final Serializer INSTANCE = new Serializer(); | ||
+ | private Serializer() {} | ||
+ | | ||
+ | @Override | ||
+ | public void writePacket(ExampleArgumentType properties, PacketByteBuf buf) { | ||
+ | buf.writeBoolean(properties.booleanValue).writeInt(properties.intValue); | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public ExampleArgumentType fromPacket(PacketByteBuf buf) { | ||
+ | return new ExampleArgumentType(buf.readBoolean(), | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public void writeJson(ExampleArgumentType properties, JsonObject json) { | ||
+ | json.addProperty(" | ||
+ | json.addProperty(" | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | public ExampleArgumentType getArgumentTypeProperties(ExampleArgumentType argumentType) { | ||
+ | return argumentType; | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | 现在你可以像这样注册: | ||
+ | <code java> | ||
+ | ArgumentTypeRegistry.registerArgumentType(new Identifier(" | ||
+ | </ | ||
zh_cn/tutorial/command_argument_types.txt · Last modified: 2024/04/15 07:24 by solidblock