← ALL ARTICLES
游戏网络9 MIN READ

异步命中结算与恢复

全库目录221
本文目录11

输入会话C的17号输入由射手实例S7、武器W3发出,服务器在Tick600消费,W3有2发弹药,开火画面瞄准T4;实际受击对象由全部合法受击体与遮挡体的最近交点决定。开火画面采用远端时刻9.820秒,服务器此时为10.000秒。延迟补偿命中确定合法查询时间、枪口和历史最近交点;本文从这份固定查询开始,说明弹药、异步结果、Tick提交与两次恢复的身份链。

命中提交与验证

查询只回答历史上击中了谁。对所有有效历史受击体与遮挡体求正向最近交点,再选距离最小者;遮挡更近便阻断,距离相同按预先规定的遮挡优先与稳定ID次序处理,避免容器遍历顺序决定命中。查询结果返回目标ID、实例代次、距离和使用的历史时间,不直接改实时物理状态。

接纳与弹药所有权

在Tick600,S7持有W3,弹匣剩2发。模拟线程先完成输入与视图校验,在同一耐久模拟提交中登记射击身份、输入摘要、S7、W3、枪口、方向、伤害参数、策略版本和固定历史查询数据,并把弹药从2减为1。这一发此刻已经接纳,形成独立的射击记录;弹药支出与记录一起提交,不能只写其中一项。重复请求按身份与摘要找到原记录,摘要不同则拒绝。

本文选择接纳即消耗的规则。历史未命中、目标已死亡、查询失败或超时都消耗这一发;接纳前的弹药不足和非法输入则不消耗。这样查询等待不再借用可变化的武器库存。射手随后死亡、丢弃W3或重生为S8,已接纳射击仍可结算,结果不会扣S8或新武器的弹药。伤害归属保留S7的稳定身份,即使其活动对象已销毁也能生成战报。需要退款的玩法必须另定义针对原库存实例的退款事务,不能在任意当前武器上加一。

同步查询可在同一次模拟提交内得到最终结果。异步查询则保存Pending并派发纯查询任务,任务只能读取固定数据,不访问活动射手或修改世界。冷却在接纳时更新;该射手后续输入可继续消费,下一发再次检查剩余弹药和冷却。输入ACK表示输入已消费,动作结果可以仍为Pending,客户端保留相应事件身份直到收到终态。

查询完成与结算顺序

查询任务返回射击身份、对局执行纪元、尝试代次、查询包哈希、历史版本和最近交点。任务只把结果放进完成队列;进入队列尚不等于伤害提交。模拟线程在每个Tick的固定结算阶段,持队列锁交换出当时的结果集合,随后到达的结果留到下一Tick。它逐项核对射击仍为Pending、纪元及代次匹配、输入包与策略版本一致,按射击身份去除重复。本轮同一Pending尝试出现不同内容的结果时,按查询损坏终结并报警,不让容器顺序决定伤害。已经提交终态后才收到矛盾消息,只追加协议故障诊断,不反向改写已提交伤害。

集合内的有效结果按接纳序号排序;未完成的更早射击不阻塞集合中的后续射击。因此Tick604完成的后一发可以先于Tick605才完成的前一发结算。要求严格开火顺序的玩法需等待全部前序终态,并承担队头阻塞。本页采用按Tick截取完成集合的规则,排序只在该集合内生效。

截止也在这个阶段决定。17号输入接纳于600、固定截止于606;606先采用交换出的有效完成集合,再把仍为Pending且到期的射击加入超时集合。完成集合内按接纳序号结算,超时集合也按同一序号稳定排列。交换队列之后才到达的17号命中不能撤销本Tick的超时。截止使用对局模拟时间,停机期间暂停,恢复不重给六个Tick。

结算时对同一目标实例当前仍存活的对象施加伤害。T4在9.820秒被击中而结算时已死亡,终态为无伤害;重生的T5不承接T4的命中。同Tick两发都指向生命只够承受一发的T4时,排序在先的一发先改生命,后一发读取修改后的状态。结果记录使用的历史时刻与当前目标代次,才能区分几何未命中、目标已失效和实际伤害。弹药在600已扣,结算不再扣一次。

权威决策与Tick日志

完成集合来自线程调度,恢复时重新运行查询不能重现它原先在哪个Tick到达。因此本页把上述集合转成显式的权威决策批次,作为 对局服务器架构 “运行中恢复的状态链”所规定的Tick输入。每个Tick记录该阶段是否执行、排序后的采用项及超时项;空集合也记录为空。每个采用项保存射击身份、尝试代次、查询包哈希和完整最近交点或无命中结果,超时项保存原截止Tick。批次还固定结算规则版本,回放不从工作线程重新收取结果。

完成队列只提供候选,下面的Tick提交才决定结果何时成为权威事实。

从已提交状态Sk计算下一步时,模拟线程依次消费本Tick玩家输入、执行玩法阶段,再按记录的决策批次执行结算归约。Tick日志保存输入顺序、决策批次、状态版本和事件发件箱;伤害、射击终态、事件ID、输入消费记录与新状态在同一耐久Tick事务中提交。写入失败时该Tick不能发布权威ACK、伤害确认或命中提示,实例停止推进并从最后已提交边界恢复。这里的批次不是独立于状态提前确认的另一笔提交。

以17号与18号射击竞争T4最后10点生命为例,若604的采用批次只有18号,605才采用17号,那么回放也先在604扣除18号的伤害,605把17号结为目标已死。即使恢复机器重新计算17号更快,也不会交换这两个结果。若606的已提交批次把17号列为超时,回放直接执行超时,不采纳重算出的命中。这正是记录“哪个结果在哪一步成为权威输入”的作用。

查询包与崩溃恢复

接纳17号之前,服务器将9.820秒查询所需历史几何、静态场景版本、枪口、方向及策略参数写成不可变查询包,校验并持久化,再把包哈希与Pending纳入Tick600事务。几何范围覆盖整条许可射线上的受击体和遮挡体,门也包含在内。包由Pending和未退出工作任务共同保护;终态落盘后仍等待旧任务退出再释放内存,持久文件按快照与日志保留根回收。

恢复记录与有序回放

查询尝试本身也会在恢复时变化。若快照保存纪元7、尝试1,第一次恢复改成纪元8、尝试2,604的伤害就可能来自尝试2。第二次恢复仍从旧快照开始时,必须先还原这次身份变化,才能理解604为何接受它。因此Tick提交和恢复记录使用同一条有序日志,每条记录分配单调日志序号LSN。

Tick记录包含前驱LSN、Tick号、权威决策及状态事务;恢复记录包含前驱LSN、当前Tick、原尝试身份、替换后的尝试身份和查询包哈希。恢复记录在同一耐久事务中比较日志尾与原Pending身份,更新Pending并追加日志。它保持模拟Tick、原截止及弹药不变。单写一份旁路诊断日志无法建立这个顺序,登记成功也不能先于事务提交对外报告。

替代实例先隔离旧执行者并取得当前所有者纪元,再加载一致快照。当前所有者纪元用于控制谁能追加日志、派发任务和接纳实时返回;快照及日志中的尝试纪元则是历史模拟状态。回放器按LSN依次还原历史身份,遇到Tick记录便消费原决策批次,遇到恢复记录便比较其原身份并安装所记录的新身份。回放期间不开查询线程、不广播事件,也不拿当前所有者纪元拒绝历史决策。否则合法的纪元8结果会在纪元9恢复时被误删。前驱、身份或包哈希不符时停止恢复并核查日志,不能跳过记录继续模拟。

沿17号射击可得到下面的两次恢复。日志号仅表示提交顺序,恢复记录不推进Tick。

日志位置耐久内容回放后的17号状态
快照覆盖LSN100,Tick603Pending,尝试(7,1),截止606,包H已扣原弹药,尚无终态
LSN101,恢复记录比较(7,1),改为(8,2),包仍为HPending(8,2)
LSN102,Tick604采用(8,2),T4伤害及终态共同提交终态,保留同一事件ID
第二次恢复到LSN102从100先应用101,再应用102终态,直接补送回执

第二次恢复取得的当前所有者纪元即使为9,也只控制新执行入口。17已经终结,不能再生成尝试(9,3)。如果第一次恢复只提交了101、尚未派发便退出,第二次恢复先应用101,看到的仍是Pending(8,2);然后才追加新的恢复记录,把它承接为(9,3)。因此登记后未派发不会丢掉射击,旧尝试晚到也没有采用资格。

回放到最后耐久LSN后,才为剩余Pending追加当前所有者纪元下的恢复记录,逐项递增尝试代次,提交后用原包派发。当前执行入口同时核对所有者纪元、Pending尝试身份与包哈希,旧任务不能追加日志,其返回也不能进入新的有效完成集合。最后耐久Tick为603而原查询在604未提交前算完时,这份线程结果可以丢失;新尝试从下一模拟Tick开始参与新的完成集合。恢复重现已提交决策,未提交的完成先后由新的运行重新决定。

快照在同一一致边界保存覆盖LSN、模拟Tick、Pending尝试身份、查询包、截止、动作回执及输入消费状态。覆盖LSN可能指向恢复记录,例如Tick仍为603但已覆盖101;后续回放从102开始。快照引用发布后,日志截断还要取所有保留恢复根所需的最早位置;旧快照仍受保留时,其后的日志继续存在。包回收同时检查当前Pending、保留快照与未截断日志的引用,以及未退出任务的内存持有;终态快照发布并不授权删除较旧可恢复快照仍引用的包。恢复记录已被快照承接时,身份来自快照,未被承接时则来自日志,两者都只应用一次。

若最后耐久边界已包含606的超时阶段,就不存在这发的Pending。若恢复停在605,下一个606仍先截取新纪元有效完成集合,再终结到期项,原截止不移动。原包丢失或校验失败时,将Pending作为查询数据损坏决策写入下一耐久Tick并报警,保留600已提交的弹药支出,不使用当前几何补算。

射击17的阶段耐久记录恢复与消费者行为
600接纳C/17、S7/W3、弹药2→1、包哈希、截止606、Pending输入ACK可推进17;客户端保留动作等待项
602射手死亡S7死亡,射击仍PendingT4查询仍使用9.820秒的原包,重生S8库存不变
603退出,尚无终态重放到603仍Pending登记新尝试后派发原包,后续Tick记录新的决策集合
604采用有效命中批次、T4伤害、终态和事件发件箱共同提交客户端按原事件ID确认命中;重发只查询同一结果
或606采用超时超时决策、终态和事件共同提交607晚到命中被拒绝,客户端结束等待,保留开火事实

这些行描述同一发的不同故障分支。600接纳前崩溃时,弹药和Pending都不存在;604提交后丢响应时,恢复直接补送回执。输入ACK只证明输入已消费,枪口效果、弹药确认和最终命中分别消费自己的接纳或终态事件。

观察与反例

先录制目标横穿射线的轨迹,核对显示r、枪口所属输入步、历史最近交点和当前结算目标。再把同一发射击放入门关闭、传送、销毁、重复请求和超窗场景,分别检查时间选择与拒绝原因。异步路径还应在接纳后死亡、接纳落盘后退出、伤害落盘后丢响应及截止边界注入故障;每次追踪原武器弹药、Pending、目标生命和事件ID,才能分清查询完成与权威提交。

验证方法

先验证命中目标销毁和重复完成,再在600接纳、604采用、606超时的提交边界退出。按LSN100→101→102回放两次恢复,检查尝试纪元、原弹药、事件ID和截止Tick。

实现与证据

本页是异步权威结算协议设计。网络参考样例覆盖历史线性插值与球形求交,未实现本页耐久Tick、恢复日志和服务器任务队列。协议数例可逐步复算,真实网络、崩溃恢复和公平性需要集成验证。

相关主题

参考资料