Skip to content

斜杠命令 ​

mod 只对着 loader 的类型声明命令树,不碰 Brigadier,也不碰任何版本的命令类。declareCommands 在 onInitialize 之后被调用一次,它留下的只是一棵纯数据树;每版本适配器在游戏每次构建 dispatcher 时 (服务端启动、以及每一次数据包重载)把这棵树翻译成真正的命令。这个分工正是命令能在 dispatcher 重建后 继续工作的原因:mod 自己往 dispatcher 里注册的话,第一次重载就会把命令丢掉。

declareCommands ​

@Mod 类实现 OMLCommandProvider:

kotlin
@Mod(id = "my_mod", name = "My Mod", version = "1.0.0")
class MyMod : OMLModInitializer, OMLCommandProvider {

    override fun declareCommands(commands: OMLCommandRegistry) {
        commands.register("mycmd") {
            permissionLevel = 2
            executes { source ->
                source.reply("hello from my_mod, executed by ${source.name}")
            }
            argument("count", OMLArgumentType.INTEGER) {
                executes { source -> source.reply("count=${source.getInt("count")}") }
                subcommand("loud") {
                    executes { source -> source.reply("COUNT=${source.getInt("count")}") }
                }
            }
        }
    }
}

register 声明根字面量(/mycmd)。subcommand 加一个子命令字面量,argument 加一个带类型的参数; executes 可以挂在任何节点上,包括已经有子节点的节点——上面的 /mycmd、/mycmd 5、/mycmd 5 loud 是三个各自独立的处理器。参数值在处理器里按 argument 给的名字读取,且只有当这个参数真的落在被执行的那条 路径上时才读得到。

声明节点的成员 ​

成员含义
subcommand(name) { }字面量子节点:/root <name> …
argument(name, type) { }带类型的子节点:/root <name> <value> …
executes { source -> }该节点作为命令终点时执行
permissionLevel该节点及其以下所有节点的最低权限等级

permissionLevel 默认 0(所有人)。等级为 0 的节点根本不会挂权限检查,所以受保护父节点下面一个没设 权限的子节点,仍然受父节点保护。

参数类型 ​

OMLArgumentType游戏侧解析值类型读取方式
WORD单个不带引号的词StringgetString
STRING带引号或单个词StringgetString
GREEDY_STRING吞掉整行剩余输入StringgetString
INTEGER整数IntgetInt
FLOAT带小数的数DoublegetDouble
BOOLEANtrue / falseBooleangetBoolean

每种类型只对应一个 getter。getter 内部是不做检查的强转,因此拿错 getter 会在你的处理器里抛 ClassCastException——命令桥接层接住它,给调用者回一条红色的 [my_mod] command failed: …,同时把堆栈 打进日志。读一个不在执行路径上的参数名也是同一套表现(… is not part of the executed path)。

命令来源 ​

每次调用会给处理器一个 OMLCommandSource:

成员含义
name调用者的显示名——控制台是 Server
hasPlayer只有玩家执行时为 true(控制台、function、命令方块都是 false)
reply(message)成功通道:玩家收到聊天消息,控制台是普通 stdout
replyError(message)失败通道:玩家侧渲染为红色
getString/getInt/getDouble/getBoolean按声明名读取参数值
platform原生版本对象(26.x 是 CommandSourceStack)

platform 就是那个常规逃生舱:当上面这套表面不够用时,用它去拿玩家、世界或游戏自己的权限状态。

权限等级 ​

permissionLevel 对应的就是游戏的 OP 等级,不是 OML 自造的数字:

值含义
0所有人
1管理员(moderator)
2游戏主管理员
3服主级管理员
4+服务器所有者

声明什么时候变成真命令 ​

翻译发生在 dispatcher 构建的时刻,有两个注入点(客户端与专用服务端各一个),而每个重建出来的 dispatcher 只会被注册进去一次。由此有两条必须知道的推论:

  • 改命令树要重启。declareCommands 只在 mod 初始化时跑过一次,之后游戏用的就是那棵树——数据包重载只是 拿同一批声明重建 dispatcher。
  • 一条坏声明会被隔离。Brigadier 会拒绝空名字、非法字符和重复的根字面量;桥接层记录 registering /<name> from mod [<id>] failed 并跳过这一条命令,其他 mod 的命令照常落地。两个 mod 抢同一个 根字面量时,先声明的那个赢,另一个就是上面那行日志——预计会撞名就用带命名空间风格的根名字 (my_mod_cool 而不是 cool)。

暂未覆盖 ​

除了游戏自带的字面量补全之外没有参数建议 / Tab 补全,没有命令别名,也没有纯客户端命令。 loader 自己就是通过同一套 API 注册了 /oml mods(列出已加载 mod 及版本,初始化失败的用红色标出)和 /oml version;除示例模组的 /techmod_ping 之外,这两条就是能跑的真实参考。

仍在沉淀

命令 DSL 属于已提供、仍在沉淀层:0.x 阶段允许调整形态,调整会记进发布说明。 ::

基于 AGPL-3.0 许可证发布