跳到主要内容

参数和字面量

备注

命令树文档中,我们已经了解了 Brigadier 命令的结构以及如何构建命令树。 如果你还没有阅读完那部分内容,我们强烈建议在阅读参数和字面量之前先完成那部分的阅读。

简介

ArgumentBuilder<CommandSourceStack, ?> 的每个 .then(...) 方法都接收另一个 ArgumentBuilder<CommandSourceStack, ?> 对象。这个抽象的 ArgumentBuilder 有两个实现:RequiredArgumentBuilderLiteralArgumentBuilder。当在 Paper 中使用 Brigadier 时,我们通过运行 Commands.literal(String) 创建 LiteralArgumentBuilder,或通过 Commands.argument(String, ArgumentType<T>) 创建 RequiredArgumentBuilder

关于它们的区别,你可以这样理解:

  • 参数是用户输入的变量。它是半不可预测的,但总是会返回其所支持对象的有效条目。
  • 字面量是用户输入的非变量。它主要用作定义可预测输入的方式,因为每个字面量都是我们命令树上的一个新分支。

字面量

在代码中,字面量通常无法被访问。但是,由于我们命令树的特性,我们总是可以知道当前在哪个字面量分支上:

Commands.literal("plant")
.then(Commands.literal("tree")
.executes(ctx -> {
/* 这里我们在 /plant tree 上 */
})
)
.then(Commands.literal("grass")
.executes(ctx -> {
/* 这里我们在 /plant grass 上 */
}));
提示

你可能注意到了 executes 方法的使用。这个方法为我们的分支声明逻辑。如果一个分支没有定义 executes 方法,它将无法执行。 关于执行逻辑的更多信息,点击这里

参数

参数稍微复杂一些。它们同样在树中定义新分支,但不是直接可预测的。每个参数都是使用 Commands.argument(String, ArgumentType<T>) 创建的。 该方法返回一个 RequiredArgumentBuilder。T 类型参数声明参数的返回类型,你可以在 executes 方法中使用它。这意味着 如果你输入一个 ArgumentType<Integer>,你可以将该参数的值作为整数检索,无需手动解析!以下是一些我们可以用于参数的内置原始参数类型:

名称返回值可能的输入描述
BoolArgumentType.bool()Booleantrue/false仅允许布尔值
IntegerArgumentType.integer()Integer253, -123, 0任何有效整数
LongArgumentType.longArg()Long25418263123783任何有效长整数
FloatArgumentType.floatArg()Float253.2, -25.0任何有效浮点数
DoubleArgumentType.doubleArg()Double4123.242, -1.1任何有效双精度浮点数
StringArgumentType.word()Stringletters-and+1234567单个词。只能包含字母、数字和这些字符:+-_.
StringArgumentType.string()String"with spaces"单个词,或如果加引号,任何带空格的有效字符串
StringArgumentType.greedyString()Stringunquoted spaces字面输入的内容。可以包含任何字符。必须是最后一个参数

布尔参数类型和参数解析

布尔参数用于获取布尔值。一个使用示例可能是一个 /serverflight 命令,它允许通过 /serverflight true/serverflight false 来启用和禁用服务器飞行:

ServerFlightCommand.java
Commands.literal("serverflight")
.then(Commands.argument("allow", BoolArgumentType.bool())
.executes(ctx -> {
boolean allowed = ctx.getArgument("allow", boolean.class);
/* 切换服务器飞行 */
})
);

在这里,你可以看到如何在代码中访问参数。Commands.argument(String, ArgumentType) 方法的第一个参数接收节点名称。字面量不需要这个, 因为它们的名称与值相同。但在这里我们需要一种方式来访问参数。executes-lambda 的参数有一个叫做 T getArgument(String, Class<T>) 的方法。 第一个参数是我们想要检索的方法名称。第二个参数是参数的返回值。由于我们使用的是布尔参数,我们输入 boolean.class 并以此方式检索参数值。

数字参数

所有数字参数(如 IntegerArgumentType.integer())都有三个重载:

重载描述
IntegerArgumentType.integer()介于 Integer.MIN_VALUE 和 Integer.MAX_VALUE 之间的任何值
IntegerArgumentType.integer(int min)介于 min 和 Integer.MAX_VALUE 之间的任何值
IntegerArgumentType.integer(int min, int max)介于 min 和 max 之间的任何值

这对于过滤掉太高或太低的输入特别有用。例如,我们可以定义一个 /flyspeed 命令。由于 Player#setFlySpeed(float value) 方法 只接受 -1 到 1 之间的浮点数,其中 -1 是反方向,限制值在 0 到 1 之间用于正常范围内的非负速度值是有意义的。 这可以通过以下命令树实现:

FlightSpeedCommand.java
Commands.literal("flyspeed")
.then(Commands.argument("speed", FloatArgumentType.floatArg(0, 1.0f))
.executes(ctx -> {
float speed = ctx.getArgument("speed", float.class);
/* 设置玩家的飞行速度 */
return Command.SINGLE_SUCCESS;
})
);
提示

一些参数可以有特殊的检索方式。最值得注意的是,所有 Brigadier 提供的参数(本页面提到的那些) 都有自己的参数值解析器。对于浮点参数,它看起来是这样的:

float speed = FloatArgumentType.getFloat(ctx, "speed");

通常使用 ctx.getArgument 还是 FloatArgumentType.getFloat 并不重要,因为它们使用相同的逻辑,但在未来的文档中, 原始值可能会使用它们自己的解析器来检索。

这些 Brigadier 原生参数的解析器都存在。它们都接受 (CommandContext<?> context, String name) 作为方法参数:

  • BoolArgumentType.getBool
  • IntegerArgumentType.getInteger
  • LongArgumentType.getLong
  • FloatArgumentType.getFloat
  • DoubleArgumentType.getDouble
  • StringArgumentType.getString

现在,如果我们输入一个在 0 到 1 之间的有效浮点数,命令将正确执行:

但如果我们输入一个太小或太大的浮点数,它会在客户端抛出错误:



这是原生参数的主要优势:客户端本身对参数进行简单的错误检查,这使得运行命令时的用户体验更好,因为他们无需将命令发送到服务器就能看到无效输入。

字符串参数

有三种字符串参数:wordstringgreedyString

word 字符串参数是这些中最简单的。它只接受由字母数字字符和这些特殊字符组成的单个词:+-_.

  • .this_is_valid_input.
  • this is invalid input
  • "also_invalid"
  • -10_numbers_are_valid
  • @_@

string 参数稍微复杂一些。如果不加引号,它遵循与 word 参数相同的规则。只允许字母数字字符和上述特殊字符。 但如果你将字符串放在引号中,你可以输入任何你想要的 Unicode 字符组合。引号 " 可以使用反斜杠 \ 转义。

  • this_is-valid-input
  • "\"quotes\""
  • this is invalid input
  • "this is valid input again"
  • "also_valid"
  • "紙の神"

greedyString 参数是唯一不执行任何解析的参数。由于其"贪婪"特性,它不允许在其声明后有任何参数。这也意味着, 任何输入都是完全有效的,并且不需要引号。实际上,引号被视为字面字符。

  • this_is_valid_input
  • this is valid as well input
  • "this is valid input again"
  • also_valid
  • 紙の神

这里你可以看到这些参数的实际效果:

进一步参考

Minecraft 参数

除了这些内置的 Brigadier 参数外,Paper 还定义了无数自定义参数。这些可以通过 ArgumentTypes 类在静态上下文中访问。你 可以在这里阅读更多相关信息。

自定义参数

有时你想定义自己的自定义参数。为此,你可以实现 CustomArgumentType<T, N> 接口。