本节摘要:多人虚拟世界的第一道题是架构——状态放在哪里、谁说了算;第二道题是同步——各端怎么对"谁在哪"达成一致。本节比较权威服务器与对等网状的取舍,拆解时钟、时间戳、插值与预测回滚的完整机制,并给出状态同步的节流设计方法。
进入协同防线第一站。第六章支柱页"瞬移的合作伙伴"事故里,残影与跳动的病根有两层:架构决定了状态从哪来,同步决定了状态何时到、对不对。本节把两层一起讲清——它们合起来决定了联机世界里"别人的一举一动"是连续的还是痉挛的。
权威服务器模式:服务器持有世界状态的唯一真实副本,各端只提交意图("我要移动""我要抓取"),服务器裁决后广播结果。好处是一致性与公平性(作弊面收窄,关键状态无法被客户端伪造)、故障半径清晰;代价是服务成本与单点风险,且远端看到的一切都多一个往返延迟。对等网状模式:各端直接互发状态,延迟最低、无服务器成本;代价是时钟难齐、作弊面全开(每个客户端都可声称任意状态)、扩展性差(全互连的连接数随人数平方增长)。
工程主流是混合:权威服务器管关键状态(位置判定、所有权、物品转移),高带宽低敏感的数据(语音、手势流)走对等或转发通道。支柱页拓扑图里那句"混合方案"就是本节的落点:把"需要裁决的"与"只需要转发的"分开,各用各的通道。
不同设备的时钟互不相认,毫秒级的偏差就能让"谁先按的按钮"各执一词。解决方案是给事件盖时间戳——用统一的时钟域。做法各端向服务器对时(记录往返延迟,推算偏移量),事件携带服务器时间域的时间戳传输;各端收到后按时间戳重排、按延迟补偿回放。支柱页事故里的"扳手位置各说各话",一半的病根就是两端各自用本地时间解读对方的状态包,时序自然对不上。
时间戳之外还有两件配套。序号:每个状态包带单调递增序号,乱序到达可识别、重复可丢弃。确认与重传策略:关键事件(抓取成功)要可靠传输,高频状态(每帧位姿)走不可靠通道——丢一帧位置无所谓,下一帧马上就到,可靠重传反而制造延迟堆积。
远端状态包以几十毫秒的间隔离散到达,直接套用会让远端角色瞬移。插值的思路是故意延迟一点回放:本地维护一个固定深度(比如两个包间隔左右)的缓冲,回放位置取"当前时刻之前"的两个快照做插值。代价是远端动作整体延迟于真实,收益是绝对平滑——第六章支柱页的结论"稳定压倒真实"在实现层就是这个缓冲深度。
插值深度的两难:浅了缓冲见底(上游稍有抖动就无帧可插,回退到瞬移),深了延迟累积。成熟做法是自适应缓冲:监测包间隔的抖动方差,波动大时自动加深、平稳时收浅,配合迟滞避免深度震荡。
远端角色回放管线(伪代码口径) 收到状态包 { seq, ts_server, pos, rot }: 存入缓冲区,按 ts_server 排序 更新抖动估计 jitter = 均值包间隔的方差 回放每帧: replay_ts = 本地对齐的服务器时间 - 缓冲深度(基线 + jitter自适应) 取缓冲区中夹住 replay_ts 的两个快照 pos = lerp(前快照.pos, 后快照.pos, 插值系数) 若 replay_ts 超出最新快照(缓冲见底): 进入外推模式(速度外推),并在新包到达时缓慢收敛回来
插值解决了"看别人",预测解决"看自己":本地玩家自己的动作不能等服务器确认,客户端立刻本地预测执行(立即移动),服务器裁决结果回来后比对——一致则继续,不一致则回滚到权威状态并重放未确认输入。这就是第六章支柱页里"递扳手"类所有权问题的标准答案:谁持有(谁抓着)谁有预测权,其余人看插值结果。
预测的范围要克制:只预测本地玩家自己的化身与所持物件,不预测他人——他人的一切交给插值,预测他人等于在两个不确定上叠加不确定。回滚的视觉处理也有讲究:大幅回滚表现为本地瞬移,是眩晕刺激源,所以校正要渐进收敛(朝权威状态平滑靠近而不是瞬跳),与第三章的缓入缓出哲学一脉相承。
💡 关键直觉:同步的本质分配是"自己的要快(预测),别人的要稳(插值)"。把这两条通道的优先级做反了,世界就会既卡自己又跳别人——支柱页事故的两个症状正是各反了一条。

全量全频的状态同步撑不过几个用户:每帧全量发位置、旋转、速度、动画参数,十人房间就能挤爆上行。节流三板斧:降频——位姿包频率压到渲染帧率的一半以下,靠插值补平滑;分区——只同步视野内或同空间的用户,远处降频或冻结;增量——只发改动的字段,动画参数按事件触发而非每帧。节流的验收标准是体感:看远端角色行走是否平滑、转身是否连贯,而不是看发包数——节流过度会以"机器人式移动"的形式反馈到体感。
| 数据类型 | 通道 | 频率 | 可靠性 | 说明 |
|---|---|---|---|---|
| 本地位姿 | 不可靠 | 渲染半频 | 丢帧可弃 | 插值平滑兜底 |
| 抓取/所有权事件 | 可靠 | 事件驱动 | 必达 | 带时间戳与序号 |
| 语音流 | 不可靠 | 连续 | 丢包容忍 | 抖动缓冲按语音调 |
| 动画状态 | 事件 | 变化时 | 少量重传 | 增量同步 |
对时听起来一步到位,实操有三个坑。坑一:只对一次时。网络延迟本身在漂移,偏移量要周期性重测,且重测期间用平滑收敛(直接跳变时钟会让插值缓冲瞬间错乱)。坑二:用对称假设。往返延迟的上下行未必对称,取半程当单程在非对称链路上偏差明显;可靠的做法是用时间戳交换的多次采样取最小往返样本(最小往返最接近纯传播时间)估算偏移。坑三:忽略时钟速率差。长时间会话里两台设备的晶振频率有微小差异,会话越久偏移越大——对时周期要按会话时长设计,长会话加入速率漂移的缓慢校正。三个坑的工位忠告是同一句:时钟是基础设施,要像对待帧率一样持续监控它的健康度。
同步策略要按会话规模分档设计。小房间(几人同场):全量互相同步,插值缓冲可以宽松,人人可见人人。中房间(十人级):引入兴趣管理——按距离与视锥裁剪同步对象,远处的人降频到"有人在那"的粗粒度表示;语音按分组建群。大房间(数十人以上):粗粒度分层(同区精确同步、邻区中等、远区统计化呈现),关键交互(发言、演示)走独立的注意力通道,不指望全量一致。规模分档的设计入口在立项时:内容玩法期望的"最多同场人数"决定同步架构,反过来,同步预算也否决过不少"百人同场大厅"的玩法提案——这是架构师与设计师早期就该摊开谈的接口。
同步架构还有一层常常后补的基础设施:状态记录。两份产出各有用处。状态快照(周期性存下权威世界状态)用于断线重连——重连端不用从头追事件流,拿最近快照加增量事件即可回场;也用于故障归因——"那一刻世界是什么样"不再靠各方回忆。事件日志(带时间戳的关键事件序列)用于审计与回放——第六章支柱页的协作事故复盘、8.3 的行为责任认定,都依赖这份日志。设计的两难与数据边界相扣:日志越全越可追溯,也越敏感;折中方案是分级留存——技术事件(连接、同步错误)留技术窗口,行为事件(交互、发言引用)按 8.3 的窗口化与授权口径留存。这套存根机制在单机时代叫存档,在协同时代是公共基础设施,立项时就要进架构图。
架构与同步立住了,但延迟本身无法归零——下一节讲延迟补偿与传输质量:缓冲怎么调、协议怎么选、网络抖动怎么保底。