在线联机原型全集:第 2 章 石头剪刀布(Rock–Paper–Scissors, RPS)
类别:回合制匹配游戏原型 目标:验证匹配、房间生命周期、锁步回合、服务器权威判定与日志存档的全流程。 原型代号:
proto-002-rps依赖模块:proto-001-echo-chatroom推荐语言栈:Go / Node.js / Rust 网络协议:WebSocket(双向实时)
一、概述(Overview)
“石头剪刀布”是所有 匹配制游戏 的起点。 它既具备简单的规则确定性(三种动作、明确胜负), 又包含了最基础的网络交互闭环:
匹配 → 准备 → 提交 → 判定 → 结算 → 存档
它验证了以下关键能力:
- 匹配队列机制:支持多玩家排队等待配对;
- 状态锁步(Turn-based Lockstep):同时提交指令,统一结算;
- 服务器权威判定:防作弊与重放;
- 断线重连 与 对局恢复;
- 日志系统:完整记录对局过程。
这使得 RPS 成为整个“联机验证体系”的第二块基石。
二、核心玩法与系统目标(Gameplay & Objectives)
2.1 玩法简述
- 玩家点击“开始匹配”;
- 匹配系统将两个玩家配成一组;
- 双方进入对战房间,倒计时开始;
- 双方在 5 秒内选择动作(石头、剪刀、布);
- 超时未提交则默认弃权;
- 服务端判定胜负,广播结果;
- 支持多局模式(BO3)。
2.2 系统目标与验证点
| 类别 | 功能目标 | 验证重点 |
|---|---|---|
| 匹配系统 | 随机或 Elo 匹配两名玩家 | 队列调度、并发锁 |
| 回合系统 | 同步状态更新 | 状态机控制、定时器 |
| 权威判定 | 服务器结算胜负 | 防作弊、防重放 |
| 存档系统 | 保存战局日志 | 可重放性验证 |
| 断线恢复 | 重新连接后恢复状态 | Snapshot 恢复机制 |
| 观战系统 | 第三方只读通道 | 延迟广播与隔离 |
| 扩展验证 | 积分 / Elo 排名 | MatchHistory + Leaderboard |
三、系统架构设计(Architecture)
3.1 模块图
graph TD
A[Client] --> B[Gateway Server]
B --> C[Matchmaker]
B --> D[Room Service]
D --> E[Game Logic Engine]
E --> F[Event Logger]
E --> G[Ranking Service]
说明:
| 模块 | 功能说明 |
|---|---|
| Gateway Server | 用户接入、身份验证、连接管理 |
| Matchmaker | 匹配系统(队列、Elo 算法) |
| Room Service | 创建房间、维护状态机 |
| Game Logic Engine | 回合判定逻辑与状态更新 |
| Event Logger | 对局日志与重放存储 |
| Ranking Service | Elo 积分与排行榜计算 |
3.2 系统分层
-
接入层(Gateway)
- 负责连接与路由
- 校验 token、绑定用户 ID
-
逻辑层(Game Engine)
- 处理回合状态
- 调用定时器触发结算
-
数据层(Storage)
- Redis(匹配队列与房间状态)
- PostgreSQL(对局日志、Elo 分数)
-
监控层(Metrics)
- Prometheus 指标采集
- Grafana 可视化
3.3 组件交互流程
sequenceDiagram
Client->>Gateway: request_match()
Gateway->>Matchmaker: enqueue(player)
Matchmaker->>RoomService: create_room(p1,p2)
RoomService->>Clients: notify_room_ready
Clients->>GameLogic: submit_action(rock/paper/scissors)
GameLogic->>Logger: record_event
GameLogic->>Clients: result(win/lose/draw)
四、功能模块详解(Functional Modules)
4.1 Matchmaker(匹配系统)
4.1.1 功能描述
匹配系统的任务是:
根据玩家的进入顺序或 Elo 分数,将两名玩家配成一场游戏。
4.1.2 队列机制
-
基于 Redis SortedSet(score 为 Elo);
-
支持:
enqueue(player_id, score)dequeue(pair of players)
-
匹配策略:
- 优先相近 Elo;
- 若等待时间 > T 秒,则扩大匹配窗口。
4.1.3 关键参数
| 参数 | 默认值 | 含义 |
|---|---|---|
MATCH_TICK |
100ms | 匹配轮询间隔 |
MATCH_WINDOW |
100 Elo | 初始 Elo 差距 |
EXPAND_RATE |
20 Elo/s | 匹配范围扩大速度 |
4.1.4 数据结构
type MatchRequest struct {
UserID string
Elo int
EnqueueTime time.Time
}
4.2 Room Service(房间服务)
4.2.1 职责
- 创建/销毁房间;
- 管理房间成员;
- 驱动游戏状态机。
4.2.2 状态机
stateDiagram-v2
[*] --> Idle
Idle --> Matching: enqueue
Matching --> Ready: pair found
Ready --> Playing: countdown
Playing --> Judging: all submitted or timeout
Judging --> Result: server decision
Result --> Finished: show result
Finished --> [*]
4.2.3 数据结构
type Room struct {
ID string
Players []Player
State RoomState
Round int
Actions map[string]string
Timer *time.Timer
}
4.3 Game Logic Engine(判定逻辑)
4.3.1 判定规则
| 动作 A | 动作 B | 结果 |
|---|---|---|
| Rock | Scissors | A 胜 |
| Scissors | Paper | A 胜 |
| Paper | Rock | A 胜 |
| 相同 | 相同 | 平局 |
4.3.2 防作弊机制
- 客户端提交动作时不暴露给对方;
- 每回合有唯一
RoundToken; - 服务器收到动作 → 暂存;
- 双方都提交或超时 → 判定;
- 所有操作写入日志;
- 客户端提交签名:
SHA256(player_id + round + secret)。
4.3.3 多局模式(BO3)
- 每局独立计分;
- 胜方计 1 分;
- 先获得 2 分即胜。
4.4 Event Logger(事件日志)
4.4.1 日志结构
{
"room_id": "rps-001",
"round": 1,
"actions": {
"u1": "rock",
"u2": "scissors"
},
"result": "u1_win",
"timestamp": 1730689200
}
4.4.2 存储结构
-
Redis:短期缓存;
-
PostgreSQL:长期归档;
-
支持回放(Replay):
- 按时间序列重放;
- 验证一致性 hash。
4.5 Ranking Service(Elo 排名)
4.5.1 算法公式
[ E_A = \frac{1}{1+10^{(R_B-R_A)/400}} ] [ R_A’ = R_A + K(S_A - E_A) ]
4.5.2 参数
| 参数 | 默认 | 说明 |
|---|---|---|
K |
32 | 调整系数 |
R_init |
1000 | 初始 Elo |
S |
胜=1, 负=0, 平=0.5 | 比赛结果 |
4.5.3 数据结构
type EloRecord struct {
UserID string
Score int
Win int
Lose int
Draw int
}
五、状态与事件定义(State & Event Definitions)
5.1 客户端事件
| 事件 | 参数 | 描述 |
|---|---|---|
match_start |
- | 匹配开始 |
match_success |
{room_id} | 匹配成功 |
round_start |
{countdown} | 回合开始 |
submit_action |
{move: rock/paper/scissors} | 提交动作 |
round_result |
{winner, actions} | 回合结算 |
match_end |
{score} | 对局结束 |
六、数据模型(Data Model)
| 表 | 字段 | 说明 |
|---|---|---|
matches |
id, p1, p2, result, start_at, end_at | 比赛记录 |
rounds |
id, match_id, round_no, actions, winner | 每回合日志 |
elo_scores |
user_id, elo, win, lose, draw | 玩家积分 |
七、验证指标(Metrics & KPI)
| 指标 | 目标 | 说明 |
|---|---|---|
| 匹配平均耗时 | < 2 秒 | 正常队列延迟 |
| 回合提交延迟 | < 100ms | Ping-Pong 校准 |
| 判定一致率 | 100% | 日志校验 |
| 断线恢复成功率 | ≥ 99% | Snapshot |
| 吞吐能力 | ≥ 1000 房间 / 节点 | 并发验证 |
八、扩展功能(Extensions)
-
积分排行榜(Elo Rank)
- Redis SortedSet + 周期性刷新
- 每日 Top N 公告
-
重连机制
- 客户端断开后 30 秒内重新连接 → 恢复房间状态
- 服务器持有
SessionSnapshot
-
观战系统
- 延迟 1 回合广播动作(防剧透)
- 分层广播:玩家 vs 观众
-
回放系统
- 重放日志 → 客户端动画化重现
- 支持加速播放与分析
九、技术选型与实现建议
| 层级 | 推荐技术 | 理由 |
|---|---|---|
| 网络层 | Go + WebSocket | 性能稳定 |
| 匹配层 | Redis SortedSet | 时间复杂度 O(logN) |
| 状态层 | FSM + TimerWheel | 精准控制回合超时 |
| 数据层 | PostgreSQL | 强一致存档 |
| 缓存层 | Redis | 快速读取 Elo 与房间状态 |
| 日志层 | Loki / ElasticSearch | 可视化检索 |
| 监控层 | Prometheus + Grafana | 实时可视化 |
十、压测与运维(Load & Ops)
| 测试项 | 内容 | 目标 |
|---|---|---|
| 匹配压力 | 10 万用户并发入队 | 平均延迟 < 2s |
| 回合同步 | 每秒 5000 回合 | 无漏包 |
| 日志写入 | 500 MB/min | IO 无阻塞 |
| 网络丢包恢复 | 30% 丢包率 | 重连成功率 >99% |
自动化监控指标:
match_queue_lengthroom_active_totalelo_update_latencydisconnect_reconnect_total
十一、设计反思与演化(Reflection & Evolution)
11.1 设计反思
- RPS 虽简单,但完整覆盖了在线对局最核心的流程闭环;
- 它的关键不是“游戏逻辑”,而是“系统可靠性”;
- 匹配、超时、判定、存档构成了所有游戏的公共骨架。
11.2 向下一阶段演化
| 目标 | 下一原型 |
|---|---|
| 网格逻辑与棋盘状态 | → #3 井字棋 |
| 回放 / 观战系统 | → #4 快速问答抢答 |
| 状态同步与 Tick 机制 | → #6 Pong |
| 房间生命周期管理 | → #10 mini-SLG |
十二、总结
“石头剪刀布” 是 服务器权威模型(Server-authoritative Model) 的最佳入门。 它让我们验证了从“客户端输入”到“服务器决策”的完整闭环: 每一条消息、每一次延迟、每一个回合状态, 都在教我们一件事——
“在分布式的不确定世界中,服务器才是唯一真相。”