zh_cn:tutorial:commands
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionLast revisionBoth sides next revision | ||
zh_cn:tutorial:commands [2022/09/04 06:53] – solidblock | zh_cn:tutorial:commands [2023/11/19 02:01] – [子命令] solidblock | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | |||
条款:本文中的代码适用于“Creative Commons Zero v1.0 Universial”条款,允许您将文中的代码示例用于自己的模组中。 | 条款:本文中的代码适用于“Creative Commons Zero v1.0 Universial”条款,允许您将文中的代码示例用于自己的模组中。 | ||
Line 6: | Line 5: | ||
====== 创建命令 ====== | ====== 创建命令 ====== | ||
- | 创建命令允许模组开发者添加可以使用命令实现的功能。本教程将会教你如何注册命令,以及Brigadier的基本命令结构。 | + | 创建命令允许模组开发者添加可以使用命令实现的功能。本教程将会教你如何注册命令,以及 Brigadier 的基本命令结构。 |
- | + | ||
- | 注意:本文的所有代码都是用于 1.19.2 的。对于旧的版本,一些版本和映射可能不同。 | + | |
===== Brigadier 是什么? ===== | ===== Brigadier 是什么? ===== | ||
Line 16: | Line 13: | ||
这是 Brigadier 的源代码:https:// | 这是 Brigadier 的源代码:https:// | ||
- | ===== '' | + | ===== Command 接口 ===== |
- | 在 Minecraft 中,'' | + | 在 Minecraft 中,'' |
'' | '' | ||
Line 30: | Line 27: | ||
</ | </ | ||
- | 在原版 Minecraft 中,命令通常用作方法引用,例如原版的 '' | + | 这个整数相当于命令的结果。在 Minecraft 中。通常来说,负值表示命令执行失败,什么也不做,'' |
- | + | ||
- | 这个整数相当于命令的结果。在 Minecraft 中,这个整数对应从命令方块中输出到红石比较器的红石能量,或者传入命令方块朝向的连锁命令方块。通常来说,负值表示命令执行失败,什么也不做,'' | + | |
==== ServerCommandSource 是做什么的? ==== | ==== ServerCommandSource 是做什么的? ==== | ||
- | '' | + | '' |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
Line 42: | Line 37: | ||
final ServerCommandSource source = ctx.getSource(); | final ServerCommandSource source = ctx.getSource(); | ||
- | // 未经检查,如果是由控制台发送的,则会使 null。 | + | // 未经检查,如果是由控制台或命令方块执行的,则会是 null。 |
- | final Entity sender = source.getEntity(); | + | final @Nullable |
- | // 如果命令源不是实体,就会中止命令。 | + | // 如果命令执行者不是实体,就会抛出错误。 |
// 这个的结果可能包含玩家,并且会发送反馈,告诉命令的发送者必须有一个实体。 | // 这个的结果可能包含玩家,并且会发送反馈,告诉命令的发送者必须有一个实体。 | ||
// 这个方法会要求你的方法能够抛出 CommandSyntaxException。 | // 这个方法会要求你的方法能够抛出 CommandSyntaxException。 | ||
// ServerCommandSource 中的 entity 选项可以返回一个 CommandBlock 实体、生物实体或者玩家。 | // ServerCommandSource 中的 entity 选项可以返回一个 CommandBlock 实体、生物实体或者玩家。 | ||
- | final Entity sender2 = source.getEntityOrThrow(); | + | final @NotNull |
+ | |||
+ | // 如果命令执行者不是玩家,则为 null。 | ||
+ | final @Nullable ServerPlayerEntity player = source.getPlayer(): | ||
- | // 如果命令源不是玩家,中止命令,并向命令的发送者发送反馈,告诉他必须有一个玩家。这个方法会要求你的方法能够抛出 CommandSyntaxException。 | + | // 如果命令执行者不是玩家,抛出错误,并向命令的发送者发送反馈,告诉他必须有一个玩家。这个方法会要求你的方法能够抛出 CommandSyntaxException。 |
- | final ServerPlayerEntity player = source.getPlayer(); | + | final @NotNull |
- | // 获取命令发送时发送者的坐标,以 | + | // 获取命令发送时发送者的坐标,以 |
- | source.getPosition(); | + | final Vec3d position = source.getPosition(); |
// 获取命令发送者所在的世界。控制台的世界就是默认重生的世界。 | // 获取命令发送者所在的世界。控制台的世界就是默认重生的世界。 | ||
- | source.getWorld(); | + | final ServerWorld world = source.getWorld(); |
// 获取发送者的旋转角度,以 Vec2f 的形式。 | // 获取发送者的旋转角度,以 Vec2f 的形式。 | ||
- | source.getRotation(); | + | final Vec2f rotation = source.getRotation(); |
// 访问命令运行时的 MinecraftServer 实例。 | // 访问命令运行时的 MinecraftServer 实例。 | ||
- | source.getServer(); | + | final MinecraftServer server = source.getServer(); |
// 命令源的名称,可以是实体、玩家、命令方块的名称,命令方块可以在放置之前命令,若为控制台则为“Console” | // 命令源的名称,可以是实体、玩家、命令方块的名称,命令方块可以在放置之前命令,若为控制台则为“Console” | ||
- | source.getName(); | + | final String name = source.getName(); |
// 如果命令源拥有特定的权限等级,则返回 true,这基于发送者的管理员级别。(在内置服务器上,玩家必须启用了作弊才能执行这些命令。) | // 如果命令源拥有特定的权限等级,则返回 true,这基于发送者的管理员级别。(在内置服务器上,玩家必须启用了作弊才能执行这些命令。) | ||
- | source.hasPermissionLevel(int level); | + | final boolean b = source.hasPermissionLevel(int level); |
</ | </ | ||
Line 77: | Line 75: | ||
命令可以通过 Fabric API 的 '' | 命令可以通过 Fabric API 的 '' | ||
- | 这个时间必须在你的模组的初始化器中注册。这个回调有三个参数。'' | + | 这个事件必须在你的模组的初始化器中注册。这个回调有三个参数。'' |
为简化代码,建议静态导入 '' | 为简化代码,建议静态导入 '' | ||
Line 92: | Line 90: | ||
CommandRegistrationCallback.EVENT.register((dispatcher, | CommandRegistrationCallback.EVENT.register((dispatcher, | ||
.executes(context -> { | .executes(context -> { | ||
- | // 对于 1.19 以下的版本,把“Text.literal”替换为“new LiteralText”。 | + | // 对于 1.19 之前的版本,把“Text.literal”替换为“new LiteralText”。 |
- | context.getSource().sendMessage(Text.literal(" | + | // 对于 1.20 之前的版本,请移除“() ->”。 |
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
return 1; | return 1; | ||
Line 101: | Line 100: | ||
</ | </ | ||
- | '' | + | **请确保你导入了正确的静态方法。**方法 '' |
+ | |||
+ | 在 '' | ||
+ | |||
+ | 如果命令失败,可以不必调用 '' | ||
要执行命令,必须输入 ''/ | 要执行命令,必须输入 ''/ | ||
+ | ===== 注册环境 ===== | ||
如有需要,你可以确保命令仅在一些特定情形下注册,例如仅在专用服务器上: | 如有需要,你可以确保命令仅在一些特定情形下注册,例如仅在专用服务器上: | ||
- | <code java [enable_line_numbers=" | + | <yarncode |
public class ExampleCommandMod implements ModInitializer { | public class ExampleCommandMod implements ModInitializer { | ||
@Override | @Override | ||
public void onInitialize() { | public void onInitialize() { | ||
CommandRegistrationCallback.EVENT.register((dispatcher, | CommandRegistrationCallback.EVENT.register((dispatcher, | ||
- | if (environment.dedicated) { | + | if (environment.field_25423) { |
...; | ...; | ||
} | } | ||
Line 118: | Line 122: | ||
} | } | ||
} | } | ||
- | </code> | + | </yarncode> |
===== 静态导入 ===== | ===== 静态导入 ===== | ||
在上面的例子中,使用了静态导入以简化代码。对于字面值,语句会简化为 '' | 在上面的例子中,使用了静态导入以简化代码。对于字面值,语句会简化为 '' | ||
- | Below is an example of some static imports: | + | 以下是一些静态导入的例子。 |
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
// getString(ctx, | // getString(ctx, | ||
Line 137: | Line 141: | ||
</ | </ | ||
- | 注意:请确保使用了 '' | + | 注意:请确保使用了 '' |
Brigadier 的默认参数位于 '' | Brigadier 的默认参数位于 '' | ||
Line 143: | Line 147: | ||
Minecraft 的参数位于 '' | Minecraft 的参数位于 '' | ||
- | ===== 条件 ===== | + | ===== 添加条件 ===== |
有时你希望命令只有管理员(OP)可以执行,这时就要用到 '' | 有时你希望命令只有管理员(OP)可以执行,这时就要用到 '' | ||
Line 151: | Line 155: | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
dispatcher.register(literal(" | dispatcher.register(literal(" | ||
- | .requires(source -> source.hasPermissionLevel(4)) | + | |
.executes(ctx -> { | .executes(ctx -> { | ||
- | ctx.getSource().sendFeedback(Text.literal(" | + | ctx.getSource().sendFeedback(() -> Text.literal(" |
return 1; | return 1; | ||
}); | }); | ||
</ | </ | ||
- | 此时命令只会在命令源为 | + | 此时命令只会在命令源为 |
+ | |||
+ | 要创建只有 4 级管理员(不包括命令方块)可以执行的命令,请使用 '' | ||
===== 参数 ===== | ===== 参数 ===== | ||
- | Brigadier 中的参数会解析任何输入的参数,并检查错误。Minecraft 创建了一些特殊的参数类型以用于自己使用,例如 '' | + | 大多数命令都使用了参数。一些参数是可选的,也就是说如果你不提供此参数,命令仍能运行。一个节点可以有多个参数类型,但是注意有可能出现二义性,这是需要避免的。 |
- | **TODO:**详细介绍如何使用参数 | + | 在这个例子中。我们添加一个整数参数。并计算整数的平方。 |
- | ===== 子命令 ===== | + | <code java> |
+ | dispatcher.register(literal(" | ||
+ | .then(argument(" | ||
+ | .executes(context -> { | ||
+ | final int value = IntegerArgumentType.getInteger(context, | ||
+ | final int result | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return result; | ||
+ | }))); | ||
+ | </ | ||
- | 要添加子命令,你需要先照常注册注册第一个字面节点。 | + | 在这个例子中,在 ''/ |
- | <code java [enable_line_numbers="true"]> | + | 注意:为了简便,'' |
- | dispatcher.register(literal(" | + | |
+ | 然后我们添加可选的第二个参数: | ||
+ | <code java> | ||
+ | dispatcher.register(literal(" | ||
+ | .then(argument(" | ||
+ | .executes(context -> { | ||
+ | final int value = IntegerArgumentType.getInteger(context, | ||
+ | final int result = value * value; | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | | ||
+ | }) | ||
+ | | ||
+ | .executes(context -> { | ||
+ | final int value = IntegerArgumentType.getInteger(context, | ||
+ | final int value2 = IntegerArgumentType.getInteger(context, | ||
+ | final int result = value * value2; | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return result; | ||
+ | })))); | ||
</ | </ | ||
- | 为拥有子命令,需要把下一个节点追加到已经存在的节点后面,这是利用 '' | + | 现在你可以输入一个或者两个整数了。如果给一个整数,会计算这个整数的平方。如果提供两个整数,会计算这两个整数的积。你可能发现,两次指定类似的执行内容有些不太必要。因此,我们可以创建一个在两个执行中都使用的方法。 |
- | 如下所示,创建命令 '' | + | <code java> |
+ | public class ExampleMod implements ModInitializer { | ||
+ | @Override | ||
+ | public void onInitialize() { | ||
+ | CommandRegistrationCallback.EVENT.register((dispatcher, | ||
+ | .then(argument(" | ||
+ | .executes(context -> executeMultiply(IntegerArgumentType.getInteger(context, | ||
+ | .then(argument(" | ||
+ | .executes(context -> executeMultiply(IntegerArgumentType.getInteger(context, | ||
+ | } | ||
- | <code java [enable_line_numbers=" | + | private static int executeMultiply(int value, int value2, CommandContext< |
- | dispatcher.register(literal(" | + | final int result = value * value2; |
- | | + | context.getSource().sendFeedback(() -> Text.literal(" |
- | ); | + | return result; |
+ | } | ||
+ | } | ||
</ | </ | ||
- | 建议给命令添加节点时缩进你的代码,通常来说缩进对应了命令树中有多少节点的深度,每一次换行也可以看出添加了一个节点。本教程后面会展示格式化树状命令的几种可选样式。 | + | ===== 子命令 |
- | **那就开始尝试运行命令** | + | 要添加子命令,你需要先照常注册第一个字面节点。 |
- | 通常如果你在游戏内输入了 '' | + | < |
+ | dispatcher.register(literal(" | ||
+ | </code> | ||
+ | |||
+ | 为拥有子命令,需要把下一个节点追加到已经存在的节点后面。 | ||
+ | |||
+ | 如下所示,创建命令 '' | ||
<code java [enable_line_numbers=" | <code java [enable_line_numbers=" | ||
Line 194: | Line 244: | ||
.executes(context -> { | .executes(context -> { | ||
// 对于 1.19 以下的版本,使用 '' | // 对于 1.19 以下的版本,使用 '' | ||
- | context.getSource().sendMessage(Text.literal(" | + | |
+ | | ||
return 1; | return 1; | ||
Line 202: | Line 253: | ||
</ | </ | ||
+ | 建议给命令添加节点时缩进你的代码,通常来说缩进对应了命令树中有多少节点的深度,每一次换行也可以看出添加了一个节点。本教程后面会展示格式化树状命令的几种可选样式。 | ||
+ | |||
+ | 类似于参数,子命令节点也可以设置为可选的。在下面这个例子中,''/ | ||
+ | <code java [enable_line_numbers=" | ||
+ | dispatcher.register(literal(" | ||
+ | .executes(context -> { | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return 1; | ||
+ | }) | ||
+ | .then(literal(" | ||
+ | .executes(context -> { | ||
+ | context.getSource().sendFeedback(() -> Text.literal(" | ||
+ | return 1; | ||
+ | }) | ||
+ | ) | ||
+ | ); | ||
+ | </ | ||
====== 高级概念 ====== | ====== 高级概念 ====== | ||
Line 214: | Line 282: | ||
| [[command_examples|Examples]] | 一些示例命令 | | | [[command_examples|Examples]] | 一些示例命令 | | ||
- | ====== | + | ====== |
- | ===== Why does my command not compile | + | ===== 代码为什么不编译 |
+ | 此问题可能有一些常见的原因。 | ||
- | There are two immediate possibilities for why this could occur. | + | * **捕获或抛出 CommandSyntaxException:**'' |
+ | * **泛型问题:**你可能遇到了泛型问题。如果你在注册服务器命令(大多数情况都是如此),确保你在静态导入中使用 '' | ||
+ | * **检查 '' | ||
+ | * **'' | ||
- | ==== Catch or throw a CommandSyntaxException | + | ===== 可以注册客户端命令吗? ===== |
- | The solution to this issue is to make the '' | + | Fabric 有个 ClientCommandManager,可以注册客户端命令。代码应该仅存在于客户端的代码中。例子: |
- | ==== Issues with generics ==== | + | <code java> |
- | + | | |
- | You may have an issue with generic types once in a while. Verify you are using '' | + | |
- | + | context.getSource().sendFeedback(Text.literal(" | |
- | ===== Can I register client side commands? ===== | + | |
- | + | } | |
- | Fabric has a ClientCommandManager that can be used to register client side commands. | + | ))); |
- | + | </ | |
- | ===== Dark Arts ===== | + | |
- | A few things we don't recommend, but are possible. | + | 如果你需要在客户端命令执行劳教中打开屏幕,不要直接调用 |
- | ==== Can I register commands in runtime? | + | ===== 可以运行时注册命令吗? ===== |
- | You can do this but it is not recommended. You would get the '' | + | 可以这么做但是不推荐,你可以从服务器中获取 |
- | After that you need to send the command tree to every player again using '' | + | 然后你需要通过 |
- | ==== Can I unregister commands in runtime? | + | ===== 可以在运行时取消注册命令吗? ===== |
- | 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 | + | 可以这么做,但是这更不稳定,并且可能造成未预料的副作用。为简化事情,你需要在 |
zh_cn/tutorial/commands.txt · Last modified: 2023/11/19 02:02 by solidblock