交互式命令允许参数通过玩家接下来的游戏操作取得。例如,命令可以等待玩家点击一个方块,再把对应的 Block 交给执行器。
交互参数由 Bukkit Runtime 提供,只能由玩家触发。Core 在解析到交互参数时保存当前命令节点、已解析变量、参数位置和输入位置;Bukkit Runtime 收到目标事件后把结果交还 Core,Core 从下一个参数继续解析并最终执行同一个叶子处理器。
声明交互参数:
commands.register(
"rs select block <selection:event:block>",
ctx -> {
Player player = ctx.requirePlayer("Only players can select blocks");
Block block = ctx.get("selection");
selectionService.select(player, block);
},
CommandOptions.options()
.permission("rainbowedit.select.block")
.player()
.i18n(i18n)
);
当前交互参数仍然占用一个命令 token。玩家需要输入一段用于进入该参数位置的内容,例如:
/rs select block begin
begin 的文本不会用于选择方块。解析器到达 selection:event:block 后挂起命令并提示玩家点击,随后用实际点击的 Block 替换这个参数。
交互参数之前的普通参数会被保存,之后的参数会在交互完成后继续解析:
rs region point <index:int(1..2)> <block:event:block> [name:string=default]
交互完成后,执行器可以同时读取 index、block 和 name。
Bukkit Runtime 当前注册以下类型:
event:block:等待玩家点击方块,返回 Block。break:block:等待玩家破坏方块,返回 Block。place:block:等待玩家放置方块,返回放置后的 Block。event:entity:等待玩家右键实体,返回 Entity。damage:entity:等待玩家攻击实体,返回被攻击的 Entity。kill:entity:等待玩家击杀实体,返回死亡的实体。event:item:等待玩家点击容器中的物品,返回 ItemStack。shoot:block:等待玩家发射的抛射物命中方块,返回 Block。交互类型会检查发送者是否为 Bukkit Player。即使叶子使用默认的 ALL,控制台解析交互参数时仍然会失败;交互命令应通过 CommandOptions.player() 明确限制为玩家执行。
sequenceDiagram
participant Player as 玩家
participant Command as 命令服务
participant Hub as InteractionHub
participant Bukkit as Bukkit 事件
Player->>Command: 输入包含交互参数的命令
Command->>Command: 解析前置参数并保存游标
Command->>Hub: 等待指定交互
Hub-->>Player: 显示交互提示
Player->>Bukkit: 点击、破坏或攻击
Bukkit->>Hub: 发送目标事件
Hub->>Command: 返回 Block、Entity 或 ItemStack
Command->>Command: 恢复后续参数解析
Command->>Player: 执行叶子处理器
恢复过程继续使用原命令服务中已经安装的全部解析器,不会另外建立一套基础参数解析器。一个命令包含多个交互参数时,前一个完成后可以再次挂起并等待下一个交互。
权限和执行目标在首次参数解析前完成检查,因此无权限玩家不会进入等待状态。
使用交互式命令时,需要注意:
event:item 点击空槽位时可能得到 null,业务代码必须检查结果。CommandFailure.Reason 预留了 INTERACTION_TIMEOUT 与 INTERACTION_CANCELLED,当前 Bukkit Runtime 尚未把超时和取消通知给命令组失败回调。LangText 标签。交互处理器执行的业务代码仍位于普通叶子执行器中,因此成功、参数失败和业务执行失败会继续触发所属命令组的结果回调。尚未产生通知的超时与取消不在此范围内。
讨论