# 组件实时协议 当前协议版本为 `1`。后端的权威定义在 `domain.rs` 的 `ComponentMessage`,浏览器渲染器只依赖本文列出的稳定字段。 ## 连接地址与认证 组件流地址: ```text wss://danmaku.luoxingci.com/api/v1/components//stream ``` OBS 页面地址中的 token 位于 fragment: ```text https://danmaku.luoxingci.com/obs/#token= ``` fragment 不会进入首次 HTTP 请求或 Nginx access log。WebSocket 建立后,客户端必须在 8 秒内发送第一帧: ```json { "type": "authenticate", "token": "component-token" } ``` 服务端验证 token 摘要、组件归属和 `events:subscribe` scope。认证成功后返回: ```json { "version": 1, "type": "authenticated", "componentId": "08d31d19-3e4b-4c11-9cf7-9bd786a39465" } ``` 之后立即发送 `overlay.settings.snapshot`,再开始发送实时事件。token 无效、被轮换或属于其他组件时,服务端以 policy close 结束连接。 ## 版本化事件信封 ```json { "version": 1, "id": "d56c1a28-a6f1-4ad9-b02f-4c22dc4d3c27", "componentId": "08d31d19-3e4b-4c11-9cf7-9bd786a39465", "sourceId": "c8087bb0-0dde-40eb-a43d-a9a2574c2717", "occurredAt": "2026-07-15T20:10:30.125Z", "roomId": "123456", "type": "live.danmaku", "payload": { "viewer": { "uid": "42", "name": "观众" }, "text": "晚上好", "segments": [{ "type": "text", "text": "晚上好" }] } } ``` `ownerId` 永远不会序列化到浏览器。消费者应按 `version` 和 `type` 分派,并忽略不认识的 payload 字段,从而允许兼容地增加元数据。 ## 事件类型 | `type` | 主要 payload | 说明 | | --------------------------- | --------------------------------------------- | ---------------------- | | `overlay.settings.snapshot` | `settings` | 认证后当前设置快照 | | `overlay.settings.updated` | `settings` | 控制台保存后的实时设置 | | `live.danmaku` | `viewer`, `text`, `segments` | 普通文字和表情分段 | | `live.enter` | `viewer` | 进房事件 | | `live.gift` | `viewer`, `gift`, `quantity`, `sourceEventId` | 一次可记账的礼物事件 | | `live.gift.combo` | `viewer`, `gift`, `quantity`, `comboId` | 同一连击的视觉更新 | | `live.superchat` | `viewer`, `message`, `price`, `sourceEventId` | 醒目留言 | | `live.guard.buy` | `viewer`, `guardName`, `quantity`, `price` | 舰长购买 | | `live.like` | `viewer` | 点赞 | | `live.share` | `viewer` | 分享 | | `live.unknown` | `command`, `metadata` | 有界且已清理的未知事件 | 礼物目录价格的原始单位是人民币的千分之一。`gift.totalPrice` 保留该整数单位,`gift.priceCny` 是供展示使用的人民币数值。 `live.gift` 与 `live.gift.combo` 不是两笔礼物。需要持久化计数的组件通常只消费 `live.gift`;连击事件用于更新同一张视觉卡片。 ## 表情分段 `live.danmaku.payload.segments` 是判别联合: - `text`:包含可直接显示的文字。 - `emoticon`:包含回退文字、图片 URL、可选尺寸、动态标记和整条大表情标记。 图片失败时客户端必须回退到 `text`,不能让一张失效图片破坏整条弹幕。 ## 背压与重连 - 每个组件使用有界 `tokio::broadcast` channel。 - 慢客户端发生 `Lagged` 时跳过已经丢失的旧帧,继续接收新事件;实时展示不保证历史重放。 - 浏览器对非鉴权关闭使用指数退避重连,上限 12 秒。 - 鉴权失败不会自动重试,避免对已撤销 token 形成无限请求。 - 需要可靠业务处理的功能必须实现 `EventHandler` 并写入数据库,不能把 WebSocket 当作消息队列。