在线联机原型全集:第 17 章 战棋对战
战棋对战(Tactical Lockstep|Command Buffer & Replay)
- 类别:回合制战术对战(TBS) + 锁步一致性
- 目标:验证战术锁步下的输入收集→命令冻结→顺序结算的确定性流程;以Command Buffer 作为唯一权威输入面向事件溯源与回放;覆盖 Fog-of-War、断线恢复、悔棋/分支回放与观战。
- 原型代号:
proto-017-tactic-lockstep - 依赖模块:
proto-002-rps(时序/提交窗口基元)、proto-014-room-escape-sync(事件溯源/快照)、proto-016-mini-moba(关键帧+增量恢复思路) - 推荐栈:Server(Go/Java,Go 优先)、Client(TypeScript/Unity),可选 WASM 共享规则库
- 协议栈:HTTP(配对/断线恢复) + WebSocket(指令/状态广播);Tick=回合步(非实时帧)
1. 核心玩法与范围
- 棋盘:六边或方格网(推荐六边以简化移动/射线判定),尺寸 12×12~18×18。
- 单位:每队 4–6 枚单位,职业互补(近战/远程/支援/控场)。
- 回合结构:I-GO-U-GO 变体:计划阶段(Plan) → 锁步(Lock) → 结算(Resolve)。
- 情境:Fog-of-War、地形高低差、覆盖(Cover)、连携(Combo)与 Overwatch(守望射击,可选)。
2. 战术锁步(Tactical Lockstep)模型
2.1 回合相位(Phases)
- PLAN(计划):各客户端离线规划路径、技能与目标(可多步);
- LOCK(冻结):到时/全员确认后,服务器冻结本回合的 Command Buffer;
- RESOLVE(结算):服务器使用确定性解释器按规则推进,生成一串事件日志(Event Log);
- SYNC(广播):向客户端广播结算事件流与回合哈希;
- CLEANUP(清理):刷新状态(CD、Buff、地形效果),进入下回合。
2.2 锁步与确定性原则
- 唯一输入:仅接收“命令”(高层语义,如“单位#7:从 A→B,若遭遇敌人则停靠最近掩体并投掷手雷”),禁止坐标/属性直接写入;
- 无共享随机源:随机数来自服务器同一 RNG 种子,结算顺序固定(见 6.2);
- 无时间竞态:锁步期间不接收新命令;计划阶段的并发冲突在锁步冻结时一次裁定。
3. Command Buffer 设计
3.1 命令规范(抽象层次)
- 原子命令(Atomic):
Move(to),Face(dir),Attack(target),Ability(slot, target),Guard/Overwatch,Interact(tile) - 复合命令(Composite):
Path([p1,p2,...], stop_cond),If(cond)Then(cmdA)Else(cmdB),Sequence(cmds...) - 应急条件(Emergency):
onContact: Stop | Evade | Engage、onSight: OverwatchShot - 预算限制:行动点(AP)/能量(EP)预算必须在客户端本地校验 + 服务器冻结再校验
3.2 数据格式(示例 JSON)
{
"turn": 21,
"team": "blue",
"author": 1001,
"commands": [
{"unit":7,"type":"Sequence","steps":[
{"type":"Path","tiles":[[4,9],[5,9],[6,9]],"stop":"onContact"},
{"type":"Ability","slot":"GREN","target":[6,11]}
]},
{"unit":9,"type":"Overwatch","arc":90,"range":6}
],
"proof":{"ap_budget":6,"checksum":"8b1f..."}
}
3.3 冻结与版本
- 客户端提交
cmd_buf(ver=n);服务器冻结时写入frozen_ver=n+1; - 客户端可在 PLAN 阶段多次覆盖(以最后版本为准);LOCK 后拒绝。
4. 冲突裁定(在冻结瞬间)
4.1 冲突来源
- 同一格目标占用(两方同时尝试占同一格);
- 行动点预算边界(客户端误判导致溢出);
- 隐身/视野(对方在 LOCK 时刻改变可见性导致目标非法);
- 技能互斥(对同一载具/开关/资源竞争)。
4.2 裁定规则(俩层)
-
合法性过滤(Hard Reject):预算不足、目标非法、前置条件不满足 → 删除该命令并产生错误事件(便于回放)。
-
竞争决胜(Soft Resolve):
- 优先级序:
Overwatch / Reaction>Interrupt>Move>Ability>Interact; - 同类竞争:按单位敏捷值(AGI)降序,若并列→按单位ID升序;
- 同格抢占:赢家落位,其他命令重写为停留/最近合法格;
- 资源竞争:采用抢占锁,失败者命令标记为失败并保留在事件日志。
- 优先级序:
注:上述裁定只发生在冻结点,结算期间不插入新命令。
5. 结算解释器(Deterministic Resolver)
5.1 执行顺序
- 阶段化执行:
Move → Reaction/Overwatch → Ability/Attack → DOT/环境 → EndStep - 同阶段批处理:按固定排序键(turn, phase, unit_id)执行,确保每次回放一致。
5.2 关键子系统
- 移动:A* 预烘焙 + 每步占用检查;若撞入 Overwatch 触发反应射击(Reaction)并可能中断剩余 Path。
- 射击命中:
hit = f(weapon_acc, range_penalty, cover, height);随机从统一 RNG 抽取; - 伤害:
dmg = base * (1 - armor/(armor+K)) + crit_mult; - 状态:
AP/CD/效果在清理阶段统一刷新; - 能见度:FoW(视野)每步增量更新,影响目标合法/命中。
5.3 事件日志(Event Log,权威)
事件是不可变、可重放的唯一事实来源。典型事件:
TurnStart, CmdAccepted, CmdRejected, MoveStep, EnterTile, LeaveTile,
ReactionShot, AbilityCast, Hit, Miss, Crit, Damage, Kill, Knockback,
ApplyBuff, RemoveBuff, TileChange, TurnEnd, Hash
6. 随机确定性与哈希
6.1 RNG
rng = PCG(seed = match_seed ⊕ turn ⊕ salt);- 同一结算时段内严格按事件执行顺序取随机数;禁止条件分支漏取。
6.2 一致性哈希
- 每回合末生成
hash(turn) = H(∑ stable_fields_of_entities, rng_cursor, event_crc); - 客户端对齐:若
hash_mismatch→ 触发强制恢复(见 9)。
7. Fog-of-War 与信息裁剪
- 可见性:
visible(tile) = ∃ unit in team s.t. dist(unit,tile) ≤ sight(unit) and not blocked; - 裁剪:服务器仅向每队下发其可见子集的状态与事件(例如隐藏敌方真实血量、技能 CD)。
- 回放:对赛后全景回放,使用全景事件流;对玩家个人回放,使用其视角裁剪事件流(保证体验与对局时一致)。
8. 网络协议与时序
8.1 消息(WS)
// 客户端 -> 服务器:提交命令
{ "t":"cmd", "turn":21, "ver":3, "team":"blue", "author":1001, "commands":[...], "sig":"ed25519..." }
// 服务器 -> 客户端:冻结通知
{ "t":"locked", "turn":21, "frozen_ver":4, "deadline_ms":0 }
// 服务器 -> 客户端:回合结算事件流(分片)
{
"t":"resolve","turn":21,"seq":1,"more":true,
"events":[{"k":"MoveStep","uid":7,"to":[6,9]}, {"k":"ReactionShot","src":12,"dst":7,"hit":true,"dmg":4}]
}
// 服务器 -> 客户端:回合尾哈希
{ "t":"hash", "turn":21, "hash":"3b29...fa" }
8.2 计划阶段时间
-
默认 20–30 秒,支持提前确认;超时未确认则:
- 使用上一版本命令或
- 启动保守 AI(Hold/Guard)。
9. 断线恢复与观战
9.1 恢复流程
- 客户端上报
last_turn_ack; - 服务器返回最近关键快照
KF(k)+events(k+1..T_now); - 客户端重建至
T_now并比对hash@T_now; - 不符则回退到更早
KF(k-1)或直接覆盖全量快照。
9.2 观战与复盘
-
观战:只能接收只读事件流,不可发命令;
-
复盘:
- 时间轴跳转:按回合号和事件序号定位;
- 分支/悔棋实验(训练模式):从任意回合快照分叉,写入平行日志(branch_id)。
10. 数据结构与存储
10.1 关系表/集合
matches(id, seed, map_id, rule, created_at, result)turns(match_id, turn, frozen_ver, hash, snapshot_uri)commands(match_id, turn, team, author, ver, payload, sig)events(match_id, turn, seq, payload)spectators(match_id, user_id, joined_at)
10.2 快照
- ECS 压缩(仅稳定字段:位置/AP/HP/CD/Buffs/地形状态)
- 存储为
zstd二进制,服务器缓存最近 N 个快照。
11. 规则/数值(示例)
| 项目 | 默认 |
|---|---|
| 单位 AP | 2/回合 |
| 移动成本 | 1 AP / 3 格(可拆分) |
| 远程射击 | 命中基础 65%,距离惩罚每 3 格 −5%,遮蔽 −20% |
| 暴击 | 15%,倍率 ×1.5 |
| Grenade | 扇形 3 格,固定 3–5 伤害,CD 2 回合 |
| Overwatch | 锁定 90° 扇形、射程 6;对穿越扇形的敌触发一次反应射击 |
12. 客户端交互与 UX
- 计划阶段 UI:路径预览(AP 消耗标注),技能范围预览,冲突/非法目标即时标红;
- 锁步提示:顶部显示所有人“已准备/未准备”,冻结倒计时;
- 结算播放:事件逐条动画化(可 1×/2×/快进),同时显示回合日志面板;
- 信息不对称:雾外单位以“问号”/影子表示;
- 悔棋/训练:在训练房开启“从回合 N 分支”。
13. 反作弊与安全
| 风险 | 说明 | 对策 |
|---|---|---|
| 命令注入/篡改 | 客户端伪造命令 | 命令签名(Ed25519)、服务器枚举验证 |
| 透视/数据外泄 | 客户端渲染雾外信息 | 服务器裁剪数据;加密通道 |
| RNG 操控 | 客户端预测随机 | 单源 RNG(服务器)、事件顺序消费 |
| 重放攻击 | 旧命令重放 | 回合号 + 版本号 + nonce 校验 |
| 时间作恶 | 超时后提交 | 冻结后拒绝;只接受 PLAN 期内提交 |
14. 性能与扩展
- 结算复杂度:单位数 U、地形格 G、事件 E;解算阶段
O(U log U + E); - 缓存:快照内存缓存近 3–5 回合;事件分片批量发送(<= 8KB/片);
- 并发:房间粒度调度;每回合解算 p95 < 40ms(U≤16)。
- 多种模式:1v1、2v2、PVE(敌 AI)、草案/排名赛(加 Elo/Glicko-2)。
15. 测试与验证
- 确定性回放:相同
seed + commands重放 100 次哈希完全一致; - 裁定边界:同格竞争、Overwatch 触发次序、多技能互斥;
- FoW 正确性:随机地图上可见集与期望一致;
- 断线恢复:拔网线 10s → 2s 内回到
T_now并 hash 对齐; - 安全:拒绝超时命令、重放命令、非法 budget;
- 负载压测:并房 1k,事件吞吐与快照 IO 不丢帧。
16. 参考实现骨架(要点)
16.1 Go:回合循环
func (r *Room) RunTurn(t int) {
// 1) 收集命令(PLAN期已入库)
cmds := r.FetchFrozenCmds(t)
// 2) 冲突裁定
resolved := r.Adjudicate(cmds) // 过滤+优先级+抢占
// 3) 结算
ev := r.Resolver.Resolve(t, resolved) // 产出事件序列
// 4) 快照、哈希、广播
snap := r.World.SnapshotStable()
h := StableHash(snap, ev, r.RNG.Cursor())
r.StoreTurn(t, snap, ev, h)
r.BroadcastEvents(t, ev)
r.BroadcastHash(t, h)
}
16.2 TS:客户端计划与提交
const buf = new CommandBuffer(turn, team, uid);
buf.sequence(7)
.path([[4,9],[5,9],[6,9]], "onContact")
.ability("GREN", [6,11]);
buf.overwatch(9, 90, 6);
ws.send(JSON.stringify({ t:"cmd", turn, ver: buf.ver, team, author: uid, commands: buf.serialize(), sig: sign(buf) }));
17. MVP 清单
- 战术锁步三相:Plan/Lock/Resolve
- Command Buffer(原子 + 组合 + 预算校验)
- 冲突裁定(合法性过滤 + 竞争优先级)
- 确定性解释器(移动→反应→技能→DOT→清理)
- 事件日志 + 关键快照 + 回放
- FoW 裁剪与视角一致回放
- 断线恢复(对齐哈希)与观战
- 基础反作弊(签名/nonce/回合号)
18. 迭代路线
| 版本 | 目标 | 内容 |
|---|---|---|
| v0.1 | 最小对战 | 固定地图、Move/Attack、Plan/Lock/Resolve 跑通 |
| v0.2 | 冲突与反应 | 同格竞争、Overwatch、Reaction 中断 |
| v0.3 | 技能与FoW | AoE/投掷、视野裁剪、遮蔽与命中模型 |
| v0.4 | 回放/分支 | 平台化回放、分支实验、训练房 |
| v1.0 | 稳定/排位 | Elo/Glicko-2、对局统计、断线恢复优化 |