跳转至

自定义命令注册(Commands)

给服务器加自定义命令(比如 /heal/giveall/ffgs),不用写 Java 模组,KubeJS 脚本就能搞定。本篇讲两种方式:

  1. basicCommand(简单):几行代码注册一条命令,适合新手;
  2. commandRegistry(进阶):完整 Brigadier 命令树,支持参数、Tab 补全、权限分级,适合做复杂的运营命令。

💡 本篇 API 依据 KubeJS 6.1 源码(dev.latvian.mods.kubejs.command / server.BasicCommandKubeEvent)编写,适用于 1.20.1。


1. 简单方式:basicCommand(推荐新手)

KubeJS 内置了最简命令注册:ServerEvents.basicCommand('命令名', event => {...}),一行注册、自动带 / 前缀:

// server_scripts/simple_commands.js
ServerEvents.basicCommand('hello', event => {   // 注册 /hello 命令(需要 OP 权限)
  event.player.tell('§a你好,' + event.player.username + '!这是 KubeJS 命令。')   // 给玩家发消息
})

// 想要所有人都能用(不需要 OP):用 basicPublicCommand
ServerEvents.basicPublicCommand('ping', event => {
  event.player.tell('§ePong!')
})

event 里能拿什么(BasicCommandKubeEvent):

属性 说明
event.player 执行命令的玩家
event.level 所在世界
event.block 执行者所在位置的方块
event.id 命令名
event.input 命令后面带的全部原始输入(字符串)

带参数(输入全在 event.input 里,自己解析):

ServerEvents.basicPublicCommand('add', event => {   // /add 10 20
  let parts = event.input.split(' ')   // 按空格拆开,['10', '20']
  let a = parseInt(parts[0]) || 0   // 第一个数,转不了就当 0
  let b = parseInt(parts[1]) || 0
  event.player.tell('§a' + a + ' + ' + b + ' = ' + (a + b))
})

⚠️ basicCommand 默认需要 OP(权限等级 2) 才能用;basicPublicCommand 所有人可用。


2. 进阶方式:commandRegistry(完整命令树)

想要参数自动校验、Tab 补全、子命令这些正经功能,用 ServerEvents.commandRegistry。它直接暴露 Minecraft 的 Brigadier 命令系统:

// server_scripts/advanced_commands.js
ServerEvents.commandRegistry(event => {   // 监听"注册命令"事件
  const { commands } = event   // commands = Minecraft 的 Commands 工具类(可调 literal/argument)

  // 注册 /hello —— 最基本的命令
  event.register(   // 把命令注册进游戏
    commands.literal('hello')   // literal = 固定字面量命令名(/hello)
      .executes(ctx => {   // 执行回调;ctx 是命令上下文
        let player = ctx.source.player   // 从上下文拿执行者(注意:basicCommand 用 event.player,这里用 ctx.source.player)
        player.tell('§a你好,' + player.username + '!')
        return 1   // 返回 1 = 命令成功
      })
  )
})

2.1 权限控制(requires)

命令注册链上可以加 .requires() 限制谁能用:

ServerEvents.commandRegistry(event => {
  const { commands } = event

  event.register(
    commands.literal('admincmd')   // 只有 OP 2 级能用
      .requires(src => src.hasPermission(2))   // hasPermission(2) = 权限等级 2(OP)
      .executes(ctx => {
        ctx.source.server.runCommandSilent('say 管理员执行了命令!')   // 全服广播
        return 1
      })
  )
})

💡 src 是命令源(CommandSourceStack):src.player 执行者、src.server 服务器、src.hasPermission(等级) 权限判断。也可以 ctx.source.player 取玩家。

2.2 参数(argument)

commands.argument('参数名', 类型) 加参数,参数值从 ctx.arguments.参数名 取:

ServerEvents.commandRegistry(event => {
  const { commands } = event

  event.register(
    commands.literal('giveall')   // /giveall <物品>
      .requires(src => src.hasPermission(2))
      .then(   // then = 命令的"下一层"
        commands.argument('item', event.arguments.ITEM_STACK)   // 参数名 item,类型是物品堆
          .executes(ctx => {
            let item = ctx.arguments.item   // 取出玩家输入的物品
            ctx.source.server.players.forEach(p => p.give(item))   // 发给全服所有人
            ctx.source.server.runCommandSilent('say 全服发放:' + item)   // 广播
            return 1
          })
      )
  )
})

常用参数类型event.arguments.XXX,来自 KubeJS 的 ArgumentTypeWrappers):

类型名 说明 取值
STRING 任意字符串 ctx.arguments.xxx 字符串
GREEDY_STRING 贪婪字符串(吃下所有剩余输入) 字符串
WORD 单个词(无空格) 字符串
INTEGER 整数 数字
FLOAT / DOUBLE 小数 数字
BOOLEAN true/false 布尔
PLAYER 在线玩家 玩家对象
PLAYERS 多个在线玩家 玩家集合
ENTITY / ENTITIES 实体/多实体 实体对象/集合
BLOCK_POS 方块坐标 坐标对象
VEC3 / VEC2 三维/二维坐标 向量
ITEM_STACK 物品 物品堆
BLOCK_STATE 方块状态 方块状态
COLOR 颜色 颜色
COMPONENT 聊天文本组件 组件
MESSAGE 消息 组件
NBT_COMPOUND / NBT_TAG / NBT_PATH NBT 复合标签/标签/路径 NBT
DIMENSION 维度 资源位置
TIME 时间 数字
UUID UUID UUID
RESOURCE_LOCATION 资源位置(如 minecraft:diamond) 资源位置
PARTICLE 粒子 粒子

💡 枚举名在 JS 里用大写event.arguments.INTEGERevent.arguments.PLAYER。参数值统一从 ctx.arguments.参数名 取。

2.3 Tab 补全(suggests)

给参数加建议列表,按 Tab 就有提示:

ServerEvents.commandRegistry(event => {
  const { commands } = event

  event.register(
    commands.literal('warp')   // /warp <地点>
      .then(
        commands.argument('place', event.arguments.STRING)   // 字符串参数
          .suggests((ctx, builder) =>   // 补全回调:返回建议列表
            event.builtinSuggestions.suggest(['主城', '商店', '刷怪塔', '出生点'], builder)   // 建议列表
          )
          .executes(ctx => {
            let place = ctx.arguments.place
            let player = ctx.source.player
            player.server.runCommandSilent('tp ' + player.username + ' 100 64 100')   // 示例:传送
            player.tell('§a已传送到:' + place)
            return 1
          })
      )
  )
})

💡 event.builtinSuggestions.suggest(数组, builder) 是 KubeJS 提供的补全快捷方式(源码注释里的官方用法)。

2.4 子命令(then 链)

命令可以无限嵌套,做「树状」命令:

ServerEvents.commandRegistry(event => {
  const { commands } = event

  event.register(
    commands.literal('ffgs')   // /ffgs
      .then(
        commands.literal('heal')   // /ffgs heal —— 治疗
          .executes(ctx => {
            let player = ctx.source.player
            player.heal(20)   // 回满血
            player.tell('§a已治疗!')
            return 1
          })
      )
      .then(
        commands.literal('fly')   // /ffgs fly —— 飞行
          .executes(ctx => {
            let player = ctx.source.player
            player.flying = !player.flying   // 切换飞行状态
            player.tell(player.flying ? '§a飞行已开启' : '§7飞行已关闭')
            return 1
          })
      )
  )
})

3. 完整案例:服务器管理命令包

把上面知识串起来,做一个带权限、参数、补全、子命令的完整示例:

// server_scripts/ffgs_commands.js —— 落英枪战服管理命令
ServerEvents.commandRegistry(event => {
  const { commands } = event

  // 主命令 /ffgs
  event.register(
    commands.literal('ffgs')
      .requires(src => src.hasPermission(2))   // 全部子命令都要 OP

      // /ffgs heal [玩家] —— 治疗(可指定别人)
      .then(
        commands.literal('heal')
          .then(
            commands.argument('target', event.arguments.PLAYER)
              .executes(ctx => {
                let target = ctx.arguments.target
                target.heal(20)
                target.tell('§a你被管理员治疗了!')
                return 1
              })
          )
          .executes(ctx => {
            ctx.source.player.heal(20)
            ctx.source.player.tell('§a已治疗!')
            return 1
          })
      )

      // /ffgs giveall <物品> —— 全服发物品
      .then(
        commands.literal('giveall')
          .then(
            commands.argument('item', event.arguments.ITEM_STACK)
              .executes(ctx => {
                let item = ctx.arguments.item
                ctx.source.server.players.forEach(p => p.give(item))
                ctx.source.server.runCommandSilent('say 管理员全服发放:' + item)
                return 1
              })
          )
      )

      // /ffgs warp <地点> —— 传送(带 Tab 补全)
      .then(
        commands.literal('warp')
          .then(
            commands.argument('place', event.arguments.STRING)
              .suggests((ctx, builder) =>
                event.builtinSuggestions.suggest(['主城', '商店', '刷怪塔'], builder))
              .executes(ctx => {
                let player = ctx.source.player
                let spots = { '主城': '0 64 0', '商店': '100 64 100', '刷怪塔': '200 64 200' }
                let pos = spots[ctx.arguments.place] || '0 64 0'
                player.server.runCommandSilent('tp ' + player.username + ' ' + pos)
                player.tell('§a已传送到:' + ctx.arguments.place)
                return 1
              })
          )
      )
  )
})

4. 两种方式对比

basicCommand commandRegistry
上手难度 ⭐ 最简单 ⭐⭐⭐ 需了解 Brigadier
参数 自己 split 解析 自动校验 + 类型转换
Tab 补全 suggests 支持
子命令 then 链支持
权限 固定 OP / 公开 requires 灵活控制
适用场景 简单功能、玩家指令 运营管理、复杂命令树

5. 速查表

想做什么 写法
简单命令(OP) ServerEvents.basicCommand('名字', event => {...})
简单命令(公开) ServerEvents.basicPublicCommand('名字', event => {...})
拿执行者(简单) event.player
拿原始输入(简单) event.input
注册命令(进阶) ServerEvents.commandRegistry(event => {...})
建命令节点 event.register(commands.literal('名字')...)
执行回调 .executes(ctx => {...})
拿执行者(进阶) ctx.source.player
拿服务器 ctx.source.server
权限限制 .requires(src => src.hasPermission(2))
加参数 commands.argument('名', event.arguments.类型)
拿参数值 ctx.arguments.参数名
Tab 补全 .suggests((ctx, builder) => event.builtinSuggestions.suggest([列表], builder))
子命令 .then(commands.literal('子命令')...)
执行成功 return 1(命令末尾必须返回数字)

常见坑: - basicCommand 里是 event.playercommandRegistry 里是 ctx.source.player,别混用; - 命令回调最后一定要 return 1,否则游戏认为命令失败; - 参数名必须和取值一致:argument('item', ...)ctx.arguments.item; - basicCommand 有输入时 event.input字符串,要自己 parseInt / split; - 改了命令脚本后 /reload 即可生效(无需重启服务器)。