找回密码
 初始化身份识别芯片
搜索
查看: 255|回复: 2

0.98 145个mod ai修复冲突记录分享

[复制链接]

巡洋大副

发表于 15 小时前 | 显示全部楼层 |阅读模式
Mod Bug汇报
已解决
游戏版本: 0.98
Mod中文名:
Mod版本:
游戏日志:
[Mod信息]

Mod名称 |Mod版本 |已启用
=======================================================================================
LazyLib |3.0.0 |是
zz GraphicsLib |1.12.2 |是
MagicLib |1.5.6 |是
! PatchLib ! |1.0.3 |是
[链戍集团军]Shackles Garrison - XXVIIBattleGroup |2.0.0 |是
A New Level of Confidence |40 3.3 Fast |是
AdvancedGunneryControl |1.20.0 |是
AEF-阿瓦隆远征队 |Ver:0.6.9-Sub:2 (数据库校对 DatabaseReview)|是
AI Tweaks AI 改修 |2.2.10 |是
Aptly Simple Hullmods |2.1.50 |是
Autosave 自动保存 |1.2d |是
BoxUtil |1.6.3 |是
CTB-星际小卖部 |0.5.8a-fix2 |是
D-MOD Services |3.0.1 |是
Demand Indicators 供需指示 |1.0.f |是
Dynamic Battle Reinforcement |0.23.5 |是
EVE总览与指挥 |1.2.3 |是
Exotica Technologies |1.8.7 |是
FSF军事公司[FSF_MilitaryCorporation] |5.1rc1-02 |是
HMI反派势力 |0.0.5d |是
Hostile Intercept (and Autopause!) |1.6.0 |是
Hypernova Infinite Engine |1.9.6 |是
Interastral Peace Casino |1.6.3 |是
J GEEK Federation |1.4.0 |是
Klark Morrigan 的实用工具 (KMU) |0.2.1 |是
Klark Morrigan 的程序库 (KMLib) |0.5.2 |是
Leading Pip |1.10.2 |是
Lukas的随手之作 |3.3.1 |是
LunaLib |2.0.5 |是
Minesector |1.6d |是
More Combat Terrain Effects 更多战斗地形效果 |1.13.1a |是
More Military Missions 更多军事任务 |0.5.0 |是
Nijigen Extend |0.3.3 |是
Nomad Flotilla |1.5.1 |是
Overlord Accessibility |1.1.6 |是
Particle Engine |0.10.1 |是
Pa的虫洞引擎 |1.0.0 |是
Persean Champion (英仙座冠军) |1.2.0 |是
Phillip Andrada:加油站经理 |1.14.1aa |是
Player-Only Boosts |1.2.2 |是
Portrait Changer 头像修改器 |1.0.7 |是
Progressive S-Mods 经验内插 |1.2.0 |是
QoL Pack 生活质量工具包 |1.5.2 |是
Refit UI Plus |0.11.0 |是
RetroLib |1.1.1 |是
RTSAssist |0.2.22exp |是
Scan Those Gates 扫描那些星门 |1.5.7 |是
SCAV-CO 造船厂 |X-8 |是
Special Hullmod Upgrades |1.6 |是
SpeedUp |协议超驰 |是
Station Augments 空间站增强 |2.4.3 |是
Station Refitting 空间站装配 |1.0.0 |是
Stellar Networks |3.3.0 |是
What we left behind 我们遗落之物 |4.5.1 |是
中微子探测器 Mk.II |1.4.4 |是
中文输入修复 |2.0.0 |是
义体改造 |0.3.3 |是
人类联合帝国 |v1.1.0 |是
价值边框 Value Borders |1.1.6 |是
便携式传送 |1.1 |是
军官拓展 |0.8.1 |是
军官捕获B |1.2.2 |是
别修这艘船! |1.1.0 |是
势力争霸 [Nexerelin] |0.12.2d |是
北极星之首 [Polaris Prime] |0.4.3 |是
博尔肯基金会 [Foundation Of Borken] |0.9.0 |是
卡特废品船插 |1.0.2a |是
卡特贸易公司 |3.2.5 |是
卢德骑士团 |1.4.0 |是
取消友军伤害 |1.2 |是
古代武器包 |0.1.9 |是
启用行星杀手 |3.0.2 |是
地貌改造 & 空间站建设 |10.0.9 |是
墨汁的mod溯源 |0.1.0a |是
天工联合船舶设计局 |2.5.9 |是
天工船舶拓展 |0.2 |是
天苑四-行星联合实业 无li版 |1.4.4 |是
失落星域 LOST_SECTOR |0.6.2d |是
威胁侵染 |0.59 |是
威胁渐显 Emergent Threats |1.4.0 |是
威胁渐显: 第九战斗群 复兴 Emergent Threats: IX Revival |1.4.0 |是
工业革命 |4.1.b |是
已知星域 |1.1.0 |是
布塔赫联盟 |1.1.7 release |是
布莱顿联邦 |0.0.4a |是
帝国先遣队 |0.9.994 |是
幽灵部队 |1.1.3 |是
应答器关闭许可 |1.2.4.1 |是
弧光设计局 [Arc Light Bureau] |1.12.3 |是
弧光设计局拓展 [Arc Light Bureau Plus] |1.1.1 |是
彻底扫描星系 |1.2.3.1 |是
恶魔航电 原汁原味 |3.0.6 |是
战后舰船超量恢复 |1.1.2 |是
战斗不结束修复补丁 |0.0.1a |是
战机拓展 |2.0.5d |是
战机拓展扩展 |1.0.0d |是
控制台 |4.0.8 |是
星坞船插集 |0.26.1 |是
星岩工会 |2.3.1 |是
星系检索网络汉化 |3.1.6 |是
星际帝国 [Interstellar Imperium] |2.6.9 |是
暗影重建局 (WISP版) [Shadowyards] |0.9.6-rc1-Wisp-007 |是
更优的技术挖掘 |2.7.5 |是
更多界面增强 |0.3.3 |是
更大的殖民地 |2.1.b |是
更强的地面防御设施 |1.2 |是
未知星域[Unknown Skies] |3.0.3 |是
水仙 |2.0.2 |是
沃兰缇亚归复局 |0.6.3 |是
沙耶之歌 |5.6.5 |是
海盗多用途船包 |0.6.0 |是
深红科技 |1.3.7 |是
清理星域 [Clean Sector] |0.2.6 |是
爪牙之路 |v1.3.0 |是
畅饮美酒 |1.1.d |是
真正的自动化舰船 Truly Automated Ships |2.0 |是
系统标识 [System Marker] |0.1.11-RC1 |是
经验军官Progressive Officer |1.9.8 |是
绿皮星域 中文版 |0.71c |是
绿皮星域骤雨扩展 |0.21 |是
翡翠工程局 |1.19 |是
自动命令 |0.3.4 |是
航母现代化 |1.2.2 |是
舰船/武器拓展 [Ship/Weapon Pack] |1.16.6 |是
舰长休息室 |1.0.2 |是
英仙座天才交易员 |0.5.0 |是
蓝海渔业[BlueSeaFisher] |1.3.1 |是
让小飞机再次伟大! [make fighter great again!] |2026.9.25 |是
详细战斗报告[Detailed Combat Results] |5.4.5 |是
详细武器数据 |0.99 |是
超空间跳跃 |2.6.0 |是
辛达强权蓝图包 |1.14 |是
远星电台 |1.0.4 |是
远星经济学 |0.0.1b |是
速子特勤局 |1.6.0 |是
锡罕帝国 [Xhan Empire] |3.0.1 |是
阿卡涅西斯之遗 |v1.9.14 |是
阿隆戴特计划 |1.1.0 |是
雇主加成 |1.999999ggggg |是
风险矿业公司 |0.5.0e |是
骤雨军事公司 |3.3.8 |是
骤雨军事公司 - 动漫风头像包 |1.2 |是
鬣犬矿业(Junk Yard Dogs) |3.7.0 |是
黑石船坞 [Blackrock Drive Yards] |0.9.5_cn0.4.5 |是
~Vram Optimizer |1.0.48 |是
A New Level of Confidence |20 3.3 |否
A New Level of Confidence |20 3.3 Fast |否
A New Level of Confidence |25 3.3 |否
A New Level of Confidence |25 3.3 Fast |否
A New Level of Confidence |30 3.3 |否
A New Level of Confidence |30 3.3 Fast |否
A New Level of Confidence |40 3.3 |否

(以上内容由 远行星号 报错信息收集工具 自动生成,生成工具版本 1.1.5).
    复现步骤:
    本帖最后由 Royic 于 2026-11-12 05:20 编辑

    0. 先读这一段这份文档里的每一项改动都配了备份目录和回滚命令。改动分三类,处理原则不同:
    类型
    例子
    改动的风险
    引擎/框架层
    fs.common_obf.jar、Graphics.jar
    高。改的是编译产物字节,升级 mod/游戏后会被覆盖,需重打
    数据/配置层
    LunaSettings 缓存、CSV、设置项
    中。改错了会导致启动崩溃或设置项错乱
    资源层
    .wpn 路径、.ogg 编码、strings
    低,但必须逐项验证资源存在,否则启动即 Fatal
    最容易踩的坑:把 CSV 里被 # 注释掉的一行"恢复"成数据行。若该武器引用的贴图不存在,游戏启动会直接 Fatal: Image with filename [...] not found or failed to load。本机在 22:30 踩过一次,已回滚。恢复任何被注释的 csv 行前,必须先验证它引用的每一帧贴图存在。

    1. 改动总览
    #
    文件
    改动
    备份 / 回滚
    1
    mods/ChineseInputFix/mod_info.json
    jars 增加 lib/jna-5.14.0.jar(612→637 B)
    backup_cif/
    2
    Miko_Simple.txt
    -classpath 加 jna;新增 -Dfossic.ime.enabled=false(3149→3219 B)
    backup_cif/
    3
    starsector-core/fs.common_obf.jar
    com/fs/util/C.class setter 末尾补 putfield C.String(310034→308093 B)
    backup_modfilter/fs.common_obf.jar.orig
    4
    saves/common/LunaSettings/*.json.data
    144 个缓存全量重建(去向:各 mod 自己的 CSV)
    LunaSettings_snapshot_before_rebuild/(144 文件)
    5
    3 个 mod 的 LunaSettings.csv
    Float→Double、补引号、去前导空格
    backup_csvfix/ + fix_csv_types.py --restore
    6
    mods/shaderLib/jars/Graphics.jar
    ① TextureData.class 1 字节 ② 重编译 ShaderModPlugin.class
    backup_autogen/Graphics.jar.orig(原始)、.before_onGameLoad(仅补丁①)
    7
    mods/shaderLib/cache/
    17496 张法线贴图 / 613 MB
    旧缓存 cache_probe_bak/(4930 文件)
    8
    mods/shaderLib 设置
    autoGenNormals 由 false 改为 true
    toggle_autogen.py --off + probe_autogen_backup/
    9
    mods/jc_sf/jars/jc_asm.jar
    已撤销。补丁是为 2.2 的缺陷打的;2.2 修好后自检能正常通过,jar 已还原成原始文件(1779610 B,md5 998ceb48be83)
    backup_jc_sf/
    10
    5 个 armaa_blinker_*.wpn
    贴图路径从旧版改到实际位置
    fix_armaa_blinker.py --restore
    11
    若干 weapon_data.csv / ship_data.csv
    #标题,数据 拆成两行,共 27 项
    fix_hash_rows.py --restore + backup_hashrows2/
    12
    mods/ORK/sounds/sfx_wpn_energy/melta_fire.ogg
    Ogg-FLAC → Vorbis(424798→61089 B)
    backup_ork/melta_fire.ogg.orig
    13
    mods/ORK/sounds/sfx_wpn_energy/bt_holy_aura_charge.ogg
    0 字节空文件 → 0.2 s 静音 Vorbis
    原本就是空文件,删掉即恢复
    14
    mods/nexerelin/data/strings/strings.json
    取消注释 tooltip_followersDiplomacy 并补中文
    backup_ork/nexerelin_strings.json.orig
    15
    mods/EVEOverviewCommand/data/config/LunaSettings.csv
    补 68 个漏配的 Keycode 字段(16 槽位 × 4 + 全体开/关 × 4),11373→18355 B
    backup_eve/LunaSettings.csv.orig + fix_eve_keybinds_csv.py --restore
    16
    mods/Narcissu/data/config/LunaSettings.csv
    补 12 个 cpi_large_* 字段(15963→19157 B),消除每局 3084 条 not found in JSONObject
    backup_narcissu/LunaSettings.csv.orig + fix_narcissu_large_csv.py --restore
    17
    48 个 .wpn + 3 个 .proj
    文件名改成 spec 内部 id,修 shaderLib Failed to find JSON(98 → 0)
    backup_wpn_rename/manifest.json(51 条)+ fix_wpn_filenames.py --restore
    18
    mods/PAGSM/data/strings/strings.json、mods/pigeonpun_bionicalteration/data/strings/strings.json
    补 11 个缺失的文案 key,消除启动期 Missing string(11 → 0)
    backup_strings/(2 个文件)+ fix_missing_strings.py --restore
    19
    bionicalteration.jar ×2、TraverserDesignBureau3.jar
    4 条伪 ERROR 的日志调用精准降级为 INFO(2.14;ERROR 100 → 96)
    backup_pseudo/ + fix_pseudo_errors.py --restore
    20
    starsector-core/starfarer_obf.jar
    com/fs/starfarer/campaign/ui/class.class → sizeChanged(FF)V 补 instanceof CampaignPlanet 保护,防 OpenCoreTab 的 ClassCastException(2.21;37421→37434 B)
    backup_core_ui_cast/ + patch_core_ui_cast.py --restore
    当前关键文件指纹(用于确认改动仍在位):

    2. 逐项详情2.1 中文输入(ChineseInputFix)现象:启动后每 50 ms 刷一条 NoClassDefFoundError: com/sun/jna/Native,共 421 条,中文输入不可用。
    根因:ChineseInputFix 的 JnaBootstrap 用 Unsafe 把 JNA 注入 system classloader,在 JDK 28-beta 下注入失败(error:inject failed),无限重试。
    改动:不走注入,直接把 jna-5.14.0.jar 放进 classpath:
    • mod_info.json 的 jars 增加 lib/jna-5.14.0.jar
    • Miko_Simple.txt 的 -classpath 增加 ../mods/ChineseInputFix/lib/jna-5.14.0.jar
    顺带确认:-Dfossic.ime.enabled=false 不影响中文输入。CIF 的 43 个 class 无一引用 fossic;汉化 IME 的 ssime.dll 在 starsector-core/native/windows/,而 Miko 的 java.library.path 指向 mikohime/windows,本来就加载不到。加上该参数反而少一套窗口 WndProc 钩子。
    验证:jna NoClassDef=0、JNA注入失败=0、窗口子类化成功=1、中文主菜单正常。

    2.2 Fast Rendering 的 modId 过滤缺陷(fs.common_obf.jar,最核心的一处)现象:所有 mod 的 LunaLib 设置界面串味——每个 mod 点开都显示同一个 mod(aitweaks)的配置;LazyLib 报 v0.0 built for UNDEFINED;日志 3168 条 Loaded default settings data 全部标着 from aitweaks,而实际只有 75 个 mod 有设置。
    根因:FR 把 com.fs.starfarer.loading.FileLoader 替换成自己的 FileLoaderFast,建了一个扁平的资源索引(启动时一次性快照 96173 个文件),并且 忽略 modId。引擎原来用 com.fs.util.C.String 字段携带"当前 mod"上下文,FR 的 agent 会把这个字段重定向到 FileLoader_locationFilter——但汉化/优化的 com.fs.util.C 的 setter 把写 C.String 的指令删掉了,导致 filter 永远为空,所有 loadCSV/loadJSON/loadText(path, modId) 都退化成"取全局第一份"。
    改动:只改 fs.common_obf.jar 里 com/fs/util/C.class 一个条目,在 setter 末尾追加 putfield C.String(让它恢复写字段),由 FR agent 自己重定向。
    验证(实机,144 mod):
    注意:这一处是整个修复链的地基。它同时解决了 LunaSettings 串味、LazyLib 版本号、jc_sf 自检误判的诱因之一。
    回滚:python work/ss_mods/patch_mod_filter.py --restore

    2.3 LunaLib 缓存污染现象:大量 mod 的设置项缺失,not found in JSONObject 69 条;部分 mod 的缓存只有 22 个 key(应为其 CSV 声明的 51 个)。
    根因:2.2 的 modId 缺陷存在期间,LunaLib 把所有 mod 的默认值都写进了同一个"错误归属"的缓存。而 LunaSettingsLoader.saveDefaultsToFile() 只 put 不 delete、且不覆盖已存在文件,所以污染被永久固化在磁盘上。
    改动:rebuild_luna_caches.py 按各 mod 自己 CSV 声明的字段重建全部 144 个 .json.data:
    • 归属表 = 各 mod 的 data/config/LunaSettings.csv
    • 别人的 key → 删除;来源不明的运行时 key(SettingsCreator)→ 保留;类型与 fieldType 矛盾 → 纠正
    关键约束:只能写 {} 不能删文件。loadCommonJSON 找不到文件会 fallback 到 settings.loadJSON("data/config/LunaSettingsDefault.default"),而 FR 的 modId 缺陷会让它把 lunalib 的默认表灌进来。
    验证:aitweaks 残留 key 3168 → 22(只剩它自己),总 key 5272 → 2126,luna_missing=0,ExecutionException=0,连跑 4 次缓存漂移为 0。
    回滚:python rebuild_luna_caches.py --restore;整目录快照 LunaSettings_snapshot_before_rebuild/

    2.4 LunaSettings.csv 的三类源头缺陷现象:3 个 mod 的设置项值被写成字符串("10"、"60"、"Radio"、"Boolean"),且 UI 里没有可编辑控件。
    根因(三层):
    • LunaLib 的 when(type) 没有 Float 分支(LunaSettingsLoader.kt),SettingsType 枚举也没有 Float,UI 落到 else -> null → Float 设置项根本没有控件;min/max 也只在 Int/Double 时才解析。而 LunaSettings.getFloat() 的文档写明 "floats dont exist in .json's, it just converts a saved double" —— Float 字段在 JSON 里本就该存 Double,CSV 写 Float 属于误用。
    • CSV 行内未转义逗号导致列错位(dynamicbattlreinforcement 两行),fieldType/defaultValue 取到了错位的值。
    • Boolean 默认值带前导空格(Boolean, true),Kotlin 的 String.toBoolean() 不 trim → 判为 false。
    改动(fix_csv_types.py,字节级精确替换 + 备份 + md5 校验):
    • hostileIntercept:Float→Double;Boolean, true→Boolean,true(1992 B,md5 c481fccb2304)
    • scan_those_gates:2 处 Float→Double(931→933 B,md5 b851ceba2269)
    • dynamicbattlreinforcement:2 行 fieldDescription 补引号恢复列对齐(11698→11702 B,md5 1bbabdd1523f)
    回滚:python fix_csv_types.py --restore,备份 backup_csvfix/

    2.5 shaderLib 法线贴图(两处改动,一个根因)现象:加/删任意 mod 后启动要很久,日志 8765~8863 条ERROR TextureData - Failed to load texture when generating normal map for X (...): Image with filename [cache/...] not found or failed to load,并生成 400+ MB 缓存。
    根因(三层,实机证明,不是猜):
    • 触发:autoGenMissingNormalMaps() 用 gameVersion + Σ(modId+version) 的 hashCode() 做缓存哈希,存在 shaderlib_cache_hash.data 且值不匹配时 invalidateCache = true → 全部重生成。
    • 失败点:不是生成失败,是"生成后立刻回读失败"。报错说找不到的文件实际存在且是合法 PNG(已实测:681 B,IHDR 正常;17496 个文件全部有效)。
    • 机制:autoGenNormalMap() 写完 PNG 后调用 Global.getSettings().loadTexture("cache/xxx.png") 回读。而 FR 的 FileLoaderFast.cachedFiles 是启动期一次性快照(日志 Cached 96173 files in 1005ms,构造函数只跑一次),运行期永不更新 → 新写的文件不在索引里 → 查不到 → 抛 Image with filename [...] not found or failed to load。代码本来就是"先写盘再回读",问题不在时机,在索引。同一个进程里等多久都没用。
    改动①(1 字节,缓存优先)
    TextureData.autoGenMissingNormalMaps() 开头:
    Graphics.jar 内 org/dark/shaders/util/TextureData.class 文件偏移 0x774C,0x04(iconst_1) → 0x03(iconst_0),即默认 invalidateCache = false。
    效果:autoGenNormalMap() 的 if (!invalidateCache) 分支先读缓存,命中即返回;mod 变更只打一行 Mod list changed 日志,不再丢整个缓存。
    脚本:patch_shaderlib_autogen.py(--apply / --restore;dry-run 会校验签名 3d 04 b3 02 fb 唯一命中,已打补丁时报 already patched)
    改动②(重编译一个类,让新贴图当次生效)
    FileLoader.initGameplay() 会把 fastLoader 置 null,之后加载走原版 FileLoader_loadInputStream_vanilla(真实文件系统)。所以只要在 gameplay 阶段做,就能读到本进程刚写的文件。
    原代码把 autoGenMissingNormalMaps() 挂在 ShaderModPlugin.onApplicationLoad() 末尾(mod 加载期,索引还活着)→ 必失败。已移到 onGameLoad(boolean)。
    • 源码:mods/shaderLib/org/dark/shaders/ShaderModPlugin.java(mod 自带源码,已验证与 jar 同源:编译产物 5931 B / 1728 B 与 jar 内完全一致,javap 方法签名 diff 为空)
    • 编译:JDK 21 javac,classpath = Graphics.jar + starfarer.api/obf.jar + lwjgl/lwjgl_util + log4j + json⚠️ Git Bash 传 ; 分隔的 classpath 会坏,必须用 PowerShell 传参
    • 校验:javap -c 确认 onGameLoad:43 有 invokestatic TextureData.autoGenMissingNormalMaps,onApplicationLoad 已无(原版在 :413)
    实测数据(4 次真实启动)
    运行
    条件
    Mod list changed
    法线贴图 ERROR
    总 ERROR
    结束时间
    空 cache
    —
    1
    8765
    8900
    163 s
    完整生成
    —
    1
    8863
    9009
    448 s
    第二次启动
    cache 完整 + hash 匹配
    0
    0
    146
    231 s
    补丁 + hash 故意不匹配
    模拟加 mod
    1(仅日志)
    0
    146
    231 s
    另:主菜单阶段日志有 192 条 com.fs.graphics.TextureLoader(原版加载器),同阶段 Auto-generated normal map 49 条全部成功 0 报错 —— 直接证明 gameplay 阶段"写盘 → 立刻回读"可行。
    当前状态:autoGenNormals=true、enableNormal=true、preloadAllMaps=false;cache 17503 个 PNG / 613 MB;VRAM 实测仅 212928 B(208 KB),因为 preloadAllMaps=false 是按需加载。
    ⚠️ shaderLib 更新会覆盖 Graphics.jar,两处补丁都会丢。升级后先跑 python patch_shaderlib_autogen.py dry-run,看到 signature hits 1 再 --apply;ShaderModPlugin 则要按上面的编译流程重做(或直接从 .before_onGameLoad 备份里取回已编译的 class)。

    2.6 jc_sf「幽灵部队模组文件损坏」误判(已撤销,不再需要补丁)现象:jc_sf 每次启动弹"幽灵部队模组文件损坏",并 abort 启动。
    根因:Jc_asm_ModPlugin.modFileCheck() 把 mod_info.json 字段 + 几个 loadText(path, "jc_sf") 读到的文本(data/config/LunaSettings.csv、settings.json、data/hullmods/hull_mods.csv、data/hulls/ship_data.csv、wing_data.csv)拼起来算 MD5,与 class 里两个基准常量比对:
    受 2.2 的 modId 缺陷影响,loadText(path, "jc_sf") 读到的是别人 mod 的同名文件 → MD5 不符 → 误判 → abort 启动。
    当时的处置:jc_asm.jar 里 1 字节等长补丁,Jc_asm_ModPlugin.class 偏移 38204,0x9a(ifne) → 0xa7(goto),跳过校验。
    现已撤销(2026-11-11 23:24 实机验证):
    结论:自检逻辑本身没问题,之前的失败是 2.2 的连带症状。2.2 修好后 loadText 能正确读到本 mod 的文件,MD5 自然匹配。这个补丁没有存在的必要,已彻底移除。
    当前状态:mods/jc_sf/jars/jc_asm.jar = 原始文件,不含任何补丁。patch_jc_sf.py 保留仅作历史记录,平时不要跑 --apply。

    2.7 CSV # 吞行导致 27 个武器/船体消失现象:77 条 Weapon spec [X] not found in weapon_data.csv + 26 条 Ship hull spec [X] not found in ship_data.csv。
    根因(两个叠加):
    • # 吞行:Starsector 把行首 # 的整行当注释。部分 mod 把分组标题和该组第一条数据写在同一行(漏换行):全库 76 行 / 18 个 csv 是这种形态。
    • 资源真的缺失:一部分被注释的武器,其 .wpn 引用的贴图根本不存在(armaa_blinker_green 是 DECORATIVE 动画武器、numFrames:20,而 graphics/.../armaa_blinker_green/ 整个目录都没有)。作者正是因为资源不全才注释掉的,不是手滑。
    处理(27 项已恢复)
    • 5 个 blinker 是纯路径 bug(作者把贴图移进 graphics/armaa/ 并把前缀改成 eis_,.wpn 忘改):帧数核对:command 2/2、green 20/21、red 20/21、white 20/21、white30 30/31(多 1 张预览图),armaa_blinker.class / armaa_blinker3.class 存在。脚本 fix_armaa_blinker.py(只改 10 处 sprite 路径,id 不动;bytes+utf-8 读写保证不加 BOM;每帧在盘上校验后才写)。
    • 22 项 # 吞行:资源全部验证齐全后才拆成「#标题 + 空 name 列的数据行」。涉及 armaa、CjyToyBox、MFGA、fob、kk_kz、looted_sector_cn、Eridani_Combine、Avali_Mod_Core。
    数据变化:武器缺行 77 → 56,船体 26 → 20,ERROR 140 → 113(后续再降到 111)。
    ⚠️ 最关键的一条教训:本机 22:30 在 armaa_blinker_green 上踩过一次 Fatal——恢复被注释的行 = 让引擎去加载它的贴图;贴图不存在 → 未捕获的 RuntimeException → 启动即死。未验证资源完整性之前,绝对不要批量恢复被注释的 csv 行。 验证必须用全局索引(starsector-core + 所有 mods 的 graphics),只查 mod 自己的目录会误判(把 core 的 graphics/fx/empty.png、light_reaper_launcher_turret_base.png 当成缺失)。
    回滚:

    2.8 音频与文案(3 处)a. melta_fire.ogg 是 FLAC
    Starsector 只认 Vorbis → ERROR sound.F - This Ogg bitstream does not contain Vorbis audio data。
    转码(D:\tools\FormatFactory\ffmpeg.exe):
    b. bt_holy_aura_charge.ogg 是 0 字节空文件(无任何引用)。用 -f lavfi -i anullsrc=r=44100:cl=stereo -t 0.2 -c:a libvorbis 生成 0.2 s 静音(4046 B)。
    c. tooltip_followersDiplomacy 被汉化注释掉
    mods/nexerelin/data/strings/strings.json 第 3300 行原文:
    值还是英文,# 一注释引擎就找不到 key → Missing string。改为:
    195627 → 195618 B(差 9 字节可精确对上:去 # 1 B + 中文 60 B vs 英文 68 B),行数/括号数与原件完全一致。

    2.9 EVEOverviewCommand:68 个键位字段漏配(补 CSV,不补缓存)现象:每局日志 64 条 LunaSettings: Value crsid_hk_slotNN_xxx of type Int not found in JSONObject;更实际的问题是这 64 个武器槽位热键在 LunaLib 面板里根本不存在,玩家改不了键。
    根因:jars/EVEOverviewCommand.jar → data/scripts/combat/CRSIDLunaKeybinds.read() 会向LunaLib 读 68 个 key(crsid_hk_slot01..16_{mod,key,ai_mod,ai_key} + crsid_hk_all_on/off_{mod,key}),但 data/config/LunaSettings.csv 只声明了 56 个字段,一个 slot 都没有;jar 里也搜不到SettingsCreator / LunaSettings.add 之类动态注册代码 → 作者漏配,不是有意为之。
    读取侧有兜底(CRSIDLunaKeybindsReader.getInt(key, def) 缺 key 时返回默认值),所以功能不坏,只是噪音 + 不可配。
    为什么不能靠补缓存:之前用 patch_eve_keys.py 往 .json.data 里塞 64 个 key 能消噪音,但LunaLib 的界面是按 CSV 生成的,缓存里有 key 也不会长出控件;而且缓存一重建(加/删 mod)就又丢了。实测删掉 64 个 key 重启,luna_missing 立刻回到 64 —— 不会自愈。
    改动:向 LunaSettings.csv 追加 72 行(68 个 Keycode + 2 个 Header + 2 个 Text),全部并入已有的「键位」页签。默认值来自 javap 反编译:
    为什么不新开一个页签(一开始是这么做的,实测有问题):LunaLib 的页签是每行 2 个自动换行,LunaSettingsUISettingsPanel 把第 2 行页签画出来后,内容面板(subpanel,用 currentTabSpacing定位)会盖在第二行页签上,导致第 4 个页签看得见、点不到。所以槽位直接并进「键位」页,页签总数保持 3 个。
    验证(真实 Miko 栈,144 mod)
    回滚:python fix_eve_keybinds_csv.py --restore(备份 backup_eve/LunaSettings.csv.orig,md5 5e405157b774)
    注意:patch_eve_keys.py 现在是历史遗留,不要再跑——CSV 声明后 LunaLib 会自己生成这些 key,手工补缓存只会掩盖问题。

    2.10 Narcissu:12 个 cpi_large_* 键没进 CSV(每局 3084 条 ERROR)现象:进战役后日志刷 LunaSettings: Value cpi_large_xxx of type Double not found in JSONObject(ModID: Narcissu),12 个键 × 257 次 = 3084 条,一局约 1.5 MB 日志。
    根因(字节码级):NarcissuSettings.fingerprint() 会遍历 ALL_FIELD_IDS(66 个)逐个取值拼指纹字符串,NarcissuModPlugin$MusicWatcher 周期性调它;而 LunaSettings.csv 只声明了cpi_large_ammo 一个 large 键,另外 12 个有 getter、有默认值、没声明 → 每次调用 12 条错误。
    这 12 个键目前没有消费方:Narcissu_Large(船插「舰船姿态强化」)实际读的是foundation_max_speed / accel / decel / turn_accel / turn_rate,Narcissu_Combat 只多读 large_ammo。所以它们属于作者遗留的死键,只被指纹函数读到。
    改动:往 CSV 追加 12 行 Double(tab=舰船姿态强化,锚点 cpi_tab_Large 之后),默认值取自各 getter 的内联常量:
    描述里统一注明「本项暂未生效,仅用于对齐设置表」,避免玩家以为调了有用。
    验证:启动后 Narcissu 缓存从 54 → 66 keys(= ALL_FIELD_IDS 数量),12 个新键值全对;not found in JSONObject = 0;ERROR 总数仍是 111。(日志里的 257 次调用要进战役才发生,本次只验证到缓存就位。)
    代价:LunaLib 面板「舰船姿态强化」页多 12 个滑块,实际不生效。
    回滚:python fix_narcissu_large_csv.py --restore

    2.11 48 个 .wpn + 3 个 .proj 文件名与 id 不符 → shaderLib 读不到 JSON现象:每次读档 WARN Failed to find JSON for weapon <id> 98 条(48 个唯一 id)。
    根因:shaderLib 用 id 拼路径读 JSON:
    而部分 mod 的文件名与内部 id 不一致,例如:
    后果不只是日志:numFrames 那段逻辑在 weaponJson != null 分支里,读不到 JSON 就只处理第 0 帧,动画武器的后续帧没有法线贴图。
    为什么改名是安全的:游戏本体扫 data/weapons/*.wpn 后按内部 "id" 建索引,不看文件名;这些武器今天本来就能正常用。改名只改文件名,内容不动(脚本逐个校验 size+md5 前后一致)。
    执行:48 个(12 个 mod)。跳过 19 个——目标 <id>.wpn 已存在(同目录或别处已有同名文件,改了必然撞名),这部分不是能靠改名解决的。
    关于 ls_zzap_bomb_chute:第一轮被当成「代码按旧文件名加载」排除了,复查发现是误报——.wpn 里的 "everyFrameEffect":"data.weapons.ls_zzap_bomb_chute_everyframe" 指的是类名,该类的 .java / .class 里既没有读文件也没有 loadJSON,只是名字里含这段字符串。所以不需要改 mod 代码,直接改名即可;排除清单已清空并补了注释。
    验证:静态检查「按 id 能否拼出 data/weapons/<id>.wpn」——不可解析数 48 → 0;实机启动正常,ERROR 仍 111、dup_ids 仍 1(改名不产生重复 id,因为 id 没变)。读档实测:Failed to find JSON for weapon 98 → 0。
    第二批:3 个 .proj(MIRV 弹头)。shaderLib 还有一支递归:
    读档日志里剩下 6 条 Failed to find JSON for MIRV,对应三个文件名不匹配的弹头 spec:
    同样按 id 改名即可(.proj 也是按内部 id 建索引,且这三个没有任何代码按路径引用)。只改这三个:全库还有 60 多个 .proj 文件名≠id,但 shaderLib 根本不查它们,不动以减少影响面(脚本里用 PROJ_ID_SUFFIX / PROJ_EXTRA_IDS 白名单限定)。
    回滚:python fix_wpn_filenames.py --restore(按 manifest.json 反向改名)
    注意:mod 更新后文件名会回到作者的写法,需要重跑 fix_wpn_filenames.py(幂等,已改过的会跳过)。

    2.12 Missing string 11 条:两个 mod 缺文案 key现象:启动期 11 条 ERROR com.fs.starfarer.loading.StarfarerStrings - Missing string: [xxx]
    先纠正一个以前的误判:这两组都不会在界面上显示 Missing string,纯日志噪音。
    • PAGSM 那 10 个:data.hullmods.sfchwi 的 <clinit> 里读进 10 个静态字段(HWISpecials / HWIText1,2 / WOCTitle / WOCText1..6),但这批字段没有任何读取方——真正显示用的是 getString(int) → PAGSM_hullmods 段的 sfchwi_1/2/3(已存在且译文正确)。作者改过实现,旧字段忘了删。
    • tooltip_isDevmode:lunaconfighelper.addSetting("isDevmode", ...) 会拼 "tooltip_" + id去 ps_lunasettings 段找悬停说明。找不到时 mod 自己会 startsWith("Missing string:") 判断后置空字符串 → 界面上是空 tooltip,不是错误文字。
    改动
    • mods/PAGSM/data/strings/strings.json:新增顶层段 sfc_pagsm(10 个 key)。文案取作者自己的用词(PAGSM_hullmods.sfchwi_2/3 与 descriptions.csv 里 sfcoverclock 的描述拆行),不自行编数字。
    • mods/pigeonpun_bionicalteration/data/strings/strings.json:往 ps_lunasettings 补 1 个tooltip_isDevmode。
    ⚠️ 踩过的坑:strings.json 里重复 key 会让游戏直接 Fatal
    第一次补的时候顺手也加了 name_isDevmode「以防万一」,结果它本来就有("开发模式"),启动立刻:
    已还原并去掉该 key。fix_missing_strings.py 现在写盘前会跑 _dup_keys() 全文件扫描,发现重复键直接 assert 失败、不写盘。以后改任何 strings.json 都要先查重复。
    验证(真实启动,109101 行日志)
    回滚:python fix_missing_strings.py --restore(备份 backup_strings/)

    2.13 Cannot find sprite 6 条:只定位,未修现象:读档时 6 条
    涉及 4 个武器(SGB_Ground_LazerGun、SGB_Ground_BlastGun、JKF_JINGWU_f_head_w、JKF_sniper_hil),共 6 个 barrel 槽位。
    根因(已查实)
    • Global.getSettings().getSprite(String) 的实现是 new Sprite(path) 再包一层——纯查表,纹理没预加载时 getHeight() 就是 0。
    • 这 6 个路径都是发光层贴图(_HG / _BG / _G / _glow),被 mod 当作turretGunSprite / hardpointGunSprite(炮管)使用。日志里从来没有Loading image [...] 它们的记录 → 游戏没预载 → 高度 0 → 报错。
    • 文件本身完好:8-bit RGBA PNG,22x38 / 30x63 / 20x20,都能正常解码。
    为什么没修
    修法只有一条:改 mods/shaderLib/.../TextureData.java,在 getSprite(spritePath) 失败后补一次 forceMipmapsFor + loadTexture 再重试(缓存分支就是这么干的,已被证明可行)。
    不做的原因:
    • shaderLib 附带的 .java 与 jar 里的 .class 不是同一编译器产物(原版 major=61 且用 $SwitchMap 分派,javac 21 重编译会换成另一种 switch 实现),重编译等于换掉一个 50KB 核心类的来源,风险远大于收益;
    • 收益几乎为零:这 6 个是发光层,法线贴图对自发光贴图基本没有视觉意义。
    若将来要做:务必先用 --release 17 编译、javap 比对方法签名(应为 SAME),再替换 jar 内 TextureData*.class 五个条目,然后跑一次读档,验收标准是Cannot find sprite = 0 且 Loading image [cache/...] 的数量不掉、Estimated VRAM 正常。
    2.14 伪 ERROR 4 条:字节码精准降级(已修)现象:启动期 4 条 ERROR,内容全是正常流程信息:
    根因:4 处日志调用选错级别,不是功能异常。
    • 三条 merging ... files 在 ba_*manager.load*() 方法开头,是log.error("merging ... files");同一个方法后面打真实结果用的却是 log.info(...),属于作者手误(同文件里合并失败/异常路径用 error 是对的)。
    • 「确定获取到了前置mod」不在 shaderLib 里。全库按 UTF-8 + GBK 扫描(find_pseudo_error.py,223 个 jar)命中mods/ArcLightBureau/jars/TraverserDesignBureau3.jar → data/scripts/TDBModPlugin.class:onApplicationLoad() 里用 Global.getLogger(TextureData.class) 借了 shaderLib 的 logger 名,成功分支打 log(Level.ERROR, "确定获取到了前置mod")。失败分支「没有获取到前置mod」也是 ERROR。⇒ 日志看起来像 TextureData 打的,其实来自弧光设计局。
    修法(fix_pseudo_errors.py;不重编译、不动功能、可逆)
    • BA 三个 class:只把 merging ... files 的 ldc 后面那条invokevirtual Logger.error:(Ljava/lang/Object;)V 操作数改为本类已有的Logger.info:(Ljava/lang/Object;)V Methodref,各 2 字节:#48→#177、#75→#461、#48→#169。这些类里其它 error 调用一条没动。
    • TDBModPlugin:常量池尾部追加 3 项(17 B:Utf8 "INFO" + NameAndType + Fieldref),把成功分支的 getstatic #62 (Level.ERROR) 改成新增的 #551 (Level.INFO)(2 B)。失败分支保留 Level.ERROR——shaderLib 真没启用时那是有效报错。只在常量池尾部追加,原有索引不变,Code 里的跳转/行号偏移都是相对的,故安全。
    • Patcher/zh-CN/jars/bionicalteration.jar 与运行时 jar 的 md5 相同,两个都打,否则重跑汉化 Patcher 会把补丁覆盖掉;Patcher/Original/(英文原版)不动。
    • 写盘前整 jar 备份到 backup_pseudo/ + manifest.json(改前/改后 md5、size),写盘后重新读回逐条比对;--restore 一键回滚,--verify 只读,重复 --apply 幂等。幂等判断必须同时扫 ERROR 和 INFO 两个 Fieldref,否则会把失败分支误降级。
    实机验收(verify_modfilter.py PSEUDO 170;20.7 MB / 135256 行,含一次读档)
    注:verify_modfilter.py 报的 fatal 2 是 FATAL 子串计数误报——命中的是data/variants/threat/overseer_unit_FATAL.variant 这个文件名(2 行 INFO),不是致命错误。
    回滚:python work/ss_mods/fix_pseudo_errors.py --restore

    2.15 armaa 3.3.6 汉化版 → 3.3.8 外文原版(移植式升级,已完成)背景:作者连更 3.3.7 / 3.3.8,只有外文原版(F:\Games\armaa-3.3.8.zip),汉化版停在 3.3.6。用户另外提供了 F:\Games\armaa-3.3.6.zip(英文原版基准),于是可以做三路对比:A=3.3.6 EN / B=本机 3.3.6 CN / C=3.3.8 EN。
    为什么值得升:4 个崩溃修复(Rajanya 自动装配、GuarDUAL 导弹硬点、Spare Chassis 无舰队数据、Bellator 战后 ConcurrentModificationException)、WINGCOM/OVERLORD 着舰循环与 AI 核心重复返还、UI 缩放下的 Watchdog/Karma 光环、以及需要 PatchLib 的战机友伤 AI 修复(本机已装 PatchLib)。
    三路结论(compare_armaa_3way.py):
    ⇒ 以 B 的汉化数据为基底、逐条应用 A→C 改动,而不是拿 C 覆盖 B。
    组装(build_armaa_338.py,产出 armaa338/build/armaa,2488 文件):
    • 完整复制 C 骨架 → 用 B 覆盖 224 个纯汉化文件(逐字节一致)。
    • 6 个冲突文本以 B 为基底应用 A→C 的列级改动:hull_mods.csv +1 行(新 hullmod armaa_renderAboveParent,中文名/描述已译)、ship_data.csv 6 行 7 个数值/tags、weapon_data.csv armaa_armorPod 描述+数值(新机制中文重译,11 个 %s 与 customPrimaryHL 对齐)、descriptions.csv armaa_siegeMode 新描述重译、GuarDUAL variant 去掉 2 个 hullmod、mod_info.json 版本 3.3.8 + 中文名/描述。
    • jar 取 C,字体必须取 B:orbitron48aa/96aa.fnt 汉化版 460 行 / 52 KB(中文字形表),两个英文原版都只有 100 行 / 10 KB;graphics/armaa/fonts/新建文件夹/ 4 个文件也保留。
    • 保留我们修过的 5 个 armaa_blinker_*.wpn(EIS 新路径)——上游 3.3.8 仍旧引用不存在的graphics/weapons/decorative/armaa_blinker_*,没修。
    • 保留我们之前放出的 # 注释 csv 行(以 B 为基底,自动保留)。
    升级后踩的两个坑(都已修,脚本已固化)
    现象
    根因
    修法
    启动即崩:ExecutionException ×2、.. and starting with a / are not allowed in filenames、MechaModPlugin.onApplicationLoad(188)
    3.3.8 用 getString("armaa_wingCommander","dialogueFont") 取字体路径;汉化版 strings.json 被重写成 12 个自有分类,丢了 armaa_wingCommander / armaa_missionBGs 两个非文案配置分类(dialogueFont 是 .fnt 路径,不是可翻译文本)→ loadFont 拿到非法名字,且该 RuntimeException 没被 catch
    merge_armaa_strings.py:中文优先 + EN 独有键并集,补 2 个分类 / 15 个 key(dialogueFont = graphics/fonts/orbitron16.fnt,文件在 core)。strings.json 中文 11199 字一字未动
    读档 8 条 Failed to find JSON for weapon:armaa_hazardHead / armaa_aleste_flamer_right_copy / armaa_ashura_torso_green / armaa_guardual_minigun_right
    3.3.8 删了这 4 个 <id>.wpn,只留下同 id 的异名副本(armaa_mushaHead_m3.wpn、armaa_aleste_flamer_right - Copy.wpn 等)。shaderLib 按 loadJSON("data/weapons/"+id+".wpn") 拼路径 → 找不到。id 来自 weapon_data.csv / variants / skin,与存档无关,新开档照样会报
    按内部 id 把 4 个异名副本重命名回 <id>.wpn(同 2.11 做法,不引入重复 spec)。贴图逐项验证过(3 个在 core,其余在 armaa 包内)
    实机验收(verify_modfilter.py,三次)
    已知代价与补救:换 C 的 jar 后,汉化 jar 里 16 个硬编码中文的 class 会变英文。224 个数据文件的中文(198965 汉字)与字体都没动。已用常量池替换补回 25 条(13 个 class),方法与覆盖边界见 2.16;汉化版靠 getString 读取的那批文案、以及被重写过的任务物品描述,常量池替换无法一一对应,仍是英文。
    与升级无关的两条(用户读档后打开 UI 触发,旧版下开同样界面也会出现,未动):LunaSettings: Could not find mod CaptainsLog ×4(CaptainsLog 未安装,只有captains_quarters)、exoticatechnologies.refit.RefitButtonAdder.advance NPE ×1(打开装配界面时 Kotlin 非空转换,游戏未崩,后面音乐照常播放)。
    备份 / 回滚:work/ss_mods/backup_armaa_upgrade/ 含armaa_336_cn/(2069 文件 / 81 MB,逐文件 md5 清单)、saves_before_338/(172 文件 / 116 MB)、manifest.json。整目录恢复:把 armaa_336_cn 拷回 mods/armaa(先移走现目录)。apply_armaa_338.py --rollback 只在退役目录还在时可用;正常流程跑完已自动删除该目录。
    重建注意:build_armaa_338.py 的 B 已指向备份目录而不是 mods/armaa,因为升级后游戏目录不再是 3.3.6 CN;重跑 build 才能幂等。重跑汉化 Patcher / 升级 armaa 后,本节的 4 类补丁(字体、blinker、strings 并集、4 个 wpn 改名)都要重新检查。完整重建顺序:build_armaa_338.py → apply_armaa_338.py → patch_armaa_cn_strings.py --apply。

    2.16 常量池替换:把中文文案注入 3.3.8 jar(已做)为什么不能直接按位置配对:inspect_cn_strings.py 先试了「常量池里第 N 个字符串 ↔ 汉化版第 N 个字符串」,对齐率最低只有 1/10(Engage the raiders 被配成「继续」)。原因是汉化版整体重编译,常量池顺序变了;javap 又证实汉化版把按钮文字改成 getString("ArmaArmatura_rulecmd", "armaa_gravionBattle_%d") 动态读取,ldc 序列也对不上。
    可靠的数据来源(armaa_cn_string_map.json,25 对 / 13 个 class):
    • A_only(3.3.6 EN 有、汉化版没有的英文)与 B_only(汉化版新增的中文)做差集;
    • 中文取自汉化版 strings.json 的 ArmaArmatura_rulecmd / ArmaArmatura_campaign;
    • 安全性证明:javap 显示 Continue / Engage the raiders 等只被写进FleetInteractionDialogPluginImpl$FIDConfig.firstTimeEngageOptionText /noSalvageLeaveOptionText 这类纯显示字段,rules.csv 里$option == Continue 出现 0 次 ⇒ 替换不会破坏对话选项 id。
    字节码做法(patch_armaa_cn_strings.py):
    • 只改 被 CONSTANT_String 引用、且没有被 Class / NameAndType / MethodType 等结构条目当作名称或描述符引用 的 CONSTANT_Utf8;把 tag + u2(length) + bytes 换成中文 UTF-8。
    • constant_pool_count 不变、所有常量池索引不变、Code 属性里的偏移都是相对的,所以常量池之后的所有字节逐字节不变(只是整体平移)。脚本对每条断言:new[new_cp_end:] == old[old_cp_end:]、重新解析后条目数一致、目标 Utf8 == 中文、其它 Utf8 一字未动。
    • 25/25 全部命中、0 skipped;jar 1667803 → 1659004 B(重新压缩),class 数仍 557。
    • 游戏目录与 build/armaa 两个 jar 同步打(md5 a8e521de),备份backup_cn_strings/armaa.jar.before_cn_strings(原版 md5 748d494c)。
    补回的 25 条(13 个 class):4 个剧情战斗的「迎战/再次迎战/继续/抵御攻击者」、Can not be assigned → 无法被安装、cataphract 的「星历年 192」落款、Deployment → 部署、两支远征舰队的 3 档「殖民地威胁」措辞、通知的「继续」、time flow altered → 时间流速改变、\x01 Squadron → \x01 中队。
    实机验收(verify_modfilter.py CNSTRINGS 170):ERROR 102 不变、Fatal / ExecutionException / VerifyError / NoClassDef / ClassFormat / NoSuchMethod /Duplicate key / Missing string / Failed to find JSON / Image with filename 全 0,screenshot=1(正常到主菜单)。6 条 java.lang.RuntimeException 与升级前逐条相同(fighterai.cfg 缺失、3 个 MagicLib bounty variant、null 武器 id),非新增。
    覆盖边界(没做的部分):汉化版把大部分长文案改成 getString(...) 从strings.json 读,3.3.8 的 class 仍是硬编码英文 ⇒ 这些文案仍是英文;armaa_RobbingTheCradleMission 的 23 个英文物品描述被汉化者重写成 7 个中文,无法一一对应;cataphract 的 %s 格式串、armaa_ViceDemand 被改写的逻辑、CN-only 的 intelMisc.class 也跳过。这不是 bug,是汉化实现方式决定的。
    想更彻底的话:compare_logic_armaa.py 用 javap 比过 opcode 序列,15/16 个中文 class 在 3.3.6 EN 与 3.3.8 EN 之间完全一致(含cataphract / cataphract2),说明逻辑没变,理论上可以直接把汉化版的 class整体换进 3.3.8 jar,中文全回来。但要先确认 companion 内部类($1 等)与3.3.8 的方法签名兼容,风险高于常量池替换,目前未做。
    回滚:python patch_armaa_cn_strings.py --restore(脚本幂等,重复 --apply 不会覆盖原始备份)。

    2.17 新开档实机验收(3.3.8 移植版 + 常量池中文补丁)用户 03:31 自行启动、03:52:35 新建生涯档 save_Royic_8184782978826651943,一直跑到 CAMPAIGN 心跳正常。日志来自游戏自带 starsector-core/starsector.log(不是 verify_modfilter.py 捕获的 stdout),这是升级后第一次真正走「新档生成」全路径。
    先注意日志滚动陷阱:starsector.log 按 51.2 MB 滚动出 .1/.2/.3 三个分片;当前文件前 23336 行是上一会话的尾巴(时间戳 83400→202849 ms),本次会话从第 23337 行(709 ms) 开始。直接对整个文件 grep 会把两次会话混算(例如 Cannot find sprite 会变成 12 条、ERROR 变 112)。本次统计一律用 tail -n +23337 starsector.log,140950 行。
    本次会话精确指标:
    升级相关项全部干净(这是本节的验收目的):
    • 2.15 的 4 个按 id 改名的 .wpn:4 个 id 各出现 9 次,全是Loading JSON from [data/weapons/<id>.wpn] / Applying data from weapon_data.csv /Loading image [cache/<id>___TURRET0_normal.png] 这类正常加载,Failed to find JSON 0。⇒ 改名修复在新档路径同样生效,法线贴图也成功生成并回读。
    • 2.16 的常量池中文补丁:VerifyError / ClassFormat / NoSuchMethod / NoClassDef 均 0,jar 内 25 条中文串没有破坏任何 class 加载。
    • strings.json 并集(2.15 的字体坑):dialogueFont 相关崩溃没再出现,启动直达主菜单。
    • 2.5 的 onGameLoad 全量法线扫描:首次拿到 84 条生成的大样本,0 条回读失败。
    新档阶段才触发的两个第三方问题(均未动,与本次升级无关):
    • NoClassDefFoundError: second_in_command.SCUtils(1 条,INFO 内嵌堆栈)
      • 调用链:DiableavionicsGen.spawnVirtuous → DiableLastLineFleetFactory.createFleet:193→ applySecondInCommandLoadout:880(mods/diableavionics/jars/Diable.jar)。
      • 根因:Second in Command(mod id second_in_command)没装——144 个启用 mod 里没有,全库 jar 也找不到 second_in_command/SCUtils.class;而 Diable 的 mod_info.json依赖里只有 LazyLib + MagicLib,属于可选软集成却没做 isModEnabled 判空。
      • 影响:被 catch(前置日志 Failed to apply Second In Command officers to Gulf Station.),堆栈后经济、舰队路线、赏金任务全部继续生成,游戏没崩。SIC 本来就没装,那支 Last Line 舰队本来也不该有 SIC 军官 ⇒ 功能影响≈0,只是堆栈噪音。
      • 修法二选一:装 SIC(会改变全部舰队军官玩法,不建议为消一条日志装);或给 applySecondInCommandLoadout 补 mod 存在性判断(需 ASM/重编译,收益低)。
    • ERROR niko_SA.SA_debugUtils - Invalid augment id: SA_defensePlatforms!(1 条)
      • 证据链完整:日志 102423/102424 行先打出augment 外置防御平台 missing mod secretsofthefrontier, skipping——stationAugmentStore 按 csv 的 req_mod_ids 列把两个 SotF augment 过滤掉了;但 mods/niko_stationAugments/data/config/modSettings.json:65 的 sindria数组里仍无条件引用 SA_defensePlatforms,新档生成 Sindria 站时按 id 查不到。
      • SA_station_augments.csv:74 的该行 req_mod_ids=secretsofthefrontier、plugin=niko_SA.augments.sotf.DefensePlatforms;Secrets of the Frontier 未安装。
      • 根因是该 mod 自身配置与数据不一致(不是汉化、不是 armaa 升级)。影响:Sindria 空间站少一个本来就没生效的可选增强。
      • 低风险修法:从 modSettings.json 的 sindria 数组删掉 "SA_defensePlatforms"那一行(注意前一行 "SA_highExplosive", # THE LION... 带行内注释,用宽松 JSON);或装 SotF。改完要重新开档才会走到那段生成逻辑。
    • ERROR market is null ×3 不是 ERROR:来自 scripts.kissa.LOST_SECTOR.world.nskr_gen的 INFO 调试文本,随后 nskr_desertFixer 正常修 3 颗星球、simpleSystem valid 127、blacksiteSpawner picked 都成功,属容错日志。

    2.18 舰队改装(REFIT)Fatal:Exotica 汉化误译按钮文字(已修,04:20 实机复测通过)现象:04:01:54 点「舰队改装」→ Fatal: null cannot be cast to non-null typecom.fs.starfarer.api.ui.UIComponentAPI。堆栈:
    同一条在 runs/ARMAA338B.stdout.txt:134137(191478 ms,读档时开过装配界面)就出现过,不是 armaa 3.3.8 / 2.16 中文补丁引入,只是这次才被用户明确撞上。
    根因(证据链完整,不是猜):
    • mods/exoticatechnologies/src/exoticatechnologies/refit/RefitButtonAdder.kt 里:这个 EveryFrameScript 靠硬编码 UI 按钮文字定位 refit 界面的 "Build in" 按钮。
    • 英文原版 Exotica-Technologies_EN.jar 的 RefitButtonAdder.class 里是#283 = "Build in";汉化版 Exotica-Technologies.jar 里这个类被单独重编译过(2026-02-21,其余 class 都是 2025-07-22),把匹配串改成了#748 = "内置舰船插件"(合法 UTF-8,18 B;javap 显示的 ÄÚÖý¢´¬²å¼þ只是 Windows 控制台编码问题,不是 class 里存了 GBK)。
    • 但汉化 core starsector-core/starfarer_obf.jar → com/fs/starfarer/coreui/refit/L.class的常量池 #172/#439/#449 显示,那个按钮实际文字是 内置(2 个字)。contains("内置舰船插件") 永远 false → find() 返回 null →字节码 962/965 的 Intrinsics.checkNotNull(..., "null cannot be cast ...") 抛 NPE→ 从 advance() 冒到 CampaignEngine,Fatal。汉化者把「按钮标签」和「功能名称」混了:内置舰船插件 是 hullmod 概念译名,不是按钮文字。
    修法(fix_exotica_refit.py,不重编译、不动功能、可逆、幂等):
    #
    改动
    字节
    1
    CONSTANT_Utf8 #748:内置舰船插件(18 B) → 内置(6 B)
    常量池 −12 B
    2
    唯一那处 as UIComponentAPI 的 invokestatic Intrinsics.checkNotNull(Object,String)V(3 B) → pop2; nop; nop(3 B)
    长度不变
    • 改动 1 让 contains("内置") 真正命中按钮,buildButton 非 null,Exotica 的改装按钮功能完整恢复(不是关功能规避)。
    • 改动 2 是防御:万一以后 UI 文字又变,checkcast 对 null 安全,putfield 存 null,原有 if (modWidgetLocal != null && buildButton != null) 分支跳过按钮创建,不再把游戏带崩。栈推演 [ref, elem, elem, msg] --pop2--> [ref, elem],与 invokestatic (Object,String)V 净栈效果完全相同,StackMapTable 无需改。
    • kotlin.String 那处 checkNotNull(928 偏移)保持原样,只改 UIComponentAPI 这一处。
    静态验证(全部通过):
    最后一项用 VerifyOnly.java 在独立 JVM 里强制触发字节码校验器,不启动游戏、不碰用户正在跑的进程(verify_modfilter.py 收尾会 taskkill所有新建 java 进程,会误杀用户实例,所以这次没用它)。
    备份 / 回滚:backup_exotica_refit/Exotica-Technologies.jar.before_refit_fix(1003974 B,md5 d93d4da4…)+ manifest.json;python work/ss_mods/fix_exotica_refit.py --restore,或 --verify 看当前状态。Exotica-Technologies_EN.jar 未改(英文原版,mod_info.json 也没挂它)。若汉化 Patcher / mod 升级覆盖了 Exotica-Technologies.jar,本补丁需重打。
    复测验收(04:20,用户重开游戏 → 读 save_Royic_8184782978826651943 → 进 REFIT)
    游戏自带日志本次会话边界 = 第 164569 行(1846473 → 747 ms,135860 行):
    • exoticaButton.png 被加载 = contains("内置") 真的命中了按钮、走到了创建分支,不是靠 pop2 的 null 保护静默跳过。这是功能恢复的正面证据,比「没崩」更强。
    • 4 × LunaSettings: Could not find mod CaptainsLog 与 ARMAA338B 里那 4 条逐条相同,由打开 INTEL / 赏金界面(extrauiplus.bounty.BountyRuntime)触发,CaptainsLog 未安装,2.15 已定性,与本次补丁无关。
    • 5 × LazyFont: Character '感'/'武'/'器'/'控'/'制' is not defined in font data:其中 4 条紧随 exoticaButton.png(84219 ms),是按钮文字渲染时的字形表缺字,属显示层 WARN,不影响点击;ARMAA338B 已有 1 条同类,非新故障类型。
    • 上次新开档也是 106 条,但构成不同(那次是 3 条 ERROR market is null 伪命中
      • 1 条 Invalid augment id),本次换成 4 条 CaptainsLog。基线仍是 96 + 6 sprite。
    ⚠️ 必须重启游戏才生效:崩溃那次 JVM 已经加载了旧 class,热改 jar 不影响运行中的进程。(用户已重启并完成复测,见上。)

    2.19 新增 Particle Engine 0.10.1(库 mod)启动验收(04:22 装 / 04:26 查日志)结论:完整加载、GL 4.3 兼容与 GLSL 编译实证通过、零新增错误类型。 与 armaa 3.3.8、2.16 中文补丁、2.18 refit 修复都无冲突;游戏跑到 CAMPAIGN 心跳 259099 ms,进程仍在。
    日志切法:本次会话被 51.2 MB 滚动切断在两个文件里 ——starsector.log.3 第 301671 行(253041 → 723 ms)到文件末尾,加 starsector.log 全部(从 18022 ms 无缝续上)。合并后 137077 行。(这是 6-13 那条坑的具体实例:只统计当前文件会漏掉 56 条武器缺行和整个 mod 加载段。)
    加载链(全部 INFO,无一处 ERROR/WARN):
    GL 4.3 兼容性不是「没报错所以大概没事」,是启动期强制检查通过了:javap -c particleengine.ParticleEngineModPlugin.onApplicationLoad() 显示enabled=true 时先读 GLContext.getCapabilities().OpenGL43,为 false 就new OpenGLException("(Particle Engine) ... doesn't support OpenGL 4.3 ...") 并 athrow,这段没有 try-catch(异常表只覆盖 0-29 的 IOException/JSONException 读配置)。随后紧接着 ParticleShader.init("particle.vert", "particle.frag") + new Particles()。本次 Fatal 0、OpenGLException 0、GL_INVALID / shader 编译失败 0、正常到主菜单⇒ 能力检查通过且两个 shader 编译成功。FR 的 com.genir.renderer.bridge 虚拟OpenGL 上下文仍暴露 GL 4.3,没有把新 mod 挡在外面,也没有旁路掉 FR。
    指标(合并会话):
    • 比上次复测多 4 条,只是 LunaSettings: Could not find mod CaptainsLog 从 4 → 8:这次打开 UI 两次(89543 / 97849 ms),每次 4 条,2.15 已定性。
    • WARN 524(上次 472)同样是既有模板重复:两个版本检查器各 76 条(新 mod 多了一个 .version 要查),另有 2 条 Failed to load master version file ... qcwxezda/Starsector-Particle-Engine 是外网访问失败,非 mod 故障。
    一个要如实说明的点:全库搜 particleengine(含所有 jar 常量池)共 42 处,除 enabled_mods.json 和 hypernova_infinite_engine/.../manifest-v1.json 里2 张 javadoc/resources/*.png 的 modId 登记外,全是它自己的文件;没有任何 mod 在 dependencies 里声明它。它是 utility: true 的纯库 mod,所以目前不会有可见的粒子效果。之后若装了依赖它的 mod,需再复验一次战斗渲染路径(启动期已验证的是能力检查 + shader 编译,不是运行时 SSBO/instanced draw)。

    2.20 反射阵列存档 Fatal:论坛案例复盘 + 本地预防(已做,需重启生效)来源:Royic 给的论坛日志 E:\download\starsector.rar → starsector.log(10 MB / 105332 行,解到 work/ss_mods/forum_log/)。反馈是「在星球上建了地貌改造 mod 的反射阵列后就不能保存」。
    论坛日志的硬事实:
    连续 5 次保存全部同样失败(2258707 / 2301154 / 2323843 / 2357138 / 2513023 ms),说明坏实体一直在内存里,不是偶发。XStream 没打 path,日志也没有 mod 自己的日志,所以「元凶一定是反射阵列」是代码路径推断,不是日志直接点名 —— 这点如实记着。
    本地 mod 与缺陷代码:mods/Terraforming & Station Construction(地貌改造 & 空间站建设 v10.0.9,jar 2026-09-03)。industries.csv 里 BOGGLED_STELLAR_REFLECTOR_ARRAY = 恒星反射镜阵列,plugin boggled.campaign.econ.industries.Boggled_Stellar_Reflector_Array。
    javap -c createMirrorsOrShades(MarketAPI):建造完成 buildingFinished() 时调用一次,创建 3 个 CustomCampaignEntity(stellar_mirror / stellar_shade),轨道参数全部无有限性检查:
    本地存档实证了这些值落盘的位置:<CCEnt ...><orbit cl="COrbtPD" r="220.0"op="40.0" ca="178.20007"> 与 <j0>{"f6":178.20007,...}</j0>。只要 angle(或 period / radius)是 NaN / Infinity,实体 JSON 与轨道属性就带上非有限数,writeReplace() 的 JSONObject.put 每次保存都抛 → 永久存不了档。
    我们当前状态(04:43 体检):
    • 唯一存档 save_Royic_8184782978826651943/campaign.xml(44.8 MB)里已有10 处 BOGGLED_STELLAR_REFLECTOR_ARRAY、46 stellar_mirror、33 stellar_shade(含 Eochu Bres 恒星镜 ×3、FSF_StationPlanetShade1 等预设实体),数值全部有限;NaN = 0。Infinity 出现 6 次全是 HMI 的星球名/跳跃点名(<name>Infinity</name>、"f0":"Infinity 跳跃点"),不是数值。
    • 当前会话无 Error saving game / non-finite。暂时安全,但同版本同缺陷代码,下次在新星球上建就是掷骰子。
    已做的两层防护:
    • 根因补丁(ASM) —— PatchReflector.java + ReflectorSanitizer.java +apply_reflector_patch.py。在 3 个局部变量 FSTORE 之后插入纯静态调用(finiteOr / positiveFiniteOr,非有限或 period≤0 就用 0f / 180f / 40f),净栈效果 0,StackMapTable 帧数不变(55→55)、偏移由 ASM 自动重定位。4 处注入(angle 1 / radius 1 / period 2),class 10790→10947 B;jar 134→135 条目,313646→312361 B,md5 153741ac… → 0eba83c5…。验证:javap 注入位置、-Xverify:all + resolveClass 两个类都verify OK、135 条目逐个校验、重复 --apply 幂等、备份 + --restore。没跑 verify_modfilter.py(会误杀用户进程,见 6-15)。
    • 存档体检 / 急救 —— check_save_finite.py(默认只读)。先定位 NaN/-Infinity/Infinity 字面量,再只用紧邻字符判定它是不是数值上下文(XML 属性 / JSON 裸数值 / 数字元素),因此 <name>Infinity</name> 和"f0":"Infinity" 不会误报误修。--fix 会先备份再按字段给安全默认值(op→40.0、ca/angle/f6→0.0、r/radius→180.0)。用构造的坏样本试跑过:3 处坏值精确命中、修复后复检 clean、名字原样保留;真存档体检 clean: 0。
    • 存档备份 backup_save_before_reflector/save_Royic_8184782978826651943/(2 文件 / 43 MB,逐文件 md5 + manifest.json)。
    万一以后真出现 Error saving game 的急救顺序:
    • 别关游戏 —— 在装配/产业界面拆除该反射阵列;notifyBeingRemoved()会调 boggledTools.clearReflectorsInOrbit() 移除那 3 个坏实体,然后再保存。
    • 若已经关了:退出后跑 python check_save_finite.py(看是否已有存档带坏值)→ python check_save_finite.py --fix(自动备份)→ 或把上面的备份目录拷回 saves/。
    • 论坛案例的教训:保存失败是持续的,别反复重试;先移除坏实体。
    ⚠️ 必须重启游戏才生效:改的是 mod jar,当前运行中的 JVM 已加载旧 class。下次启动后补丁才拦得住新建的反射阵列;已有实体的数值本来就正常,不受影响。mod 升级 / 汉化包替换 jar 后要重打(apply_reflector_patch.py --apply)。

    2.21 论坛案例二:探索轨道居住站 → OpenCoreTab ClassCastException(core 缺陷,已做通用防护)症状(论坛):剧情生成的「轨道居住站」点「开始调查」→ 对话消失、退回空太空背景 + 下边栏,无法操作;随便开一个页签再关掉,回到对话并在文本区显示红字CustomCampaignEntity cannot be cast to CampaignPlanet,可无限复现,按「离开」后正常。该站开局不存在、进星系即可见、不提供探测经验 ⇒ 典型的剧情动态生成自定义实体。
    根因(本地反汇编实证,不是猜):
    starfarer_obf.jar → com/fs/starfarer/campaign/ui/class.class 的 sizeChanged:
    偏移
    指令
    问题
    67-74
    market.isPlanetConditionMarketOnly() → ifeq 161
    pc-only 市场才走颜色分支
    81
    market.getPrimaryEntity()
    返回 SectorEntityToken
    84
    checkcast CampaignPlanet(无 instanceof 保护)
    自定义实体必崩
    182/195
    instanceof CampaignPlanet + checkcast
    同一方法里另一处反而有保护 ⇒ core 自身遗漏
    ⇒ 触发条件 = 任何 mod 给 CustomCampaignEntity 挂了 setPlanetConditionMarketOnly(true) 的市场,再对它执行 OpenCoreTab CARGO / FLEET / REFIT。
    OpenCoreTab 命令全库(含 181 个 jar 的常量池)只有 core rules.csv 7 处 + Nexerelin 1 处;core surveyOptionPerformSurvey($option == surveyPerform → OpenCoreTab CARGO OPEN)最贴合「开始调查」。但对方没给完整日志,具体命中哪条规则无法点名,属代码路径推断。
    我们环境的暴露面:
    • niko_morePlanetaryConditions 的 MPC_singularityStagingStation(轨道居住站工地,niko_MPC_generateUltima.kt:282 剧情动态生成)形态最像,但它用Misc.setAbandonedStationMarket() —— 该方法 iconst_0 →setPlanetConditionMarketOnly(false),不中招。core 自己的 Corvus / Mayasura / Yma 废弃站同理。
    • 真实高危形态是 ArkLeg/jars/src/data/world/al_campaign_hax.java:64 的addConditionMarket(SectorEntityToken entity, ...):对任意实体setPrimaryEntity(entity) + setPlanetConditionMarketOnly(true)。源码里未找到调用点,但这类「给站加条件市场」的写法在剧情 mod 里很常见。
    • 存档体检 check_save_market_entities.py:当前 0 个雷。23 个<isPlanetConditionMarketOnly>true</...> 市场的 <primaryEntity> 全是 cl="Plnt";另有 121 个 CCEnt 作 primaryEntity,但都不是 pc-only 市场。
    做法(PatchCoreUICast.java,两遍式 ASM,不重编译、不动功能、可逆、幂等):
    在偏移 74 的 ifeq fallthrough 之后插入 5 条指令,复用同一个跳转 label:
    非行星实体直接跳过行星图标色计算,保留默认势力配色(原本这种情况是崩溃,零行为损失)。无新 label、无新局部变量、分支点栈平衡 ⇒ StackMapTable 帧数 190→190、14 个表全不变;class 37421→37434 B(+13);Utf8 字面量 659→659,零新增。check_core_ui_patch.py 逐方法证明:46 个方法只有 sizeChanged 变(1420→1425 条指令),其后指令流完全一致。ASM 重建常量池时去重了若干 ldc_w 条目(索引变、值不变),已核对。
    patch_core_ui_cast.py 备份整个 starfarer_obf.jar + 原 class,--apply/--verify/--restore幂等;写入后 3127 个条目逐个校验(除目标外全部逐字节相同)。jar6997855→6941554 B 是 zipfile 重写的压缩率差异,内容一致。
    验证:
    • 静态 VerifyOnly.java 对混淆 core 类不可用:原版和补丁版同样报ClassFormatError: Illegal field name "do.super$class"(父类 interfacenew 还有private.int)——原版混淆产物自带非法字段名,与补丁无关。改用 javap + 等价性证明。
    • 实机 verify_modfilter.py COREUI 170:正常到主菜单,exec_exception / verify_err / classformat / noclassdef / nosuchmethod 全 0。
    • 读档会话(starsector-core/starsector.log 第 103128 行起,134659 行):Reading save data from save_Royic_8184782978826651943 ×2、载入 ×1;打开 OUTPOSTS / INTEL / MAP / CARGO / REFIT / FLEET / CHARACTER 多个页签—— 正是论坛崩溃的 CARGO / FLEET / REFIT 都在其中 ——ClassCastException 0、cannot be cast 0、Fatal: 0、Error saving game 0,exoticaButton.png 1(2.18 修复仍在);ERROR 106 = 56 武器 + 20 船体 +20 plugin + 6 sprite + 4 CaptainsLog,与 2.18 基线完全一致。
    • ⚠️ 诚实边界:这些页签是在正常行星/市场上打开的,我们存档里没有「custom entity + pc-only market」实体,没有在坏实体上实机复现。补丁通用性由字节码逻辑(偏移 84 无保护强转)保证,不是坏实体实测。
    监测:python check_save_market_entities.py(只读)扫描所有存档,报告 pc-only 市场的 primaryEntity 类型;剧情生成新站后可再跑。当前 clean: 0 risky。
    回滚:backup_core_ui_cast/starfarer_obf.jar.before_cast_guard(6997855 B / md5 77157b3b…)+ CoreUIClass.orig.class + manifest.json,python patch_core_ui_cast.py --restore 一键还原。
    升级注意:游戏本体升级 / 汉化包替换 starfarer_obf.jar 后补丁会被覆盖。脚本会因 class md5 不匹配报 CHANGED-UNKNOWN 并拒绝 apply —— 别改脚本绕过,要先重新导出 CoreUIClass.orig.class、重跑 PatchCoreUICast 生成新 patched class。

    3. 脚本清单(全部在 F:\workspace\apk_ws\work\ss_mods\)补丁类(带 --apply / --restore)
    脚本
    作用
    patch_mod_filter.py
    fs.common_obf.jar 的 com/fs/util/C setter(2.2)
    patch_shaderlib_autogen.py
    Graphics.jar 的 TextureData 1 字节(2.5 改动①)
    patch_jc_sf.py
    已废弃,仅作历史记录。jc_sf 已还原原始 jar,不要再跑 --apply
    fix_csv_types.py
    3 个 LunaSettings.csv(2.4)
    fix_hash_rows.py
    # 吞行拆行(2.7)
    fix_armaa_blinker.py
    blinker .wpn 路径(2.7)
    rebuild_luna_caches.py
    144 个 LunaSettings 缓存重建(2.3)
    toggle_autogen.py
    切换 autoGenNormals(--off / --on)
    fix_eve_keybinds_csv.py
    给 EVEOverviewCommand 补 72 行键位声明(2.9)
    fix_narcissu_large_csv.py
    给 Narcissu 补 12 个 cpi_large_*(2.10)
    fix_wpn_filenames.py
    .wpn/部分 .proj 文件名改成内部 id(2.11,幂等;已改 51 个)
    fix_missing_strings.py
    补 2 个 mod 的缺失文案 key(2.12;自带重复键自检)
    fix_pseudo_errors.py
    4 条伪 ERROR 降级为 INFO(2.14;--apply / --restore / --verify,幂等)
    find_pseudo_error.py
    全库按 UTF-8 + GBK 搜日志字符串来自哪个 jar(2.14 定位用)
    check_save_finite.py
    存档非有限数体检 / 急救(2.20);默认只读,--fix 先备份;--dir 可测样本
    PatchCoreUICast.java
    两遍式 ASM:给 core sizeChanged(FF)V 的 pc-only 颜色分支补 instanceof 保护(2.21)
    check_core_ui_patch.py
    逐方法 javap 等价性证明:只有 sizeChanged 多 5 条指令、无新常量(2.21)
    patch_core_ui_cast.py
    把补丁装进 starfarer_obf.jar;--apply/--verify/--restore,幂等,3127 条目逐个校验
    check_save_market_entities.py
    只读体检:pc-only 市场的 primaryEntity 是否为 Plnt,找出潜在 CCE 站点(2.21)
    PatchReflector.java / ReflectorSanitizer.java
    ASM 给反射阵列 3 个轨道参数加有限性保护;用 aitweaks/jars/asm-9.1.jar 编译
    apply_reflector_patch.py
    把补丁 + helper 装进 TASC jar;--apply/--verify/--restore,幂等
    fix_exotica_refit.py
    修 refit Fatal(2.18):常量池 #748 改译文 + as UIComponentAPI 强转改 null-safe;--apply/--verify/--restore,幂等
    VerifyOnly.java
    独立 JVM 里 defineClass + resolveClass 强制跑字节码校验器,不启动游戏、不 kill 任何进程
    inspect_refit_constpool.py
    dump 两个 Exotica jar 的 Utf8 常量,定位按钮文字(探查用)
    audit_pseudo_run.py
    两次运行日志的 WARN/ERROR 模板差集,判断「新增告警」是否只是读档重跑
    backup_armaa_upgrade.py
    升级前全量备份 mods/armaa + saves(逐文件 md5 清单,2.15)
    compare_armaa_3way.py / compare_armaa_detail.py / inspect_armaa_upgrade.py / inspect_jar_merge.py / inspect_cn_jar_core.py
    3.3.6 EN / 3.3.6 CN / 3.3.8 EN 三路对比与 jar 分析(只读,2.15)
    build_armaa_338.py
    在工作区组装 3.3.8 汉化移植版(含 CSV 列级合并、字体、strings 并集、wpn 改名)
    merge_armaa_strings.py
    补回汉化 strings.json 丢掉的两个配置分类(启动崩溃修复)
    apply_armaa_338.py
    原子替换 mods/armaa(备份校验 → 改名 → 复制 → 逐文件 md5 → 失败回滚)
    inspect_cn_strings.py / armaa_cn_literals.json / ARMAA_CN_STRINGS.txt
    三份 jar 的字符串常量池差集分析(2.16)
    compare_logic_armaa.py / armaa_logic_compare.json
    javap opcode 序列比较,判断 class 逻辑是否跨版本改变(2.16)
    armaa_cn_string_map.json
    25 对英文→中文映射(人工核对,2.16)
    patch_armaa_cn_strings.py
    常量池 Utf8 原地替换(--apply / --restore / --verify,幂等,2.16)
    patch_eve_keys.py
    已废弃。键位字段已在 CSV 里声明,LunaLib 会自己生成缓存 key,不要再跑
    实测验证类输出项:lazi_running / dup_ids / luna_missing / plugin_init / jc_damaged / cif_subclass / fatal / exec_exception / noclassdef / verify_err / classformat / nosuchmethod / fr_version,以及 LazyLib 版本行。日志落在 work/fr_study/runs/LABEL.stdout.txt。
    审计类audit_warnings.py(全量告警分类)、audit_csv_alignment.py(CSV 列对齐/类型合法性)、audit_value_types.py(缓存值 vs CSV 声明类型)、scan_type_garbage.py(找「值等于类型名」的列错位指纹)。

    4. 备份与回滚速查
    目录
    内容
    回滚方式
    backup_modfilter/
    fs.common_obf.jar.orig
    拷回 starsector-core/
    backup_autogen/
    Graphics.jar.orig(原始)、.before_onGameLoad(仅改动①)、shaderLib.json.data、hash_after_full.data
    拷回
    backup_jc_sf/
    jc_asm.jar.orig(= 当前正在使用的原始文件)
    一般不需要;已还原
    backup_cif/
    CIF mod_info.json、Miko_Simple.txt
    拷回
    backup_csvfix/
    3 个 LunaSettings.csv
    fix_csv_types.py --restore
    backup_hashrows/ + backup_hashrows2/
    第一批 7 个 / 其余 csv
    见 2.7
    backup_armaabl/
    5 个 blinker .wpn + armaa csv
    fix_armaa_blinker.py --restore
    backup_ork/
    melta_fire.ogg.orig、nexerelin_strings.json.orig
    拷回
    backup_eve/
    LunaSettings.csv.orig(11373 B)、EVEOverviewCommand.json.data.before_csvfix
    fix_eve_keybinds_csv.py --restore;缓存可删(会从 CSV 重建)
    backup_narcissu/
    LunaSettings.csv.orig(15963 B)
    fix_narcissu_large_csv.py --restore
    backup_wpn_rename/
    manifest.json(51 条:48 wpn + 3 proj)
    fix_wpn_filenames.py --restore
    backup_strings/
    PAGSM__strings.json、pigeonpun_bionicalteration__strings.json
    fix_missing_strings.py --restore
    backup_pseudo/
    3 个原 jar(2 份 bionicalteration 共用一份)+ manifest.json
    fix_pseudo_errors.py --restore
    backup_reflector/
    TASC 原 jar(313646 B / md5 153741ac…)+ manifest.json
    apply_reflector_patch.py --restore(2.20)
    backup_save_before_reflector/
    当前生涯档 2 文件 / 43 MB + 逐文件 md5 manifest.json
    整目录拷回 saves/(2.20)
    backup_save_finite/
    check_save_finite.py --fix 自动生成的存档备份
    按时间戳目录取回
    backup_exotica_refit/
    Exotica-Technologies.jar.before_refit_fix(1003974 B)+ manifest.json
    fix_exotica_refit.py --restore(2.18)
    backup_armaa_upgrade/
    armaa_336_cn/(2069 文件)、saves_before_338/(172 文件)、manifest.json
    把 armaa_336_cn 拷回 mods/armaa(2.15)
    backup_cn_strings/
    armaa.jar.before_cn_strings(1667803 B / 748d494c)+ manifest.json
    patch_armaa_cn_strings.py --restore(2.16)
    backup_core_ui_cast/
    starfarer_obf.jar.before_cast_guard(6997855 B / 77157b3b…)、CoreUIClass.orig.class、manifest.json
    patch_core_ui_cast.py --restore(2.21)
    probe_autogen_backup/
    shaderLib.json.data(autoGenNormals=false 原状)
    拷回
    LunaSettings_snapshot_before_rebuild/
    144 个 .json.data 改前快照(700 KB)
    整目录拷回
    mods/shaderLib/cache_probe_bak/
    旧法线贴图缓存 4930 文件 / 410 MB
    确认新缓存稳定后可删

    5. 当前状态与已知遗留指标96 条的构成与定性
    类别
    条数
    定性
    能否修
    weapon_data.csv 缺行
    56
    .wpn 存在但任何 csv 都无该 id(机甲部件/模块武器居多),或资源真缺
    不能。恢复需凭空造行,且资源缺失会 Fatal
    ship_data.csv 缺行
    20
    同上
    不能
    plugin init 探测
    20
    FR loadModClasses() 对配置里引用的类名做无参 newInstance 探测;这些类构造依赖 Global.getSector/getFactory/getCombatEngine,加载期必然 NPE,被 catch 后 continue,只有 loadClass 失败才致命
    不需要。已用字节码异常表 + 不在 settings.json 的 plugins 数组里 + 实际创建走 json/csv 反射三重验证
    读档额外 Cannot find sprite
    +6
    SGB/JKF 发光层贴图被当炮管用、未预载(2.13)
    不修。收益≈0,需换掉 shaderLib 50KB 核心类
    已清零的历史项:Missing string 11(PAGSM sfc_* 10 + tooltip_isDevmode 1,见 2.12)、伪 ERROR 4(见 2.14)、Failed to find JSON 98+6(2.11)、not found in JSONObject 3084/局(2.9 / 2.10)。
    读档日志(runs/PSEUDO.stdout.txt)复核结论用户读完档后逐项比对(audit_pseudo_run.py 对 STRINGFIX2 做模板差集):
    • 无新致命项:FATAL 实际 0(脚本报 2 是 overseer_unit_FATAL.variant 文件名)、VerifyError / NoClassDef / ClassFormat / NoSuchMethod / OutOfMemory /Duplicate key / Exception in thread 全 0;NPE 32 条与上次完全相同,都是启动期PkPostWarIndustry 被 catch 后打 INFO 的旧问题。
    • 法线贴图:Auto-generated normal map 5 条、Failed to load texture ... normal map 0→ onGameLoad 全量扫描路径稳定,缓存基本完备。
    • 差集里 171 个「新增」WARN 模板,绝大多数是读档阶段把启动期逻辑又跑了一遍,不是新故障:MagicSettings 163 条 unable to find <factionId> within armaarmatura inmodSettings.json(Armaa Armatura 配置里没给这些势力写数据,被 catch)、MagicAchievementManager 把 MagicLib/data/config/magic_achievements.csv 的注释行# description: ... 当成就解析(2 条 + ClassNotFoundException 空类名堆栈,上游模板文件问题)、MagicLib 3 个 bounty variant(grendel_罗格多恩 里 "WS 014":"null" 等,旧问题)、版本检查器访问外网的 WARN。均为 mod 数据缺项或网络,不动。
    • 游戏一直正常跑到 screenshot105.png、心跳体检 CAMPAIGN,没有崩溃/卡死迹象。
    • 注意:verify_modfilter.py 收尾会 taskkill 它自己启动的 java 进程;若正在那个实例里玩,验证跑完游戏会被关掉,重开即可。
    缺失项逐项清单那 56 个武器 + 20 个船体到底属于谁、缺了什么,见 MISSING_SPECS.md(762 行):
    • 每个 id 一条:spec 路径、numFrames、贴图字段数、缺几张(列出具体文件)、CSV 是「有正常行 / 只有 # 行(并注明列数 N/M) / 完全没有」、判定代码(A0–D3)
    • 按 mod 汇总表(含显示名与版本)
    • 判定口径表 + 为什么「这批不动」
    • 生成脚本 audit_missing_specs.py,可随时按任意一次运行日志重跑
    两个结论性发现,值得记住:
    • spec 必须按内部 "id" 索引,不能只看文件名:MPC_steelPecker.wpn 的 id 是MPC_steelpecker,jet_shoota_payload.wpn 的 id 是 ls_jetshoota_payload,kol_buckler_amm.wpn 的 id 是 kol_buckler。按文件名查会误报「spec 不存在」。
    • 当前分布:D2(有 spec、贴图齐、任何 CSV 都无行)51 个,D1(缺贴图)15 个,D3(spec 无贴图字段,模块/装饰类)5 个,C*(# 注释行)3 个,A2(无 spec)2 个。
    已验证的一件(原「未推进」)生涯存档已建好并多次读档实测:onGameLoad 全量扫描生成后回读0 失败(首次生成 131 张 / 8 s;缓存完备后只剩遍历,1.7 s;本次 5 张 / 0 失败)。2.5 节的「挪到 onGameLoad + 回退 vanilla」方案由此实证成立。

    6. 不要做的事
    • 不要批量恢复被 # 注释的 csv 行,除非逐项用全局索引验证过贴图。22:30 崩过一次。
    • 不要删 LunaSettings 缓存文件,只能写 {}(否则会 fallback 到 lunalib 的默认表,被 FR 的 modId 行为灌满)。
    • 不要用 pscap 截图。Win11 的 PrintScreen 会弹「截图工具覆盖」并抢走前台焦点,导致后续键盘输入全丢。给游戏窗口发 F12(游戏内置截图键),或用 RobotUtil cap(只对普通桌面程序有效,对 LWJGL 全屏无效)。
    • 不要用 Git Bash 传含 ; 的 javac classpath,会被路径转换搞坏,必须用 PowerShell。
    • 不要在 # 注释标题里带「废弃/deprecated」的行上动手,那是作者主动禁用的标记。
    • shaderLib / 游戏本体更新后,补丁需要重打(jc_sf 已不需要:2.2 修好后它的自检自己就能过)。升级流程:先跑 patch_mod_filter.py / patch_shaderlib_autogen.py 的 dry-run 确认签名命中,再 --apply。bionicalteration / ArcLightBureau 升级或替换 jar 后也要重跑 fix_pseudo_errors.py --verify;弧光设计局的 jar 若被汉化包覆盖,同样重打。
    • verify_modfilter.py 的 fatal 是 FATAL 子串计数,不是致命错误数。它会命中 overseer_unit_FATAL.variant 这类文件名。判断有没有 Fatal 要 grep -n -i "Fatal:" 看实际日志行。
    • 别把读档阶段的 WARN 当成新故障:onGameLoad 会把启动期逻辑再跑一遍,MagicSettings / MagicAchievementManager 的 WARN 会翻倍。用 audit_pseudo_run.py 做模板差集再下结论。
    • armaa 升级/汉化包重跑后,4 类补丁必须复查:汉化字体 .fnt/.png、5 个 blinker .wpn(上游至今仍是错的旧路径)、strings.json 的 dialogueFont 并集、4 个按 id 改名的 .wpn。缺 dialogueFont 会启动直接崩,不是 WARN。
    • 重跑 build_armaa_338.py 用备份当基底(脚本已指向 backup_armaa_upgrade/armaa_336_cn)。别把「当前 mods/armaa」当 3.3.6 CN 基准,升级后它已经是 3.3.8。
    • 完整重建顺序必须是 build_armaa_338.py → apply_armaa_338.py → patch_armaa_cn_strings.py --apply。前两步会把 C 的原版 jar 装回去,丢掉 2.16 的中文补丁;第三步幂等,重复跑安全。
    • 别按常量池「位置」配对翻译:汉化版重编译过,字符串顺序完全不同(实测对齐率最低 1/10)。要补文案只能用 armaa_cn_string_map.json 这种显式映射,并先确认目标串不是 option id / 字段名。
    • 读游戏自带 starsector-core/starsector.log 前先切会话边界:它按 51.2 MB 滚动成 .1/.2/.3,且当前文件头部经常是上一次会话的尾巴,时间戳会回绕。整文件 grep 会把两次会话混算(实测 Cannot find sprite 6→12、ERROR 106→112)。用时间戳回绕点定位边界后 tail -n +N 再统计。
    • 新开档才走的路径(世界生成 / 空间站 augment / Diable Last Line 舰队)会暴露读旧档看不到的第三方软依赖问题(2.17)。判断「新增告警」时先分清是本次升级引入,还是只是第一次执行到那段代码。
    • 别跑 verify_modfilter.py 去验证「用户自己开着游戏」时的改动:它收尾会 taskkill /PID /T /F 所有不在启动前快照里的 java 进程(第 101-102 行),会杀掉用户手动开的实例。改用 VerifyOnly.java 在独立 JVM 里做字节码校验,或让用户重启后自己点一次。
    • 汉化 mod 里「按 UI 文字找组件」的逻辑,必须拿 core 的混淆类常量池核对(2.18)。javap 在中文 Windows 上显示的乱码是输出编码问题,别据此断言「class 里存了 GBK」——要用 parse() 读 Utf8 原始字节再下结论;本次真实原因是译名多了 4 个字。
    • 看到 Error saving game + non-finite numbers 别反复重试:坏实体常驻内存,每次保存都会失败(论坛案例连 5 次)。先在游戏里拆掉产出该实体的建筑,或退出后跑 check_save_finite.py --fix(2.20)。
    • TASC / 汉化包升级后重打 2.20 补丁(apply_reflector_patch.py --apply)。同理,建大型产业前先备份 saves/ —— 存档损坏是所有改动里最难回滚的一种。
    • 别用 VerifyOnly.java 验证 starfarer_obf.jar 的混淆类:原版与补丁版同样报 ClassFormatError: Illegal field name "do.super$class"(父类还有 private.int),是原版混淆产物自带含点字段名,与补丁无关。验证 core 类要用 javap + 逐方法等价性 + 真实启动(2.21)。
    • 游戏本体升级 / 汉化包替换 starfarer_obf.jar 后,2.21 补丁要重打,且必须重新导出 CoreUIClass.orig.class 再跑 PatchCoreUICast;patch_core_ui_cast.py 遇到 md5 不匹配会报 CHANGED-UNKNOWN 并拒绝 apply,别改脚本绕过。
    • 判断 OpenCoreTab 崩溃别只看 mod:崩在 com.fs.starfarer.campaign.ui.class.sizeChanged 的是 core 对 CampaignPlanet 的无保护强转。排查方向是「哪个 mod 给自定义实体挂了 pc-only 市场」,用 check_save_market_entities.py 先扫存档。

    巡洋大副

     楼主| 发表于 15 小时前 | 显示全部楼层
    如有雷同崩溃,让你的ai抄作业吧(

    星域军阀

    英仙领大胖鸡

    发表于 8 小时前 | 显示全部楼层
    AI - 批处理之终极
    一百万伏特算力——
    口也——
    游历星域, 播撒福恩, 光荣朝圣, 复兴英仙领.

    本版积分规则

    Archiver|手机版|小黑屋|远行星号中文论坛

    GMT+8, 2026-11-12 20:56

    Powered by Discuz! X3.5

    © 2001-2077 Tencent Cloud | Durian Software Studio | IP地址位置数据由纯真CZ88提供支持

    快速回复 返回顶部 返回列表