Skip to content

预测、补偿与纠正 ​

Category ​

server / sync 子树。节点定位:在「等服务端迟钝」与「信客户端失控」之间搭桥——预测、补偿、纠正的边界:哪些状态能抢时间,纠正不只有瞬移回去。

Definition ​

如果完全等服务端结果再表现,实时游戏会显得迟钝;如果完全相信客户端,本地体验是顺了,裁决和反作弊又会崩。预测、补偿与纠正,就是在这两端之间搭桥的一组工程机制。它的难点不在概念,而在边界:哪些状态能抢时间,哪些状态必须等权威,服务端回来以后又如何把本地前馈重新收拢。

Problem ​

难点不在概念在边界:哪些状态能抢时间、哪些必须等权威、服务端回来后本地前馈怎么收拢。边界不清,实时手感与裁决反作弊会同时塌——帧同步和状态同步里的预测还不是一回事。

为什么必须抢时间 ​

人在操作时,对延迟的感知并不会等服务端给你面子。只要角色位移、射击、释放技能、换弹、近战命中和镜头反馈这些东西都要等一轮网络往返,手感一定会塌。

所以同步体系必须同时回答三件事:

  • 客户端先演什么。
  • 服务端怎么算真结果。
  • 真结果回来后怎么纠偏。

这三件事缺一不可。

预测、补偿、纠正分别在做什么 ​

  • 预测:客户端根据本地输入和已知状态,先推进一部分未来表现或逻辑。
  • 补偿:系统通过输入窗口、插值、服务端回溯或其他机制,减小网络延迟对体验和判定的影响。
  • 纠正:服务端权威结果回来后,把客户端和服务端的状态偏差重新收拢。

很多讨论把三者混成一句“客户端预测”,这是不够的。预测解决的是先反馈,补偿解决的是延迟本身,纠正解决的是前两者必然带来的偏差。

不是所有状态都应该预测 ​

比较稳妥的实践通常会把状态分层:

  • 适合大胆预测:本地移动、朝向、起手动作、部分非关键动画反馈。
  • 适合有限预测:技能起手、投射物出手、近战挥砍开始、局部命中特效预播。
  • 更适合权威确认:资源扣除、死亡、击飞击退最终落点、关键控制、结算相关状态。

越靠近胜负裁决、越容易被作弊利用、越依赖复杂碰撞和多人交互的状态,越不适合完全本地决定。

常见的预测与补偿手法 ​

本地输入前馈 ​

玩家按下移动键,本地角色立即开始动,服务端稍后给出权威位置。最常见于位移和手感敏感的动作反馈。

动作和技能前摇预播 ​

客户端在发送施法请求后,先播放起手动作和起手特效。服务端若判定失败,再做打断或回退。

远端对象插值与外推 ​

别人的角色通常不是预测“他要干嘛”,而是对最近两个权威状态做插值,必要时做短时间外推。这不是本地操作预测,但同样是在抢时间。

服务端回溯判定 ​

射击和某些命中判定里,服务端可能按客户端上报的射击时间回看历史位置,以补偿链路延迟。这属于补偿,不是客户端预测,但常和预测一起出现。

纠正不是只有“瞬移回去” ​

服务端结果回来以后,纠正有很多层级:

  • 硬纠正:直接把客户端状态改成服务端状态,简单粗暴,但体验容易炸。
  • 平滑纠正:用插值在短时间内收敛到权威状态,视觉更自然,但实现更复杂。
  • 分类纠正:不同状态采用不同方式,例如位置平滑回收、死亡立即权威、生效中的技能按规则中断。
  • 回滚重演:在更重的方案里,客户端回滚到过去某个权威点,再重放后续输入。

没有哪种方案总是更好,关键是别把所有偏差都当成一种偏差处理。

帧同步和状态同步里的预测并不一样 ​

在服务端权威状态同步里,预测通常是为了弥补客户端到服务端这段 RTT,焦点是本地手感和服务端纠正。

在帧同步体系里,系统更常依赖领先帧、输入缓冲和统一帧号来吞掉一部分延迟。此时预测空间通常更小,但也不是完全没有。有些项目仍会对动作起手、镜头反馈和部分局部表现做前馈。

所以“做帧同步就不用预测”并不准确;更准确的说法是,帧同步把更多延迟处理放进了输入窗口和共享帧推进,预测边界通常比服务端状态同步更保守。

服务端补偿最容易引发什么争议 ​

补偿如果设计不清楚,会直接变成玩家口中的“明明躲开了还中”。最典型的是:

  • 服务端为了照顾开枪者体验,回溯受击者过去位置。
  • 客户端本地已经看到自己走开了。
  • 两边都觉得自己没错。

因此,补偿策略必须和玩法匹配。竞技性越强,越要提前定义清楚:

  • 最多允许回溯多久。
  • 哪类技能允许回溯判定,哪类不允许。
  • 位移、闪现和无敌帧如何参与判定。
  • 日志里能否复原当时的时序证据。

补偿不是“越多越好”,而是要让体验和可追责都说得通。

一套更现实的状态分层 ​

比较稳妥的项目里,常见会把状态分成三层:

  • 本地体验层:优先快,允许短时间偏差,例如动作起手、镜头和部分位移前馈。
  • 权威裁决层:必须准,宁可慢一点也不能错,例如伤害、死亡和关键控制。
  • 表现收敛层:负责把前两层之间的差异做平滑处理,例如位置插值、效果取消和补播命中。

一旦这三层没有拆开,系统就很容易把“表现先播”和“裁决先算”混成一件事。

预测链路必须配日志 ​

预测相关问题最怕没有证据。至少应该记录:

  • 客户端发起输入的本地时间和逻辑帧。
  • 服务端收到输入的时间和处理帧。
  • 客户端本地预测结果。
  • 服务端最终权威结果。
  • 纠正触发时机与纠正幅度。

否则玩家说“我明明已经滚过去了”,工程师只能靠录像肉眼猜。

常见误区 ​

  • 把预测范围无限扩张,最后连关键裁决都在本地先演一遍,服务端变成形式上的确认器。
  • 服务端结果一回来就强行回写,不做平滑和分类,结果体验像系统在和玩家抢控制权。
  • 只盯本地手感,不看补偿策略给公平性和仲裁带来的副作用。
  • 没有时序日志,预测链路出了问题以后只能靠猜。

预测、补偿与纠正真正要解决的,不是“延迟怎么藏”,而是怎样在不丢掉裁决权和可追责性的前提下,把延迟从玩家手里偷走一部分。

实战案例:一次「明明躲开了还中」的回溯判定争议排查 ​

案例为教学示例(非摘自真实项目),数字经过取整简化。它把本页「服务端补偿最容易引发什么争议」「纠正不是只有瞬移回去」「一套更现实的状态分层」「预测链路必须配日志」用到深水区:补偿窗口本身不是 bug,窗口与客户端可见状态不在同一口径才是——这是实时对战的服务端裁决排查,也是「补偿不是越多越好,要让体验和可追责都说得通」的完整展开。

背景与现象:一款 PVP 射击 + 位移闪现的手游,日活 20 万、日对局 60 万场(场均射击 12 次)。服务端命中判定按客户端上报的开火时间做回溯判定,回溯窗口 200 毫秒;位移技能带 120 毫秒无敌帧。上线首周各项都平,直到新增两个带位移的射手技能,客服「明明躲开了还中」的工单开始爬坡,稳定在日 1800 单,而同期命中率口径一点没变——这正是最难查的那种信号:业务指标全绿,玩家体感在崩。

第一步:现象与口径——先把「明明躲开了」变成可统计的工单标签 ​

争议口径:客服工单中,判定为「客户端画面已表现为躲开、服务端仍记命中」的单量。日 1800 单,占全日命中的 0.1%——比例看着不高,但它同时是仲裁风险:这类单子一旦坐实,裁决权就成了玩家眼里的玄学。

命中率口径:服务端回溯命中数 / 射击请求数 = 25%(全日射击 720 万次、命中 180 万次)。先把两个口径并排摆出来,才能说明问题不在命中判定「准不准」,而在它「凭什么这么判」。

第二步:分层归因——按三层状态把争议拆开 ​

按本页的状态分层逐层核对争议单归属:

text
争议工单 1800 单/日  归属
  回溯口径(无敌帧未排除)    88%  →  1584 单
  硬纠正瞬移(观感争议)        9%  →   162 单
  其他(表现/状态不同步)        3%  →    54 单
  • 本地体验层:无异常。起手动作、镜头和位移前播都在按设计走,玩家确实本地就躲开了——这坐实了争议单的口径没问题。
  • 权威裁决层:88% 的争议集中在这里,全部指向同一条规则:回溯取历史位置时,无敌帧不在排除集合里。玩家在客户端按下闪现的同一时刻,服务端正在回看 200 毫秒前的受击方位置。
  • 表现收敛层:9% 是硬纠正造成的观感争议——服务端结果一回来直接回写位置,玩家看到的是瞬移。这不是错判,但会把本来可接受的判定变成投诉。

时序对照(同一场射击,开火方 P99 RTT 80 毫秒):

text
  客户端  t=0ms     按下射击,同时闪现进入无敌帧(120ms)
  服务端  t=80ms    收到请求,回看 200ms → 取 t=-120ms 的受击方位置
  判定    t=80ms    命中:t=-120ms 受击方在弹道上,无敌帧未进入排除集合

第三步:根因——窗口比玩家能看见的躲闪提前量还长 ​

根因只有一条,但它有两个咬合在一起的参数:回溯窗口 200 毫秒 > 无敌帧 120 毫秒 + P99 RTT 80 毫秒。也就是说,服务端为了照顾开枪者体验,把判定窗口开到了玩家根本来不及看见的那一段;而恰恰在这一段里,客户端已经用无敌帧作出了「我躲开了」的可见承诺。两边都没写错代码,是判定口径与客户端可见状态没有对齐。

配套的次生问题是无据可查:争议单要复盘时,团队既没有客户端上报的开火时间与逻辑帧,也没有服务端的处理帧与纠正幅度,仲裁准确率只有 61%,剩下 39% 只能靠录像肉眼猜,最后统计成一句「玩家感觉有点问题」。

第四步:处置与回填——三处改动,窗口按 P99 对齐 ​

  1. 回溯窗口 200 毫秒收到 80 毫秒,与 P99 RTT 对齐:判定只回看链路已消耗掉的那段,不再回看玩家看不见的未来。
  2. 无敌帧纳入回溯的排除集合,位移、闪现、无敌帧参与判定的方式逐技能写进判定规则表,不再由回溯实现默认继承。
  3. 硬纠正改平滑纠正:位移平滑收敛、死亡与关键控制仍立即权威,分层照本页的三层来。

复核账:争议工单日 1800 单 → 日 180 单,争议率 0.1% → 0.01%;命中率 25% → 24%(窗口收窄后回溯命中略降,这部分下降是主动的);仲裁工单复核准确率 61% → 97%,依据是补齐五项时序日志后,争议单可逐条复原时序证据。

回填清单:

回填项动作回填处
状态三层拆分技术评审模板把权威裁决层与表现收敛层分栏评审,杜绝「表现先播 + 裁决先算」混成一件事一套更现实的状态分层
补偿口径与玩法匹配逐技能判定规则表:回溯上限、是否允许回溯、位移/闪现/无敌帧如何参与判定服务端补偿最容易引发什么争议
纠正层级规范位移平滑收敛、死亡与关键控制立即权威,写成纠正规范而非各处临时判断纠正不是只有「瞬移回去」
五项时序日志战斗日志全量留存,争议单 100% 可复原时序:客户端发起输入的本地时间和逻辑帧、服务端收到输入的时间和处理帧、客户端本地预测结果、服务端最终权威结果、纠正触发时机与纠正幅度预测链路必须配日志

案例的三个教训 ​

  1. 回溯窗口不能超过玩家可见的躲闪提前量。窗口按 P99 RTT 定档,不按「手感更好」拍脑袋;本页说「最多允许回溯多久」要提前定义,定义时就该照链路指标定,而不是照愿望定。
  2. 权威判定必须与客户端可见状态同口径。客户端敢用无敌帧对玩家作出承诺,服务端就必须把它算进排除集合;承诺与判定不同口径,裁决权就会变成玄学。
  3. 争议率和命中率必须一起看。命中率 25% 纹丝不动时,公平性已经在塌——「手感和公平」的指标缺一个,监控就只剩半只眼睛。

Used By ​

预测补偿方案设计;状态分层评审;服务端补偿争议的裁决依据。

  • Colyseus(commit e620123):补偿与纠正的轻量实现——patch 节奏下用 Rewind.record 记录快照供客户端插值复现(packages/core/src/Room.ts:1416-1420),不可靠通道字段走数据报且排在可靠 patch 之后(Room.ts:1396-1403)

关系链:抢时间 → 三件事分工 → 状态可预测边界 → 常见手法 → 纠正形态 → 帧/状态差异 → 状态分层。上游:sync/02。下游:client/01(客户端预测面)、networking 树。

Reference ​

本页为工程经验归纳;页内实战案例为教学示例(数字取整简化)。

游戏知识体系