指令树与注册机制

基本信息

属性 值
指令总数 137(136 个 @Command 方法 + 1 个显式注册的 /set)
顶层根指令 6 组
命令图构建入口 com.sk89q.worldedit.extension.platform.CommandManager
命令图构建行号 CommandManager.java:136-210
分发器获取 CommandManager.getDispatcher()(CommandManager.java:388)
向 Forge 注册 ForgePlatform.registerCommands(Dispatcher)(ForgePlatform.java:154-183)
@Command 注解总数 136(全仓 grep -rn '@Command(' src/main/java 计数,两种写法交叉验证一致)

注册流程

WorldEdit 不使用 @Mod 的 preInit 直接注册指令,而是构建一棵流式命令图(fluent command graph),最后把根节点的子命令逐个包装成 Forge ICommand 注册。

  1. CommandManager 构造时调用 new CommandGraph().builder(builder),链式调用 .commands() 得到根 DispatcherNode,随后逐个 .registerMethods(...) 与 .group(...)(CommandManager.java:136-210)。
  2. .registerMethods(obj) 通过 ParametricBuilder.registerMethodsAsCommands 反射扫描对象上所有带 @Command 的方法并注册(DispatcherNode.java:82-87)。
  3. .group(alias...) 创建一个子分发器并注册到当前节点,返回一个 parent 指向当前节点的新 DispatcherNode(DispatcherNode.java:102-105)。
  4. .parent() 返回父节点(DispatcherNode.java:114-120)。
  5. ForgePlatform.registerCommands 遍历 dispatcher.getCommands() —— 该方法只返回直接子节点,不展平嵌套(SimpleDispatcher.java:73-76),因此只有 6 个根组被注册进 Forge。

// 与 / 前缀规则

这是 WorldEdit 1.7.10 指令拼写最容易误解的一点。规则由注解别名本身是否以 / 开头决定:

  • 别名以 / 开头(如 /set)→ 注册名含一个斜杠,玩家在聊天框需再打一个斜杠,即 //set
  • 别名不以 / 开头(如 version)→ 注册名即别名,玩家输入 /version

机制:ForgeWorldEdit.onCommandEvent 与 CommandWrapper.getCommandName() 直接使用别名原文(CommandWrapper.java:43-45 返回 command.getPrimaryAlias()),Forge 端命令名照单全收。同一 registerCommand 调用的多个别名共用一个 SimpleCommandMapping,getCommands() 基于 commands.values()(HashSet)去重,因此一个根组只向 Forge 注册一次,其余作为 getCommandAliases() 返回(SimpleCommandMapping.java:47-49,CommandWrapper.java:47-50)。

6 个顶层根指令

根指令 别名 注册行 内容类 命令数
/worldedit we CommandManager.java:158-161 WorldEditCommands(5) + ForgeRotationCommands(1) 6
/schematic schem、//schematic、//schem CommandManager.java:163-165 SchematicCommands 5
/snapshot snap CommandManager.java:167-169 SnapshotCommands 5
/brush br CommandManager.java:171-173 + :174-202 BrushCommands 7
/superpickaxe pickaxe、sp CommandManager.java:203-204 SuperPickaxeCommands 3
/tool (无) CommandManager.java:206-208 ToolCommands 9

ToolCommands 被注册两次:一次作为根指令(CommandManager.java:150),一次作为 /tool 组的子命令(CommandManager.java:208)。因此 //tree 与 /tool tree 均可使用。

根节点直接注册(非 // 组内)的类共 15 个(CommandManager.java:138-151),贡献 119 个 @Command 方法;另有 1 个 /set 以 .register(adapt(new SelectionCommand(...)), "/set") 显式注册(CommandManager.java:152-157),它不带 @Command 注解。

权限

每个 @CommandPermissions("...") 值在注册时经 ForgePlatform.registerCommands 逐个登记进 ForgePermissionsProvider(ForgePlatform.java:162-181)。首个别名取 permissions.get(0) 绑定到具体命令,其余别名绑定 null(仅登记权限名)。未标注 @CommandPermissions 的命令不需要权限节点——@Command 的 desc/usage 之外无隐含权限检查。

//we reload 与 //we reloadmappings 共用权限节点 worldedit.reload(WorldEditCommands.java:73-74、ForgeRotationCommands.java:25-26)。

事件挂钩

指令执行前,Forge 的 CommandEvent 被 ForgeWorldEdit.onCommandEvent 拦截,转成 WorldEdit 自己的 CommandEvent 发到事件总线(ForgeWorldEdit.java:201-216);CommandWrapper.addTabCompletionOptions 把补全请求转成 CommandSuggestionEvent(CommandWrapper.java:54-70)。详见 事件总线与事件类。

相关条目