CD Code Digest Hub /GTA V

多人同步与实体所有权

11 分钟阅读 7 个章节 2 张图

返回总览。本篇沿脚本广播、原生数组同步、网络实体和事件链路分析。所有性能讨论都是机制分析,未采样带宽、测延迟或运行多人环境。

1. 三种同步平面#

平面典型内容源码入口不应承担的职责
脚本广播变量任务阶段、选中变体、队伍、网络实体 ID、玩家参与状态NETWORK_REGISTER_HOST/PLAYER_BROADCAST_VARIABLES复制每一个本地临时变量
网络实体复制角色、车辆、物体及同步树中的状态CNetworkObjectMgr、SyncTree、clone决定所有业务奖励和解锁
网络/脚本事件操作请求、通知、参与者变化、一次性交互SEND_TU_SCRIPT_EVENT、事件队列作为迟加入恢复所需的唯一事实来源

另有 NetworkScriptWorldStateManager 及道路、scenario 阻塞区、人口倍率区等 world state 类型,表明“场景级规则变化”也不应都包装成一个可见实体。[S9]

2. 广播数据是显式登记的契约#

freemode.sc 登记多个 GlobalplayerBD_* 和 GlobalServerBD_* 块,包括任务请求、任务列表、排除区域、活动选择器、房产实例、自由模式事件、随机事件等。[S1,L15093 起]

主机块表达脚本级共享事实,玩家块表达按参与者维护的状态。未登记的本地变量不会因为脚本在多人模式运行就自动同步。登记地址、大小、顺序和结构布局均属于双方解释数据的契约。

Native 实现把登记交给 script Handler 的 network component,再由 netArrayManager_Script 建立相应 array handler。原生层区分 host data 和 player data,记录 handler 序号及高频更新属性,并有重复登记检查。[S2][S3]

这不是“每帧把整份 freemode 全局内存广播一遍”。数组系统具有脏元素、消息大小估算、WriteUpdate/ReadUpdate、ACK 和 split-update ACK 等机制。[S4] 也不能据 SIZE_OF 直接报线上字节数;例如事件 Native 会把脚本 word 数乘以 sizeof(scrValue),其他通道还涉及序列化和包头。

3. 主机迁移不是重新开始任务#

MigrateScriptHost 发送迁移消息、设置 pending host 和迁移计时器,并暂时清空当前 host。主动迁移条件包含本地为 host、存在其他参与者、没有 pending host、没有迁移阻止等;这些是该入口的条件,不等于所有失联恢复都使用完全相同规则。[S3,L612]

最有价值的带宽设计出现在 netArrayHandlerBase::HandleChangeOfArbitration:当仲裁者变化时,为避免把全部数组状态重新发给所有玩家造成峰值,客户端向新仲裁者发 checksum,仅对不一致者补状态;代码同时保留让新加入者获得此前修改元素的机制。[S4,L1065]

由此可得内容层的要求:全局推进所需事实必须存在于接手者可获取的状态中;纯本地临时计时器要能重建或重新初始化;“我刚成为 host”不能无条件把已进行一半的任务重置到起点。checksum 是一致性修复工具,不等于对所有恶意或业务错误的验证。

4. 实体 owner 与脚本 host 不同#

CNetworkObjectMgr 负责注册本地网络对象、创建远端 clone、同步/移除 clone、处理 ACK,并跟踪等待 owner 变化的对象。[S5]

NETWORK_REQUEST_CONTROL_OF_ENTITY 是请求;NETWORK_HAS_CONTROL_OF_ENTITY 才查询当前控制条件。脚本在请求后仍应等待和验证,不能据一次请求调用直接认为已取得写权限。[S2]

图省略了 H 与 O 同机的常见情况。具体 Native 也可能替脚本转交远端操作:GivePedScriptedTask 对 clone/待迁移 Ped 调用网络处理路径。因此不能把“非 owner 调用必然完全无效”当成所有接口的统一规则。[S6]

创建时又有额外区分:CREATE_VEHICLE 的网络对象标志与 script-host object 标志不同。要求 host object 的创建会检查当前网络脚本的 host 身份。实体可见、存在、由本机拥有和由本脚本管理,也应分别判断。

5. 事件通道与 API 版本细节#

net_process_events.sch 处理加入/离开脚本、广播变量更新以及大量业务事件,通过 GET_EVENT_DATA 按结构读取。[S7] 事件适合通知和请求,但不能把“已经发送”视为全体客户端已完成对应动作,也不能假定它天然保存所有历史。

当前源码中应特别关注 API 演进:旧 TRIGGER_SCRIPT_EVENT 原生实现带有在线时“不再使用此命令”的非 FINAL 断言;实际脚本可见 SEND_TU_SCRIPT_EVENT,其注册在 commands_script.cpp 中绑定到 CommandWinterIsComing。不能根据函数名字寻找不到实现就断定接口缺失。[S8]

原生实现把脚本 word 大小换成字节,处理本地与远端接收者位掩码,并检查活动玩家范围。业务处理器仍需验证事件对应的玩家、活动实例、目标状态;传输成功不等于业务请求合法。本文未对全部事件处理器进行安全审计。

6. 预算、频率与内容规模#

网络对象管理器有 clone create/remove cap;带宽管理器提供目标带宽、节流与更新逻辑;在线事件启动器还会预留角色、车辆、物体额度。[S5][S10] 三者分别限制对象生命周期突发、传输量和活动资源需求,不能互相替代。

对内容设计的直接影响是:

  • 广播“阶段、目标 ID、必要参数”,把纯视觉插值和界面细节留本地。
  • 避免在可重复执行的初始化中反复清空/重写巨大广播结构。
  • 大量生成和销毁应评估对所有附近玩家造成的复制突发。
  • 结束活动时既要停止广播和参与关系,也要移交/删除实体并撤销世界覆盖。
  • 新增字段要核对所有登记方和读写方,不把结构兼容性当成本地编译器的问题。

本地代码证明存在分包、ACK、校验和迁移恢复机制;它并不直接提供可用于本项目的实测“每玩家多少 KB/s”结论。

源码证据#

编号位置
[S1]freemode.sc L15093–15174 广播登记
[S2]commands_network.cpp L4123 网络脚本;L4332 广播;L5809/5879 控制权
[S3]scripthandlernetcomponent.cpp L612 迁移;L1672/1739 广播登记
[S4]netarrayhandler.cpp L297/415 更新;L516/579 ACK;L968 dirty;L1065 仲裁变化
[S5]NetworkObjectMgr.h L67 注册;L236–282 clone;L314 待转移 owner
[S6]script_cars_and_peds.cpp L1261 网络任务转交
[S7]net_process_events.sch L522、L914、L1067;L6278 TU 事件发送
[S8]commands_script.cpp L478 数据大小;L493 旧接口;L731 起 TU 路径;L862 注册
[S9]network/General/ScriptWorldState 世界状态管理与具体类型
[S10]NetworkBandwidthManager.cpp L83 初始化;L97 更新

[S1]: <../../../GTAV Source/script/dev_ng/multiplayer/scripts/modes/freemode/freemode.sc> [S2]: <../../../GTAV Source/src/dev_ng/game/script/commands_network.cpp> [S3]: <../../../GTAV Source/src/dev_ng/rage/framework/src/fwscript/scripthandlernetcomponent.cpp> [S4]: <../../../GTAV Source/src/dev_ng/rage/framework/src/fwnet/netarrayhandler.cpp> [S5]: <../../../GTAV Source/src/dev_ng/game/network/Objects/NetworkObjectMgr.h> [S6]: <../../../GTAV Source/src/dev_ng/game/script/script_cars_and_peds.cpp> [S7]: <../../../GTAV Source/script/dev_ng/multiplayer/include/public/net_process_events.sch> [S8]: <../../../GTAV Source/src/dev_ng/game/script/commands_script.cpp> [S9]: <../../../GTAV Source/src/dev_ng/game/network/General/ScriptWorldState> [S10]: <../../../GTAV Source/src/dev_ng/game/network/Bandwidth/NetworkBandwidthManager.cpp>