参数和字面量
在命令树文档中,我们已经了解了 Brigadier 命令的结构以及如何构建命令树。 如果你还没有阅读完那部分内容,我们强烈建议在阅读参数和字面量之前先完成那部分的阅读。
简介
ArgumentBuilder<CommandSourceStack, ?> 的每个 .then(...) 方法都接收另一个 ArgumentBuilder<CommandSourceStack, ?> 对象。这个抽象的 ArgumentBuilder
有两个实现:RequiredArgumentBuilder 和 LiteralArgumentBuilder。当在 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() | Boolean | true/false | 仅允许布尔值 |
| IntegerArgumentType.integer() | Integer | 253, -123, 0 | 任何有效整数 |
| LongArgumentType.longArg() | Long | 25418263123783 | 任何有效长整数 |
| FloatArgumentType.floatArg() | Float | 253.2, -25.0 | 任何有效浮点数 |
| DoubleArgumentType.doubleArg() | Double | 4123.242, -1.1 | 任何有效双精度浮点数 |
| StringArgumentType.word() | String | letters-and+1234567 | 单个词。只能包含字母、数字和这些字符:+、-、_ 和 . |
| StringArgumentType.string() | String | "with spaces" | 单个词,或如果加引号,任何带空格的有效字符串 |
| StringArgumentType.greedyString() | String | unquoted spaces | 字面输入的内容。可以包含任何字符。必须是最后一个参数 |
布尔参数类型和参数解析
布尔参数用于获取布尔值。一个使用示例可能是一个 /serverflight 命令,它允许通过 /serverflight true 和 /serverflight false 来启用和禁用服务器飞行:
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 之间用于正常范围内的非负速度值是有意义的。
这可以通过以下命令树实现:
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.getBoolIntegerArgumentType.getIntegerLongArgumentType.getLongFloatArgumentType.getFloatDoubleArgumentType.getDoubleStringArgumentType.getString
现在,如果我们输入一个在 0 到 1 之间的有效浮点数,命令将正确执行:
但如果我们输入一个太小或太大的浮点数,它会在客户端抛出错误:
这是原生参数的主要优势:客户端本身对参数进行简单的错误检查,这使得运行命令时的用户体验更好,因为他们无需将命令发送到服务器就能看到无效输入。
字符串参数
有三种字符串参数:word、string 和 greedyString。
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> 接口。