「被围观」是一款联机游戏社交化、竞技化、长留存的重要能力。观战与回放系统让高手对局变成内容、变成教学、变成社区话题,也让玩家自己复盘成长。本 PRD 是《在线联机原型全集》的扩展篇之一,聚焦如何为已有联机原型低成本加上「观战 + 回放」双通道。
1. 目标与验证点
| 编号 | 验证能力 | 说明 |
|---|---|---|
| E1 | 观战通道 | 实时/延迟观战、镜头权限、观战者不干扰对局 |
| E2 | 事件流录制 | 全量事件序列化、日志压缩、与状态快照对齐 |
| E3 | 确定性重放 | 同一输入序列可复现同一结果(hash 校验) |
| E4 | 回放交互 | 进度条跳转、倍速播放、关键帧定位 |
| E5 | 反作弊闭环 | 回放数据与服务器日志比对,杜绝伪造 |
一句话目标:让「每一局都能被回看」,观战不拖垮对局性能,回放与服务器裁决一致。
2. 系统架构
flowchart TD
A["Game Room"] -->|action events| B["Event Collector"]
B -->|compressed stream| C["Replay Logger"]
C -->|snapshot+delta| D["Object Storage (S3/OSS)"]
B -->|live feed| E["Spectator Gateway"]
E --> F["Spectator Client"]
D -->|lazy load| G["Replay Service"]
G --> H["Replay Player"]
H -->|verify| I["Hash Verifier"]
说明:
- Event Collector:驻留房间进程内,拦截全部输入与裁定事件,附全局单调时钟
- Replay Logger:按
roomID + 局次落盘,事件流 + 周期快照 - Spectator Gateway:从同一事件流转发实时观战,支持延迟档位
- Replay Service:按需加载回放,做索引、跳转、倍速服务
3. 观战模式设计
3.1 三种观战档位
| 档位 | 延迟 | 用途 | 实现 |
|---|---|---|---|
| 实时观战 | 0-2s | 好友围观 | 从事件流直接转发,降级丢帧 |
| 延迟观战 | 2-10min | 电竞转播/反作弊 | 延迟队列 + 数据缓存 |
| 回放观战 | 无 | 复盘/教学 | 读已落盘 Replay |
3.2 观战者权限
观战者 = 只读角色:
- 只能订阅状态流,不能发操作指令
- 房间容量 = 对局人数 + 观战席上限(如 32)
- 观战者不参与匹配、不占对局席位、不计入胜负
关键约束:观战通道必须与对局逻辑解耦——观战者掉线不得影响对局,观战流量走高也不得拖慢主逻辑。
4. 事件流录制与存储
4.1 录制内容
每局录制三部分:
01 元信息:模式、规则、种子、玩家列表、时间
02 事件流:每次输入与每次服务端裁定(含 seq + 时钟)
03 快照链:每 N 帧一个完整状态快照(用于跳转与校验)
4.2 快照 + 增量混合
慢速对局(回合制):事件流为主,快照稀疏
快速对局(帧同步):快照密集(每 1s),增量 delta 压缩
跳转策略:找到最近快照 → 应用后续 delta/事件 → 恢复到目标时刻
4.3 数据模型
| 表 | 字段 |
|---|---|
replay_meta | replay_id、game_id、mode、seed、player_ids、result、created_at |
replay_events | replay_id、seq、tick、ts_ms、type、payload_json |
replay_snapshots | replay_id、tick、state_hash、payload_bytes |
5. 接口定义
| 模块 | 接口 | 描述 |
|---|---|---|
| Spectator | ws /spectate/{roomId} | 加入观战流,?delay=30s 指定档位 |
| Spectator | ws /spectate/{roomId}/leave | 退出观战 |
| Replay | GET /replay/{replayId}/meta | 获取对局元信息 |
| Replay | GET /replay/{replayId}/stream?from_tick&to_tick | 分段拉取事件/快照 |
| Replay | POST /replay/verify | 提交本地 hash,与服务器比对 |
6. 确定性重放与反作弊
6.1 确定性条件
确定性重放 = 同一 seed + 同一输入序列 + 同一逻辑版本 → 同一结果
需要保证:
- 逻辑无随机性依赖(或随机数全部由 seed 派生)
- 浮点运算确定性(定点化或记录精度)
- 时钟只做展示,不做裁定输入
6.2 校验机制
// 每 60 tick 计算一次状态 hash,与服务器裁决比对
func computeStateHash(state *GameState) string {
h := sha256.New()
for _, ent := range state.Entities {
h.Write(ent.SerializeDeterministic())
}
return hex.EncodeToString(h.Sum(nil))
}
// 回放端周期性上报 hash,服务器返回期望值
func (s *ReplayService) Verify(replayID string, tick int, hash string) bool {
expected := s.snapshotHash(replayID, tick)
return subtle.ConstantTimeCompare([]byte(expected), []byte(hash)) == 1
}
反作弊价值:观战/回放数据必须与服务器权威结果一致。任何「伪造精彩操作」的录像都能通过 hash 比对识破,保证社区内容的可信度。
7. 关键技术点
| 主题 | 方案 |
|---|---|
| 时钟对齐 | 全局单调时钟 tick_ms,客户端播放按它而非本地时间 |
| 带宽控制 | 观战流丢帧降级(只传关键实体),避免拖垮转播 |
| 冷热存储 | 热回放 Redis 缓存,冷回放 S3,按需加载 |
| 索引检索 | 按 mode / player / result 建索引,支持「搜某个玩家的精彩局」 |
| 容量控制 | 回放默认保留 N 天,超期归档或清理 |
8. 里程碑与验收
M1 事件录制 + 落盘(1 周)
✅ 对局结束生成 replay_meta,事件流完整可查
M2 确定性重放(1 周)
✅ 同一 replay 重放两次结果一致(hash 相等)
M3 实时观战(1 周)
✅ 观战席可加入、退出,不影响对局;延迟档位生效
M4 回放播放器(1 周)
✅ 支持进度条跳转、倍速、关键帧定位
验收指标:观战接入后对局 CPU 增量 < 10%;回放 hash 校验一致率 100%;观战掉线率不影响对局 P99 延迟。
9. 相关原型衔接
本系统复用并增强已有原型:
- #3 井字棋/四子棋:观战只读通道 → 升级为通用 Spectator 通道
- #12 团队占点:快照回放 → 升级为快照+增量混合回放
- #37 战斗录像与分析系统:事件日志 → 升级为确定性重放 + hash 校验
扩展篇定位:观战与回放是「让对局产生二次价值」的能力。它不改变玩法本身,却把每一局变成可传播、可复盘、可教学的资产——是联机游戏从「可玩」走向「有生态」的台阶。
延伸阅读
- 「在线联机原型全集」目录(60 个原型全景) — 原型全景索引
- 「在线联机原型全集」第三章:异步与平台化层(#21–#30) — 回放/录像相关原型
- 「在线联机原型全集」第七章:验证体系全景图 — 验证体系与依赖矩阵
- [[products]] — 产品原型开发专题
- [[game]] — 游戏服务端实战
下一扩展篇:成就与里程碑系统。观战让对局被看见,成就让成长被记录——从「被围观」到「被铭记」。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。