CD Code Digest Hub /GTA V

SanScript 桥接、脚本调度与资源所有权

范围:script/、script_headers/,并关联 script/dev_ng 的玩法内容。总入口:Summary。

8 分钟阅读 8 个章节 1 张图

1. 原生能力与玩法编排的边界#

SanScript 使用 .sc/.sch,不是 Lua、JavaScript 或 C#。脚本表达任务、活动、菜单和世界流程;Native 声明定义可调用能力;游戏层 commands_*.cpp 校验上下文并调用原生模块;RAGE scrThread 执行脚本程序。

GtaThread 是游戏侧线程适配,继承和调用 scrThread。这里的“脚本线程”是 VM 的执行单元,不能根据名字推断每个脚本拥有独立操作系统线程。其执行受游戏调度、指令计数、暂停和等待状态约束。

2. 完整的 Native 调用链#

第一步表示调用者应完成的准备契约,不表示 CREATE_VEHICLE 内部自动请求模型。对应 .sch 的 Native 声明和 SCR_REGISTER_SECURE(CREATE_VEHICLE, ..., CommandCreateVehicle) 将脚本接口与实现连接。

CommandCreateVehicle 104 行附近检查任务车辆容量、世界坐标、模型有效性、已装载和 vehicle 类型;联机创建要求合适的 network script handler,host object 需要本地脚本 host。非网络车辆会清除 host-object 意图,多人过场条件还可能将网络创建改为本地创建。

成功后通过 GetGUIDFromEntity 返回脚本标识,并调用 RegisterEntity。GUID 是句柄式边界,脚本不直接持有 C++ 指针;失败路径返回初始化为 0 的结果。调用方要处理失败,不能只依赖 debug assertion。

3. 真正的调度入口#

当前 script.cpp 943 行的 CTheScripts::Update() 是空函数。Process() 包装 InternalProcess(),后者先更新 script handler manager、虚拟键盘,再判断是否允许执行,随后处理网络前脚本阶段、区域、脚本角色/车辆、HUD、world point 和 brain dispatcher 等。

ShouldBeProcessed 同时检查暂停策略、是否完整更新/渲染、网络游戏、初始化特例、Landing Page 宏和回放模式。不能简单推断“暂停即所有脚本停止”或“前端从不执行脚本”。

GtaThread::Update(int insnCount) 检查线程暂停,执行 forced cleanup 检查,准备每线程 HUD 和部分临时状态,再调用 scrThread::Update(insnCount);之后还有网络安全及 XML、存档构造等成对操作的断言。这些检查说明某些 Native 在一次执行片段内必须配对收尾,不能任意跨越 yield 保存半完成的全局构造状态。

4. 句柄、Handler 和资源跟踪#

层次管理内容
GtaThreadVM 状态、脚本身份、暂停、终止与 brain 类型
CGameScriptHandler : scriptHandler脚本实体、资源注册/移除、对象清理
GameScriptResourcesstreaming 资源、游戏资源类型与引用
GameScriptHandlerNetwork网络脚本组件、host 与联机资源语义
GameScriptMgr线程与 handler 的注册和集中更新

Handler 的 remove 接口带 detach 参数,表明解除脚本归属不一定等于立即销毁资源。实体还可能由世界、其他系统或网络继续管理;正确清理需要区分删除、释放需求和交还环境人口。

GtaThread::Kill 会先注销 script handler,再按 brain 类型恢复关联 Ped、Object、world point 状态,最后继续底层终止。只停止 VM 而不处理这些状态,会留下“实体仍认为脚本在运行”的逻辑残留。

5. Script Brain 与流式玩法#

script_brains、streamedscripts 和 world point 将脚本运行与世界对象、区域及 scenario 关联。角色或物件接近相关条件时可以启动专用脚本,离开或结束后再恢复待触发状态。这样大量环境行为无需始终保持所有脚本运行。

模型、脚本程序、动画、UI 和效果资源分别由不同模块加载,但脚本 handler 可以集中记录需求。任务退出的关键不是“已经没有脚本指令执行”,而是所有所持资源和异步请求是否已经按协议释放。

script_shapetests 也说明脚本 Native 可以包装异步引擎操作;Native 调用返回不一定代表其工作已完成。脚本必须依据 API 的 handle/状态约定查询结果。

6. 单机与在线内容组织#

单机 main 脚本通过 USING 同时引入 commands、任务流、过场、金融、列车和其他公共定义;具体任务/活动分布于 missions、Ambient、Minigames 等。多人 Transition 与 freemode 则组织加入切换和在线玩法,shared/core 提供公共能力。

这个分工不是绝对隔离:脚本可以共享头文件和原生命令,在线状态又改变同一 Native 的校验和所有权规则。理解玩法时应从对应 .sc 向 Native 追踪,理解能力约束时则从原生实现回看调用者准备条件。

本报告聚焦游戏层桥接,不逐篇分析数千个任务脚本的剧情和活动状态机;它们作为内容层与原报告的脚本目录分析衔接。

7. 性能与接口变更#

脚本成本不只取决于指令数。一次 Native 可能触发实体创建、路径请求、资源加载或 Scaleform 调用;高频循环中调用昂贵 Native 会把成本转移到原生模块和异步队列。

变更接口需同时核对 .sch 声明、参数/返回类型、注册标识、原生实现、脚本调用和生命周期。可见 declaration 不保证所有平台都支持相同分支;新增资源 Native 应设计强制终止时的清理。

8. 源码依据#

源码重点
commands_vehicle.schCREATE_VEHICLE Native 声明
commands_vehicle.cpp104 行创建实现;15346 行注册
gta_thread.cppUpdate、Reset、Kill
script.cpp注册、空 Update、ShouldBeProcessed、InternalProcess
GameScriptHandler.h资源归属与清理接口
GameScriptResources.h资源类型与 streaming 适配
单机 main.sc玩法入口与 USING 依赖

关联:流式加载、车辆、网络、持久化。