快照同步
全库目录221
本文目录16
远端输入尚未到达,本地通常只能显示已收到的状态。快照同步保留一段历史,在两个确定快照之间插值;压缩只相对接收端已确认基线,丢失时用完整状态恢复。
快照身份与差量恢复
快照包的发送时间与每个实体最后一次被观察的时间可能不同。服务器每50毫秒发送一包,并不意味着每个实体都以20赫兹更新。带宽调度可能连续十包都省略远处实体;若客户端把每包时间写成该实体的新样本时间,旧状态就会不断变得“新鲜”,外推上限也永远不会触发。
因此先区分两份数据。解码基线是某个包ID对应的完整复制记录表,用于恢复之后的差量;显示历史是每个实体实际收到的运动样本,用于在服务器时刻r求姿态。记录表可以完整,里面的实体样本仍有不同年龄。
字段、身份与观察时间
包头包含服务器纪元、包ID、发送时间、基线ID和基线窗口代次。实体键为(实体ID, 生成代数),运动记录包含连续段ID、观察时间、量化位置、旋转及速度。位置、旋转和速度组成一次运动观察;字段相对基线省略,表示在本次明确发送的运动观察中该字段与基线相同。整条实体运动记录未发送,则表示没有新的运动观察,沿用原观察时间。生命值等独立字段的更新也不能刷新运动观察时间。
服务器在同一个模拟时刻冻结被调度实体的运动字段,再相对基线编码;不能把1.0秒的位置和1.1秒的速度拼成1.1秒的样本。相同值但确实再次观察到的实体,可以发送含新观察时间的空字段掩码。这样客户端能区分“仍停在那里”和“这次没有带宽发送”。
生成与销毁通过可靠控制消息携带生效时间和生成代数。完整生成给出初始状态;销毁建立墓碑,晚到的旧代数记录不能复活对象。运动包提前到达时,可在有界等待队列中等生成消息,超时请求完整状态。此时包可以保存为解码基线,但显示层尚不创建实体;基线确认只表示记录表已正确恢复,不表示每个对象都可显示。
完整记录表的重建
位置范围为[−100,100]米、量化步长0.01米时,编码q=round((x+100)/0.01),20001个合法值需要15位,舍入误差至多0.005米。超范围时换更大范围编码或区域原点。差量作用在量化整数上,最后才还原米,避免两端反复浮点累加。
解码过程先验证纪元、窗口与基线是否存在,再复制基线记录表,对包内实体应用字段掩码和整数差量,检查身份、范围与段ID,最后把新表连同包ID保存为不可变基线。任何字段失败都拒绝整个包,不确认一个只恢复了部分字段的基线。完整包直接建立记录表;它同样保留每条记录自己的观察时间。
例如基线42中,A和B的观察时间均为1.00秒,位置整数分别为10000和11000。43号包在1.05秒发送,仅含A的新观察:时间1.05、位置差20、其余运动字段与42一致。43的完整表中A为10020、时间1.05;B仍为11000、时间1.00。44若也相对42编码,A在1.10秒的位置差应为40,恢复为10040;接收端不能在43上再加40得到10060。
显示历史只追加43里A的实际新观察,B没有1.05秒的新样本。迟到42可以补基线或历史空隙,但不能覆盖更晚实体状态。传送切换段ID时发送新段完整运动状态,跨段不应用运动差量。进入AOI的新生成代数也先完整生成,不能继承同ID旧代数的基线字段。
基线确认与退休
服务器只引用客户端确认保存的基线。接收端只有40、包43却引用42时,拒绝43并请求完整状态;完整44保存并确认以后,服务器才可改用44。
缓存上限需要双方共同执行。采用32槽窗口时,接收端申请退休旧基线,发送端提交新的窗口代次并保证后续包只引用新窗口;接收端拿到确认后先拒绝旧窗口代次,再释放旧基线。在途旧包由窗口代次识别,不能查询已释放的记录。等待协商期间槽位仍计入未退休基线;达到32槽就停止确认新基线并请求窗口推进,保留空间后再恢复。
窗口推进保留至少一份双方已确认的完整记录表作为锚点。例如32槽已满,双方保留42,接收端请求退休其余31份;发送端把新窗口w+1的可用基线集合先限制为{42},以可靠控制消息提交这份集合及窗口代次。接收端验证42仍完整,切换到w+1、拒绝w旧包并释放其余槽,再确认窗口控制。这个过程不依赖第33份基线的确认,因而满槽仍能推进。发送端在窗口确认前暂停新的差量发送,或只发送不会引用已申请退休表的旧窗口包;确认后才能用w+1和42生成新差量。新窗口包若因传输乱序先于控制到达,接收端暂存于有界队列或丢弃,不能自行猜测窗口已经提交。控制重发以代次去重,旧控制不能回退窗口。
另一种实现给基线固定租约。租约内接收端保持基线,过期后服务器发送完整状态,直到新基线确认。两条方案都把“发送端还能引用多久”与“接收端能保留多久”接在一起,才能给内存一个真实上限。
远端实体插值
其他玩家通常不预测输入,而是把显示时间故意放在估计的当前服务器时间之前一小段插值延迟,在两个快照之间插值。缓冲越大越平滑,但画面越旧。
短暂缺包时可按速度外推或航位推算,时间过长则冻结或切安全状态,避免无限漂移。位置和旋转的插值要与传送、死亡等离散事件区分,事件不能被当成普通连续值穿过去。先建立全局播放时间,再由后文的实体求值器判断每个对象是否能在该时间显示。
插值缓冲的取时刻
插值先建立时钟目标serverEstimate-interpDelay,interpDelay按快照频率与目标丢包容忍度设置。实际取时r由下一节的播放状态推进。包时间轴只负责推进r;渲染随后在每个实体的运动历史中寻找夹住r的两个样本,参数,位置线性插值、旋转球面插值。某个实体缺少右样本时,执行后文的逐实体外推与有效性规则。
快照到达节奏抖动时缓冲会耗尽或溢出:耗尽时可限时外推或停留在最新状态;积压时可小幅加快显示时间推进。一份明确的自适应规则是保存最近64个能推进缓冲最新服务器时间的非重复有效快照的本地到达间隔,每秒计算其95百分位G,取目标延迟Dtarget=clamp(G+T,2T,max(2T,0.25秒)),T为服务器发送周期。50毫秒周期下,G=50毫秒得到100毫秒延迟;G=150毫秒表示队列抖动或连续丢包留下大间隙,目标升至200毫秒。
旧时间戳的乱序包可以补齐历史或基线,但不更新这份右端样本到达统计;否则一串迟到旧包会制造“仍持续来包”的假象。用到达间隔而非只用RTT,能直接观察显示缓冲多久没有右端样本。参数上限代表可接受画面年龄,超出的突发依靠有限外推或冻结处理,无法保证任意断网仍平滑。
单调播放与缓冲恢复
延迟D每秒最多改变0.01秒,时钟目标为serverEstimate-D。播放时间r独立保存,它表示当前画面实际消费的服务器时刻;开火消息引用的也是这份r,而非另算的目标时间。
正常播放取target=min(serverEstimate-D,latest-T),T是发送周期,给最新样本留下至少一个周期余量,再算rate=clamp(1+2*(target-r),0.95,1.05),候选时间为candidate=r+rate*renderDt。系数2单位为每秒,目标落后时减速、领先时加速,正常段不会倒放。
稳定网络延迟过大时,latest-T会限制时钟目标,实际画面年龄也随之增加,不能把D上限误当成任意网络下都能保证的实际年龄。
外推上限与冻结
包时间轴允许从最新有效包时间最多前进E=0.1秒,实际取时为rNext=min(candidate,latest+E)。到达这个上限时同时冻结r和该时刻的显示姿态,进入Frozen状态;r不会在画面静止时继续增加。各实体能否在r求姿态仍由自身样本决定,包时间轴的余量不能延长某个实体的外推期限。
否则断网一秒后画面仍停在旧位置,开火却提交一秒后的r,历史命中查询便不再对应所见状态。冻结期间还保留夹住r所需的左样本和最新样本,不能只按当前墙钟把它们淘汰。
已知传送/重生事件在Te切断某个实体的连续段时,该实体旧段的外推上限还要截在Te;r达到Te就切换它的新段离散状态,不能用两段位置插值。这个事件不截断其他实体共享的包时间轴。若事件迟到且Te已小于当前r,立即应用新段并做一次显示校正,不把r倒退到Te补播。
缺少新段位置时把该实体隐藏或显示为明确的无效占位,标记当前视图viewReady=false并请求该段完整状态,禁止用旧段姿态继续显示为有效目标。依赖这个视图的历史命中请求暂停,也不缓存成恢复后补发的射击;取得完整状态并能在当前或更晚r求值后,登记新的呈现段与有效视图再恢复。事件处理状态与原段外推姿态分别记录,所见时刻、呈现段和有效性一起交给延迟补偿命中校验。
缓冲恢复与重同步
短缺包恢复时,Frozen等待latest-r≥Dresume,本文取Dresume=max(2T,0.1秒),并且至少两份同纪元样本能覆盖r,才恢复播放。
单个新包只把最新时间从0.9推进到1.05,而r已冻在1.0时,余量0.05不足,继续冻结;最新时间推进到1.2,余量0.2足够,恢复帧仍从r=1.0开始,下帧再按正常速率推进。恢复时用新缓冲在同一r重新求姿态,先前的速度外推与权威插值不一致可产生一次位置校正;它改变姿态估计,不把逻辑时间偷偷推到没有右端样本的位置。
等待超过R=0.5秒,或所需左样本已经丢失时,进入Waiting重同步状态,暂停依赖所见时间的命中请求。收到至少两个有效样本后,在同一服务器纪元内选择新锚点rNew=clamp(target,max(earliest,rOld),secondLatest),前提为secondLatest≥rOld,这样锚点不倒退且至少有最新样本作为右端。尚不满足就继续等待;不能把未来r强行夹回旧时间。
重建时递增本地播放代次、清理旧代次的待处理显示结果,按新锚点重新求姿态并恢复请求。服务器纪元改变则建立独立的新时间轴,旧纪元样本和请求全部失效。
例如r冻在1.0秒,等待超时后收到1.8、1.85、1.9三份样本,而目标已到2.1秒,新锚点取1.85而非2.1;当前能在1.85与1.9之间播放。若最新只到0.99,则没有不倒退的锚点,仍等待。持续积压且abs(target-r)>R时也进入这条显式重同步路径,避免靠最大1.05倍速无限追赶。启动时r尚未建立,按同样的至少两份样本规则在[earliest,secondLatest]内选择锚点。
播放时钟参考实现Playing、Frozen和Waiting转移,python -B 知识库/examples/mechanism_chain/snapshot/test_playback.py以6个手算序列检查时间上限、单包不足、恢复原时刻、超时重锚、旧包不倒退与积压重锚。参考固定T=0.05秒,专注全局快照时间,实体连续段的姿态和离散事件按上述规则接入。
稀疏实体的显示求值
全局r协调同一画面的时间,实体历史决定这个时刻是否有足够数据。前文的latest、earliest和到达间隔用于有效快照包形成的时间轴;包只有在完整解码后才推进它们。这里另给实体e保存最新观察时间latest_e,以及当前连续段的有序运动样本。包时间增长不会改变latest_e。
求值顺序与有效性
给定r,先按生成、销毁和传送事件确定实体在r所属的生成代数及连续段。r早于生成时间时不显示,r到达销毁时间时移除。该段有左右样本t0≤r≤t1时,按t=(r-t0)/(t1-t0)插值位置、球面插值旋转;相同时间戳先去重。若只有左样本且0≤r-latest_e≤E_e,可用最后速度外推位置p+v*(r-latest_e);旋转在未发送角速度时保持最后值。本文取实体外推上限E_e=0.1秒。
超过上限时,实体保留在latest_e+E_e算出的姿态,状态为Stale。它不能把这个姿态标成当前r的有效求值。接口返回pose、sampleTime、mode、segment、validAtR:插值和允许范围内的外推返回sampleTime=r与真值;陈旧保持返回实际封顶时间与假值。缺少新连续段的初始状态则返回Missing,隐藏或显示无效占位,并请求完整状态。其他实体继续按全局r播放。
全局Frozen与实体Stale因此有不同原因。前者表示整条包时间轴耗尽,r停下;后者表示某个实体的调度或丢包不足,r可以继续前进。全局进入Frozen时,冻结该帧各实体求值结果;恢复播放时在原r重新求值,恢复实体的外推与插值差异可产生一次校正。实体单独恢复时也只在当前r求值,不能把新收到的旧样本从头补播。
低频更新与查询接口
假设r从1.00前进到1.40秒,A每50毫秒更新,B只在1.00和1.50秒更新。B在1.08仍可外推,在1.12起已经Stale,停在1.10的姿态。A的包持续到达不改变这个结果。收到B的1.50样本后,若r为1.42且仍保留1.00样本,就能在两者之间插值并恢复有效状态。若r已到1.70,则新样本仍不足,B继续Stale,不能因收到一次更新便重置为有效。
这也说明低频移动实体不能同时享有短延迟、短外推和持续有效显示。调度器若希望B持续有效,必须提高B的更新频率、增加显示延迟,或采用允许更长误差的运动模型。选择由玩法容许误差决定;省略记录不能替代这项取舍。本文保持统一r,对低优先级陈旧实体采用明确的无效显示。
命中查询还要知道无效实体是否影响查询。交互目标与可能遮挡射线的复制实体属于查询关键集合,按整个AOI内的玩法类别保守确定,不能只按已经显示的射线路径筛选。该集合存在Stale或Missing时,聚合的viewReady=false,暂停依赖该视图的历史命中请求;低优先级装饰实体的陈旧不阻塞射击。服务器按自身类别验证这个集合,并校验r、播放代次、段ID及画面年龄,客户端不能自行把目标降为装饰物。完整网络命中模型见延迟补偿命中。
例如目标B被冻结在1.10,而A正常显示在1.40,发送一个不带有效性约束的r=1.40射击会让服务器查询B的1.40位置,与屏幕不符。关键集合门控把这个矛盾显式暴露;单纯提交全局r不能证明所有实体都在该时刻有效。即使有效外推,其姿态也仍是估计,和权威轨迹的误差由命中策略另外处理。
验证方法
复现42→43→44的整数差量例子,确认44始终从声明的42解码;把B从43中省略,确认B的观察时间保持1.00。随后让A每50毫秒更新而B连续500毫秒不更新,逐帧记录全局r、B的sampleTime和validAtR,B应在自己的外推上限转为Stale。再加入迟到的传送、旧代数包、缺失基线与窗口退休,检查它们分别作用于身份、解码和显示,不能因包已确认便跳过显示有效性。
实现与证据
2026-10-22,以本机 Python 3.11 运行 python 知识库/examples/revision_16_21/test_reference.py,17项检查通过。参考实现与测试输入可复现本页注明的机制。 本页共用历史插值的区间检查;压缩格式、基线恢复协议和真实到达抖动仍是接入验收项。
相关主题
参考资料
- Glenn Fiedler:Snapshot Interpolation:Jitter and Hitches、Interpolation Buffer;2026-10-22核读。