Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

RA2 MOD 移植 OpenYRWeb 踩坑实录

日期:2026-08-02

🚨 触发场景

把《红色警戒2》原版平台的扩展 MOD(本文以「黄色警戒」为例)移植进 OpenYRWeb(开源、可自托管的 RA2/YR 浏览器引擎)时,会遇到三类隐蔽问题,全部能在真机上稳定复现:

  1. artmd.ini 全量替换导致原版单位渲染异常:MOD 自带 art.ini/rules.ini(RA2 非 md 命名),而 OpenYRWeb 是 YR‑only 引擎——只读 rulesmd.ini/artmd.ini/thememd.ini 等 md 变体;若直接把 MOD 的 artmd.ini 作为 standalone 文件放进 mod 目录,引擎会用它整体替换 ra2md.mix 里原版的 art 表,导致大量 Map object 'XXX' has no art section 报错,专属单位全部渲染异常。
  2. 初始化阶段抛 Must specify an art name for type "Animation" 直接崩溃——根因在引擎 patchAudioVisualRules():它会把 [General] 段的 35 个键(Behind/ChronoBeam/Parachute 等)同步覆盖到 [AudioVisual] 段;MOD 是 RA2 风格,这些键只存在于 [AudioVisual]、[General] 缺失,同步后变成空值触发崩溃。
  3. 游戏内生产某些飞行器(如 NDHB 重型轰炸机)时崩溃弹窗 Unit "NDHB" cannot be docked to NARADR——停靠校验要求单位 rules 必须声明 Dock= 字段,否则引擎不为其挂载 DockableTrait,停靠时直接抛异常。

三个问题排查后都指向同一条主线:数据格式与引擎预期不一致。

🔍 谬误溯源:三个想当然的假设

1. 想当然:以为“MOD 里放一个 artmd.ini 覆盖一下就行”

实际上 OpenYRWeb 的 YR‑only 设计决定 standalone artmd.ini 是全量替换而非增量合并——ra2md.mix 里原版 1581 段 art 定义全被丢弃,MOD 增量表(307KB,1705 段)只覆盖了自己定义的部分,其余原版单位全部变成 has no art section。

正确做法:把 MOD 的 art 表合并进一份完整的 artmd.ini(原版段 + MOD 段),且保持 CRLF 行尾。

2. 想当然:以为初始化崩溃是 MOD 配置写错

逐行对引擎源码后发现是引擎的合并逻辑:Engine.loadRules() 里 patchAudioVisualRules(r.clone().mergeWith(t)) 会把 [General] 段的 35 个键同步到 [AudioVisual];MOD 是 RA2 风格(键只在 [AudioVisual]、[General] 缺失),同步出来的 AudioVisual 键值为空,随后 Animation 等系统对象找不到 art 名直接 throw。

修复不是改引擎,而是把这 35 个键从 [AudioVisual] 补写进 [General] 段(保持 CRLF)。

3. 想当然:以为停靠崩溃是“生产逻辑 bug”

实际引擎 Aircraft 对象构造时按 rules.dock.length 决定是否挂 DockableTrait,dockUnitAt 里 traits.find(DockableTrait) 为 undefined 就抛 Unit "X" cannot be docked to Y。MOD 原版 rules.ini 的 NDHB 就没有 Dock= 行,导致生产即崩。

此外,移植 rulesmd.ini 时还要给 [Animations] 段补注册所有被引用的动画(剥离 ;% 注释后扫描,别漏 NATorpedoT_A 这类键)。

📜 源码验证与落地结论

验证点都在引擎源码里:

  • VirtualFileSystem 的 standalone 文件优先级高于 mix 归档(getFileNameVariant("rules.ini") 返回 rulesmd.ini);
  • Engine.ts.js 的 patchAudioVisualRules(35 键同步逻辑)、GameRes/loadResources(mod 目录挂载);
  • game/gameobject/Aircraft.ts.js(t.dock.length && add(DockableTrait));
  • game/order/DockOrder.ts.js(dockTrait.isValidUnitForDock → e.rules.dock.includes(建筑名))。

落地修复分四层

  1. art 表合并:MOD art 表合并进 artmd.ini(原版 1581 段 + MOD 1705 段),同时给 rulesmd.ini 的 [Animations] 段补注册全部被引用动画;
  2. AudioVisual 补键:把 [AudioVisual] 的 35 个键补进 [General] 段(Behind/ChronoBeam/Parachute/Veins 等),保持 CRLF 行尾;
  3. Dock 字段声明:给所有 ConsideredAircraft=yes/AirportBound=yes 的飞行器补 Dock=(苏军→NARADR,盟军→AMRADR/GAAIRC,苏盟共用→三者都列),例如 NDHB: Dock=NARADR、GBION: Dock=NARADR,AMRADR,GAAIRC;
  4. 数据注入浏览器 OPFS:mods/yellow-alert/ 目录 + _inject 路径,重跑注入脚本后刷新页面验证。

修复后:主菜单正常、专属单位渲染齐全、飞行器生产停靠不再崩溃。

经验总结:移植跨平台引擎时,“文件名变体(md)、ini 段注册(General↔AudioVisual)、Dock 字段声明、art 表全量合并”这四类差异是最高频的坑;排查顺序应先对源码格式再改数据,别先怀疑渲染层或游戏逻辑。


📋 人类开发者速查

四步自检:

  1. 引擎只读 md 变体(rulesmd/artmd/thememd),RA2 原版数据需改名或合并,standalone artmd.ini 是全量替换不是增量;
  2. 初始化报 Must specify an art name 时检查 [General] 段是否缺失 AudioVisual 的 35 个补键;
  3. 飞行器生产即崩溃查 Dock= 字段是否声明(DockableTrait 依赖它);
  4. 改 rulesmd.ini 保持 CRLF 行尾,[Animations] 段补全被引用动画。

🤖 对话式 AI 速查

提问模板:“OpenYRWeb 移植红警2 MOD 崩溃/单位不显示怎么修?”

应回答:YR‑only 引擎只读 md 变体;standalone artmd.ini 会整体替换原版 art 表需合并;Must specify an art name for type Animation 是 [General] 缺 AudioVisual 补键;飞行器停靠崩溃是缺 Dock= 字段。

⚙️ 代码 Agent 速查

关键定位:

  • engine/Engine.ts.js(patchAudioVisualRules 35 键同步)
  • data/vfs/VirtualFileSystem.ts.js(standalone 优先级)
  • game/gameobject/Aircraft.ts.js(dock.length 挂 DockableTrait)
  • game/order/DockOrder.ts.js(isValidUnitForDock)

改完重跑注入脚本后刷新页面验证。

本文采用木兰开放作品许可协议 署名-专利许可,第1版 (MulanOWL BY-PL v1) 授权,亦可选用 CC0-1.0(公共领域奉献)。版权归作者所有,转载须署名并保留本声明,企业合规请保留原始声明。 原始版本见:https://k4m7v2pz.github.io/zh/blog/games/ra2-mod-openyrweb-port-pitfalls.html