加载顺序与覆盖规则
整文件覆盖与同名对象覆盖、FIOS/LIOS,以及 !!! 前缀为何会坑你
两种覆盖,规则完全不同
模组想改原版内容,有两种办法:
| 方式 | 做法 | 谁说了算 |
|---|---|---|
| 整文件覆盖 | 在模组里放一个与原版路径完全相同的文件 | 模组的加载顺序 |
| 同名对象覆盖 | 在自己的新文件里重新定义一个与原版同名的对象(科技、事件、脚本效果……) | 文件名排序 + 该文件夹的 FIOS/LIOS 规则 |
很多新手以为“我的模组排在后面就一定生效”,这只对第一种成立。
下文的实测结论来自 stellaris-Launcher 项目在 4.5.2 上做的对照实验:两个测试模组在不同文件名、不同加载顺序下定义同名对象,再看 error.log 里哪一份存活。
整文件覆盖
同一个相对路径(例如 common/buildings/03_resource_buildings.txt)在多个模组里都存在时,只读取加载顺序中最后一个模组的那份,前面的(包括原版)整份不读。
优点是简单直接;缺点是:
- 游戏每次更新改了这个文件,你的模组就带着旧内容覆盖新内容,容易出 bug;
- 两个模组覆盖同一个文件时,后加载的那个会把前一个的改动全部吃掉。
所以只改一两个对象时,应优先用同名对象覆盖。
同名对象覆盖:先排序,再看规则
游戏读取一个文件夹(如 common/technology)时,会把原版和所有模组在这个文件夹里的文件合在一起,按文件名排序后依次读取。实测要点:
- 排序是逐字节比较的,所以大写字母排在小写字母前面(
FOXCrisis.txt排在crisis_events_1.txt前面)。 - 对于文件名不同的文件,模组的加载顺序不影响读取顺序,只看文件名。
- 常见前缀的先后:
!(0x21)< 数字 < 大写字母 <_< 小写字母 <~。
读到同名对象时,各文件夹的处理方式不同:
- LIOS(Last In Only Served,后读的生效)
- FIOS(First In Only Served,先读的生效)
- 合并(两份内容合在一起)
- 两份都保留(通常意味着出错)
实测结果(4.5.2)
| 文件夹 | 规则 | 依据 |
|---|---|---|
common/technology | LIOS | Object with key … already exists, using the one at … |
common/strategic_resources | LIOS | 只保留后读的一份 |
common/scripted_triggers、common/scripted_effects | LIOS | 同上提示 |
common/agreement_term_values | LIOS | 同上提示 |
common/scripted_variables | FIOS | Variable name … is already taken,使用方拿到先读的值 |
events | FIOS | an event with id [xxx] already exists! |
common/section_templates | FIOS | duplicate section template found(按内部 key 判断) |
common/component_templates | FIOS | Component template key used multiple times |
common/traits | 两份都保留 | 重复定义的特质会出问题 |
没有实测的文件夹,社区工具 CWTools 的规则是:common/ 下大多数为 LIOS;event_chains、global_ship_designs、special_projects、solar_system_initializers 等为 FIOS;on_actions、defines 等为合并;name_lists 等两份都保留。本地化文件的重复键游戏不会写日志,请用 replace 目录处理(见本地化)。
为什么 !!! 前缀会出问题
很多模组习惯把文件命名为 !!!_my_override.txt 或 000_xxx.txt,以为“排在最前就最优先”。但看上面的表就知道:
- 在 LIOS 文件夹(
common/下的大多数,包括scripted_effects)里,!!!_文件最先被读,随后原版的00_…、01_…文件读到同名对象时把你的定义覆盖掉——你的修改根本不生效。 - 只有在 FIOS 文件夹(
events、scripted_variables、component_templates等)里,排在前面才会赢。
反过来,zzz_ 前缀在 LIOS 文件夹里能赢,在 FIOS 文件夹里却会输。所以没有万能前缀,要按文件夹的规则选:
common/scripted_effects/zzz_my_mod_effects.txt ← LIOS:想覆盖原版,排在后面
events/!my_mod_overrides.txt ← FIOS:想覆盖原版事件,排在前面提示 LIOS 文件夹里的
!!!_文件也有正当用途:当作“兜底定义”。例如为玩家可能没有的 DLC 内容放一个占位定义,玩家有 DLC 时原版文件后读,自然覆盖占位。
模组名称前面加 !!!(例如 “!!! Universal Patch”)是另一回事:它只影响某些启动器按字母排列模组列表时的位置,游戏本身只认 dlc_load.json 里的顺序。stellaris-Launcher 排列模组列表时会忽略名称前的这类标点。
另一个隐蔽的坑:BOM
实测 4.5.2 会把脚本文件开头的 UTF-8 BOM 当成第一个对象名字的一部分:你写的 my_tech 实际被登记为“BOM + my_tech”这样一个看不见差别的新名字,于是它不覆盖任何东西。脚本文件请存成不带 BOM 的 UTF-8;原版有 84 个文件以 BOM 开头,但它们的第一行都是注释,所以没事。
实践建议
- 只改少数对象:新建一个带自己前缀的文件,按文件夹规则选择排序前缀,重定义同名对象。
- 改动面很大、或者要删除原版对象:才考虑整文件覆盖,并在更新说明里注明覆盖了哪些文件。
- 给别的模组做兼容补丁:在描述文件里写
dependencies,并让补丁在播放集里排在被补丁模组之后(整文件覆盖才会生效)。 - 不确定谁赢了?看
error.log里的already exists/using the one at提示,见调试。