zh_cn:tutorial:commands
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
zh_cn:tutorial:commands [2022/09/04 07:02] – [''Command'' 接口] solidblock | zh_cn:tutorial:commands [2023/11/19 02:02] (current) – [可以注册客户端命令吗?] 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 18: | Line 15: | ||
===== Command 接口 ===== | ===== 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 216: | Line 284: | ||
====== 常见问题 ====== | ====== 常见问题 ====== | ||
- | ===== 命令为什么不编译 ===== | + | ===== 代码为什么不编译 ===== |
+ | 此问题可能有一些常见的原因。 | ||
- | 有两种可能的原因。 | + | * **捕获或抛出 CommandSyntaxException:**'' |
- | + | * **泛型问题:**你可能遇到了泛型问题。如果你在注册服务器命令(大多数情况都是如此),确保你在静态导入中使用 '' | |
- | ==== 捕获或抛出 CommandSyntaxException | + | * **检查 '' |
- | + | * **'' | |
- | 这个问题的解决方法就是让 | + | |
- | + | ||
- | ==== 泛型问题 | + | |
- | + | ||
- | 你可能有时出现了泛型问题,请检查你的静态导入里面用的是不是 | + | |
===== 可以注册客户端命令吗? ===== | ===== 可以注册客户端命令吗? ===== | ||
- | Fabric 有个 ClientCommandManager,可以注册客户端命令。 | + | Fabric |
- | ===== 黑科技 ===== | + | <code java> |
+ | ClientCommandRegistrationCallback.EVENT.register((dispatcher, | ||
+ | .executes(context -> { | ||
+ | context.getSource().sendFeedback(Text.literal(" | ||
+ | return 1; | ||
+ | } | ||
+ | ))); | ||
+ | </ | ||
- | 一些可行但是不推荐的做法: | + | 如果你需要在客户端命令执行过程中打开屏幕,不要直接调用 '' |
- | ==== 可以运行时注册命令吗? ==== | + | ===== 可以运行时注册命令吗? |
可以这么做但是不推荐,你可以从服务器中获取 '' | 可以这么做但是不推荐,你可以从服务器中获取 '' | ||
Line 242: | Line 313: | ||
然后你需要通过 '' | 然后你需要通过 '' | ||
- | ==== 可以在运行时取消注册命令吗? ==== | + | ===== 可以在运行时取消注册命令吗? |
可以这么做,但是这更不稳定,并且可能造成未预料的副作用。为简化事情,你需要在 brigadier 中使用反射并移除这个节点,然后还需要再次使用 '' | 可以这么做,但是这更不稳定,并且可能造成未预料的副作用。为简化事情,你需要在 brigadier 中使用反射并移除这个节点,然后还需要再次使用 '' | ||
zh_cn/tutorial/commands.txt · Last modified: 2023/11/19 02:02 by solidblock