消息与事件
桥接本身不定义业务,只规定一份通用的消息格式。你发什么、对方怎么理解,全靠 type + action 约定。
消息格式
一条消息就是一个 JSON 对象:
{
"type": "mc_event",
"action": "player_join",
"data": { "player": "Steve", "uuid": "069a79f4-44e9-4726-a5be-fca90e38aaf5" },
"clientId": "survival",
"timestamp": "2026-11-12T10:00:00Z",
"requestId": null,
"replyTo": null
}| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type | 字符串 | ✅ | 消息大类,业务路由的第一级。建议小写 + 下划线,如 mc_event、mc_command、mc_chat |
action | 字符串 | ❌ | 同一大类下的细分动作,如 player_join、exec。内置脚本要求它非空才处理 |
data | 任意 JSON | ❌ | 业务数据,对象 / 数组 / 字符串都行,通常用对象 |
clientId | 字符串 | ❌ | 发送方的服务器标识。MC 侧发出的消息会自动带上自己的 client-id;Bngeuit 侧发出的消息由服务端填成发送目标或 * |
timestamp | 字符串/数字 | ✅ | RFC3339 时间(如 2026-11-12T10:00:00Z)或 Unix 秒/毫秒数字。插件自动填,不用手写 |
requestId | 字符串 | ❌ | 需要对方回复时才有,12 位随机串 |
replyTo | 字符串 | ❌ | 这是一条「回复」时才有,值等于原请求的 requestId |
别自己拼 JSON
MC 侧脚本一律用 BridgeMessage 或 channel.send(type, action, data),时间戳、requestId、clientId 都会自动补齐。手拼 JSON 最容易漏 timestamp。
认证流程
连接建立后不是立刻能发消息,先握手:
MC-BngeuitBridge(客户端) BngeuitBridge(服务端)
│ │
│──────── WebSocket 连接 ─────────────→│
│←─── {"type":"auth","action":"request"}│
│ │
│─── {"type":"auth","data":{ │
│ "token":"xxx", │
│ "clientId":"survival"}} ──────→│
│ │
│←─── {"type":"auth","action":"ok", │ 认证成功
│ "data":{"clientId":"survival"}}│
│ │
│←═══════ 双向业务消息 ═══════════════→│认证通过之前发的业务消息会被服务端拒绝(not_authenticated)。
认证失败的三种情况
| 服务端返回 | 含义 | MC 控制台提示 |
|---|---|---|
auth / auth_failed | token 不对 | BngeuitBridge 认证失败!请检查 token 配置 |
error / client_id_required | 认证包里没有 clientId | 服务端要求必须提供 clientId,请在 config.yml 中配置 client-id |
error / client_id_taken | clientId 已被别的连接占用 | clientId "survival" 已被占用,请修改 config.yml |
三种情况客户端都会主动断开,不会自动重连,改完配置重载插件才会重新握手。
其他协议层错误
| action | 触发条件 |
|---|---|
invalid_json | 发过去的不是合法 JSON |
not_authenticated | 认证完成前发业务消息 |
message_too_large | 单条消息超过 max-message-size(默认 64 KB),data 里会带 maxSize |
binary_not_supported | 发了二进制帧,本协议只收文本 JSON |
请求-响应
普通消息是「发出去就不管」,但有些场景要等对方回话,比如查状态、查白名单。
发请求时用 channel.send(message),插件会给消息加上 requestId 并返回一个 BridgeResponse:
val request = BridgeMessage.of("mc_query", "online", null)
channel.send(request)
.onSuccess { resp -> Bukkit.getLogger().info("对方回了: " + resp.getData()) }
.onFailure { err -> Bukkit.getLogger().warning("没等到回复: " + err) }对方收到带 requestId 的消息后,用 message.reply(data) 回复,replyTo 自动指向原请求,发起方那边的 onSuccess 就会被触发。
A ── type=query, requestId=abc123 ──→ B
A ←── type=query, replyTo=abc123 ──── B规则:
- 超时 30 秒,超时后
onFailure收到「请求超时」 - 连接断开时所有待响应请求立刻失败,
onFailure收到「连接已断开」 - 回复消息(带
replyTo)不会再产生requestId,也不会等待回执,避免套娃 BridgeResponse.get(timeout, unit)可以阻塞等待,别在主线程用
内置约定:bridge/status
游戏内 /bngeuitbridge status 走的就是请求-响应:
| 项 | 值 |
|---|---|
| 请求 | type=bridge、action=status |
| 应答方 | Bngeuit 侧的 McBridgeStatus.nc |
应答 data | {version, uptimeSeconds, connectedClients, clientIds} |
| 超时 | 30 秒(没装应答脚本就会走到这一步) |
这个应答不是 BngeuitBridge 内置的,必须装 McBridgeStatus.nc,否则命令一定超时。同理,你想加别的查询(TPS、内存、在线人数),在同一个 type=bridge 下加 action 分支即可。
事件
两侧都能通过事件监听桥接的动静。
MC 侧(Bukkit 事件)
| 事件类 | 触发时机 | 线程 |
|---|---|---|
BridgeConnectEvent | TCP 连上服务端,此时还没认证 | WebSocket 线程 |
BridgeAuthEvent | 收到认证结果。成功时 success=true、有 clientId;失败时 errorMessage 有值 | WebSocket 线程 |
BridgeDisconnectEvent | 断开连接。closeCode、reason、wasAuthenticated | WebSocket 线程 |
BridgeMessageReceiveEvent | 收到一条业务消息(不含 auth、不含回复) | 异步线程 |
BridgeMessageSendEvent | 自己发出一条业务消息(含回复,不含 auth / 心跳) | 异步线程 |
字段:
| 事件 | 可用字段 |
|---|---|
BridgeConnectEvent | serverUrl |
BridgeAuthEvent | success、clientId、errorMessage |
BridgeDisconnectEvent | closeCode、reason、wasAuthenticated |
BridgeMessageReceiveEvent / BridgeMessageSendEvent | message,以及快捷属性 type、action、data、clientId |
这些事件都不在主线程
除认证/连接类事件外,消息事件都在 WebSocket 线程触发。任何 Bukkit API(改方块、发消息给玩家、注册监听、执行命令)都必须切主线程,用 SchedulerUtils.sync { ... } 包一层,否则轻则数据错乱重则崩服。
BridgeMessageReceiveEvent 是异步事件(async = true),可以直接在监听器里做耗时操作,但同样不能碰 Bukkit。
Bngeuit 侧(插件事件)
Bngeuit 的脚本插件监听的是同名但不同包的类:cn.org.bukkit.bngeuit.bridge.event.*。
| 事件类 | 触发时机 | 可用字段 |
|---|---|---|
BridgeConnectEvent | 有客户端连上(未认证) | remoteAddress |
BridgeAuthEvent | 有客户端认证成功/失败 | remoteAddress、clientId、reason |
BridgeDisconnectEvent | 客户端断开 | clientId、remoteAddress、reason |
BridgeMessageReceiveEvent | 收到某台 MC 发来的业务消息 | message、clientId、type、action、data |
BridgeMessageSendEvent | 自己发出的消息 | message、clientId(广播时是 *) |
Bngeuit 侧写脚本的完整方式见 Bngeuit 脚本插件文档,本插件相关部分见 脚本与插件开发。
