自定义命令注册(Commands)¶
给服务器加自定义命令(比如 /heal、/giveall、/ffgs),不用写 Java 模组,KubeJS 脚本就能搞定。本篇讲两种方式:
- basicCommand(简单):几行代码注册一条命令,适合新手;
- 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.INTEGER、event.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.player;commandRegistry 里是 ctx.source.player,别混用;
- 命令回调最后一定要 return 1,否则游戏认为命令失败;
- 参数名必须和取值一致:argument('item', ...) → ctx.arguments.item;
- basicCommand 有输入时 event.input 是字符串,要自己 parseInt / split;
- 改了命令脚本后 /reload 即可生效(无需重启服务器)。