后台脚本与内容更新
返回总览。BGNG 除了清单和打包辅助,还包含可独立分发的玩法修正脚本;原生 BackgroundScripts 管理它们的加载与生命周期。本篇区分清单实例、通用机制和当前脚本中的实际执行逻辑,不推断这些文件现在仍部署于线上。
1. 三种更新粒度#
| 方式 | 改变内容 | 边界 |
|---|---|---|
| Tunables | 已有逻辑读取的开关、倍率、阈值与时间 | 必须已有相应读取逻辑 |
| UGC/内容数据 | 通用运行器支持的场景、规则和资源引用 | 受模式的数据结构和解释能力限制 |
| Background script | 调用现有 Native、读写共享状态的新增脚本流程 | 受程序/全局布局、资源、Native 与退出契约限制 |
因此后台脚本比“修改一项配置”更有表达力,但也具有更强的版本耦合。它仍运行在游戏客户端的脚本系统中;名称中的 background 不能解释成独立服务器服务,也不等于一个独立操作系统线程。
2. 从清单到运行中的 GtaThread#
bgscripts.xml 的 Utility 条目包含脚本名、起止时间、Context 和 LaunchParam。[S1] 当前样例采用 GLOBAL,但原生 CommonScriptInfo::HasActiveContext 支持全部 context 满足和任意 context 满足两种策略;UtilityScriptInfo::ShouldBeRunning 进一步检查时间窗口。[S2,L507–604]
RequestStreamedScript 将独立文件登记到 streaming,并请求程序资源;Boot 在资源加载后检查脚本参数结构的字节数是否与 m_launchData 相符,然后通过 scrThread::CreateThread 创建线程、初始化 GtaThread 并登记 script handler。[S2,L237–325] 参数布局不兼容会禁用该项,这不是一个可向任意版本发送任意源码并立即执行的接口。
BackgroundScripts::Update 在初始化且凭据有效后发起首次 cloud 请求,并逐项更新脚本是否应运行。[S2,L993] 下载完成通过事件进入 LoadData、FixupScriptInfos,再向脚本网络事件组投递 BGScript added 事件。[S2,L885]
一个易误读的细节:404 被视为“查询完成但没有后台文件”,m_bHasCloudScripts 也会置真。因此不能仅依据这个变量名判断必然加载了某个脚本;要结合返回结果和后续记录。
3. 共享全局布局是更新契约#
Bg_Common_Helpers.sch 明确警告不要改变 globals 的 USING 顺序,否则会引起 global signature mismatch。[S3,L6–22] 这里引用基础、多人、freemode、UGC、tunables、property 和统计等全局定义,说明补丁并非只使用一组无状态 API,还与现有脚本数据布局相连接。
这使得后台修正能够及时影响常驻玩法,也意味着新增/重排字段、混用版本头文件或调整引入顺序都需要核对兼容性。启动参数大小检查与全局布局约束是不同防线,不能以其中一项通过代替另一项。
同一全局可能同时被常规脚本、云端 tunables 与后台逻辑读写。补丁必须明确初始化时机、写入优先级及退出后是否恢复,不能每帧无条件覆盖所有配置,再期待其他系统自动协调。
4. 实际案例:文件名早已不足以解释职责#
valentineRpReward2.sc 当前入口先禁止暂停、标记网络安全,写入版本字符串的 hash,启动后等待约 10 秒,然后以 WAIT(0) 持续维护。[S4,L488] 主体操作需要网络游戏正在进行且 UGC 全局块已加载。
现有执行内容包括车辆售卖规则调整、部分持有车辆标志修正、按统计状态切换个体规则,以及导演模式残留检查;并包含 Chiliad 醒来场景维护。[S4,L124–239、L327 起] 这些内容足以说明它已成为复用的修正载体,不能按旧文件名将其归类为单纯节日 RP 发奖。
其中两个设计细节值得借鉴:
- 部分规则在
g_FinishedFreemodeInit后只检查一次,离开初始化状态后清除本地完成标志,允许下次会话重新计算。一次性初始化标志具有明确的会话边界。 - 车牌限制逻辑只在条件满足时启用,没有在条件不满足时主动关闭;源码解释是避免覆盖全局下发的禁用规则。这是多个配置来源之间优先级的具体体现。
Chiliad 内容将持久统计/解锁、重生活动、空间位置、时段和天气合成准入条件,再处理音效与手机打断。[S4] 小型追加内容仍需接入现有状态,才能融入世界而不是只按距离突然出现。
上述规则仅用于解释架构,不把本地阈值、版本字符串或清单时间窗口认定为当前线上政策。
5. 正常退出协议与当前样例之间的差异#
BGScriptInfo::Stop 首次设置 ExitFlag 并记录时间,后续等待 ExitFlagResponse、线程 aborted 或超过 30 秒;超过条件后进入停止/资源处理。[S2,L386] Native 的 BG_IS_EXITFLAG_SET、BG_SET_EXITFLAG_RESPONSE 直接接入线程状态。[S5]
但是当前 valentineRpReward2.sc 的主循环是 WHILE TRUE,尾部 BG_SET_EXITFLAG_RESPONSE() 被注释,也未在该循环中检查退出标志。[S4,L508–549] 应将其记录为这个样例与正常退出协议的明显差异,而不是为它补想一个优雅退出流程。原生存在兜底并不说明该脚本完成了自定义资源清理;实际后果仍需具体生命周期和运行环境验证。
6. 在线版本一致性还需要玩法层协调#
RUN_BG_SCRIPT_KICK_CHECK 默认约每 60 秒检查一次;比较期望版本与 g_iBGScriptVersion,结合 tunables、是否处于任务/事件、配置的时间点和分级通知,最终可标记玩家离线直至重启。[S6,L254] freemode 将相应 wrapper 纳入常驻调度。[S7]
这证明版本一致性不是只有下载器关心:玩法需要选择打断时机、解释即将发生的操作,并避免把更新强制插入关键任务中。通知阈值来自 tunables,代码旁的“5/15/30 分钟”注释不能替代实际配置值。
7. 内容更新机制的取舍#
后台脚本适合在现有 Native 能力范围内追加小规模规则与补救流程。它能降低修改整套内容程序的范围,但增加常驻检查、全局写入冲突和版本兼容负担。完整设计应同时覆盖资源加载、幂等写入、模式切换、正常退出、强制终止和版本落后的处理。
这条更新路径与在线自由模式与产业活动、存档统计与进度共同构成持续内容运营的客户端基础。服务端发布流程、签名策略和实时部署状态不在本文静态证据范围内。
源码证据#
| 编号 | 阅读位置 |
|---|---|
| [S1] | bgscripts.xml Utility 清单样例 |
| [S2] | BackgroundScripts.cpp L115 状态;L237 streaming;L268 Boot;L386 Stop;L507 context;L585 时间;L885 cloud;L993 update |
| [S3] | Bg_Common_Helpers.sch L6–22 全局顺序约束 |
| [S4] | valentineRpReward2.sc L124 起修正;L327 起场景;L488 入口;L508–549 主循环与退出 |
| [S5] | commands_script.cpp L646–717 BG Native |
| [S6] | net_bg_script_kick.sch L254–349 版本、活动、时间与退出标记 |
| [S7] | freemode.sc L52348 检查 wrapper;L53187/53945 常驻登记 |
| [S8] | gta_thread.cpp L351 退出响应断言 |
[S1]: <../../../GTAV Source/script/dev_ng/BGNG/BGScript/bgscripts.xml> [S2]: <../../../GTAV Source/src/dev_ng/game/game/BackgroundScripts/BackgroundScripts.cpp> [S3]: <../../../GTAV Source/script/dev_ng/BGNG/BGScript/Common_Bg_Headers/Bg_Common_Helpers.sch> [S4]: <../../../GTAV Source/script/dev_ng/BGNG/BGScript/valentineRpReward2.sc> [S5]: <../../../GTAV Source/src/dev_ng/game/script/commands_script.cpp> [S6]: <../../../GTAV Source/script/dev_ng/multiplayer/include/public/net_bg_script_kick.sch> [S7]: <../../../GTAV Source/script/dev_ng/multiplayer/scripts/modes/freemode/freemode.sc> [S8]: <../../../GTAV Source/src/dev_ng/game/script/gta_thread.cpp>