CD Code Digest Hub /GTA V

启动流程与脚本调度

9 分钟阅读 7 个章节 1 张图

返回总览。本篇分析脚本服务的装配、运行和回收;只依据源码,不表示已验证某个平台的实际启动顺序。

1. 多层生命周期而非一个 main#

startup.sc 负责全局注册等启动准备;main_persistent.sc 明确以跨单机/在线持续存在为目标;main.sc 是单机主流程,负责请求 initial,后者启动单机控制脚本并设置单机状态。[S1][S2][S3][S4]

图表示装配职责,省略 boot 路径和条件分支,不能据此将所有节点解释为互相直接 START_NEW_SCRIPT。

main_persistent 请求手机、对话、商店、selector、HUD、context、统计、成就、建筑等脚本。这说明商店或角色选择器可以跨模式存在,它们内部必须区分 SP/MP,而不是进入在线后换成完全独立的一套服务。

2. 加载和启动分开#

REQUEST_AND_LAUNCH_INITIAL_SCRIPT_WITH_WAIT 的顺序是请求、等待加载完成、START_NEW_SCRIPT、SET_SCRIPT_AS_NO_LONGER_NEEDED。最后一步释放的是启动者对脚本资源的需求,不等于立即杀掉已经启动的脚本。[S3]

initial 中的 Start_SP_Control_Scripts 批量准备控制器。启动时分配的栈大小有多种业务类别;main_persistent 的调试映射可见默认、手机、商店、任务、freemode 等栈配置。它们是脚本栈配置,不应直接当成全部运行内存或网络数据大小。[S2][S4]

恢复存档也不是只把变量拷回去:Validate_And_Repair_Restored_Game、Attempt_To_Restore_To_Saved_Strand_Command 重新检查剧情流恢复位置。持久数据和当前编排数据可能需要校对。

3. 调度的三个尺度#

尺度机制适合处理
脚本线程WAIT 和运行时线程更新独立生命周期、独立清理的控制器
控制器内部定时器、阶段枚举、轮转索引不必每帧扫描的世界状态
在线服务内部Script_Thread_Update.sch 回调列表大量功能的每帧、分帧、长周期更新

SCRIPT_THREAD_DATA 保存函数指针和三类列表:EVERYFRAME、STAGGERED、LONG。REGISTER_UPDATE_THREAD 把回调登记到列表;移除时会先置待删除标记,避免直接破坏正在遍历的数组。[S5] 此处的“thread”实质是被调度的脚本函数条目,不是新建 OS 线程。

环境区域控制器也采用轮转:一次只处理 AMB_AREAS_PER_LOOP 个区域,保存 iCurUpdateIndex 跨帧继续。它把世界空间检查成本摊开,而非对全部区域每帧执行完整判断。[S6]

4. 模式切换的关键是撤销占用#

单机转在线需要撤销任务、过场、单机全局占用、世界修改和脚本资源需求,之后才能让在线系统拥有玩家和场景。main_persistent 中有清理完成、已执行 force cleanup、已做 mission cleanup、已停止 cutscene 等位,体现了分阶段撤销,而不是一次广播后假定所有脚本立即结束。[S2]

反向切换需要恢复单机主脚本和世界状态。常驻服务则继续工作或进入适合当前模式的分支。若常驻组件缓存了旧实体句柄、任务候选或房产实例,就可能在切换后引用已经失效的数据;这解释了源码中大量 reset 和 cleanup 分支的必要性,并不意味着本文已经复现相关缺陷。

5. 通用控制器的边界#

event_controller 处理自己的事件队列,并不代表承包所有原生事件和网络事件;code_controller 是集中检查/响应脚本与游戏状态的控制器,也不是虚拟机本身。[S7][S8] script_launch_control.sch 维护脚本启动条件,与运行时的脚本资源加载职责不同。[S9]

设计上,可持续服务通常按“初始化—监控—重置/清理”组织;有限活动则按“请求—开始—运行—完成”组织。公共服务尽量处理共享占用与仲裁,具体内容脚本保存局部阶段,减少所有玩法都直接改写共享资源的冲突。

6. 性能与内容扩展#

增加一个常驻功能,要决定其更新频率、是否只有附近才需要工作、模式切换时由谁重置。低频变化的数据可通过变化检测更新;手机绘制、交互输入、帧级世界覆盖则需要每帧维护。把所有工作简单移入 freemode 或 main 会放大编译单元、全局依赖和每帧扫描成本。

建议保留源码已有的四种观察面:线程是否存活、当前阶段、资源是否就绪、清理是否完成。它们比仅看“脚本已启动”更能解释加载或切换卡住的原因。

源码证据#

编号阅读位置
[S1]startup.sc L189 注册依赖;L221 全局注册
[S2]main_persistent.sc 文件头、清理位、Request_And_Launch_Persistent_Scripts_With_Wait
[S3]main.sc L170 附近 initial 请求/启动;SCRIPT_CLEANUP
[S4]initial.sc L122 控制器启动;L414/438 恢复校验
[S5]Script_Thread_Update.sch L24/32 结构;L154 登记;L178 延迟删除
[S6]controller_AmbientArea.sc L37–89 轮转更新
[S7]event_controller.sc L119 队列处理
[S8]code_controller.sc L19 脚本入口与主循环
[S9]script_launch_control.sch 条件启动公共实现

[S1]: <../../../GTAV Source/script/dev_ng/singleplayer/scripts/main/startup.sc> [S2]: <../../../GTAV Source/script/dev_ng/singleplayer/scripts/main/main_persistent.sc> [S3]: <../../../GTAV Source/script/dev_ng/singleplayer/scripts/main/main.sc> [S4]: <../../../GTAV Source/script/dev_ng/singleplayer/scripts/main/initial.sc> [S5]: <../../../GTAV Source/script/dev_ng/multiplayer/include/public/Script_Thread_Update.sch> [S6]: <../../../GTAV Source/script/dev_ng/singleplayer/scripts/Ambient/AmbAreaController/controller_AmbientArea.sc> [S7]: <../../../GTAV Source/script/dev_ng/singleplayer/scripts/eventcontrol/event_controller.sc> [S8]: <../../../GTAV Source/script/dev_ng/singleplayer/scripts/codecontrol/code_controller.sc> [S9]: <../../../GTAV Source/script/dev_ng/singleplayer/include/private/script_launch_control.sch>