formatting and comments

This commit is contained in:
2026-07-16 00:12:26 -07:00
parent edb6d2b5b4
commit 994854d104
45 changed files with 2514 additions and 628 deletions
+103
View File
@@ -0,0 +1,103 @@
# 组件实时协议
当前协议版本为 `1`。后端的权威定义在 `domain.rs` 的
`ComponentMessage`,浏览器渲染器只依赖本文列出的稳定字段。
## 连接地址与认证
组件流地址:
```text
wss://danmaku.luoxingci.com/api/v1/components/<publicId>/stream
```
OBS 页面地址中的 token 位于 fragment:
```text
https://danmaku.luoxingci.com/obs/<publicId>#token=<component-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 当作消息队列。