Skip to content

​[Bug] 安卓端 ModLoader 加载时机过早,导致 NDailyRunScreen 静态初始化失败 (类中毒) 及游戏启动闪退 #33

Description

@Icebearjkn

提前叠个甲:
我不是专业的 C# 或 Godot 开发人员,只是一个普通玩家。遇到几次闪退后,自己对着日志查了一些关于 C# 底层机制的资料,做了个大概的推测分析。如果接下来说的机理有误导或者不准确的地方,请大佬见谅,权当提供一个查错的参考思路。

  1. 问题描述
    在目前的安卓移植版环境中,尝试加载部分大型内容 Mod(例如 wylder 追踪者角色(B 站名 kashenmir)、NightReign 黑夜君临(B 站名 Rebelwings)等,)时,游戏会在启动阶段直接崩溃闪退。
    这些 Mod 的共同点是:它们不仅添加了新卡牌/新角色,通常还包含对游戏底层模式的大幅修改(例如注入新的游戏模式、修改每日挑战或联机大厅参数等)。

2. 报错日志 (Error Logs)

提取了游戏崩溃时的核心报错堆栈(以 NightReign 为例,其他几个大型 Mod 的死法几乎一模一样):

[ERROR] Exception thrown when calling mod initializer of type NightReign.NightReignModInitializer: System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
 ---> HarmonyLib.HarmonyException: Patching exception in method System.Void MegaCrit.Sts2.Core.Nodes.Screens.DailyRun.NDailyRunScreen::SetupLobbyParams(MegaCrit.Sts2.Core.Multiplayer.Game.Lobby.StartRunLobby lobby)
 ---> System.TypeInitializationException: The type initializer for 'MegaCrit.Sts2.Core.Nodes.Screens.DailyRun.NDailyRunScreen' threw an exception.
 ---> System.NullReferenceException: Object reference not set to an instance of an object
   at MegaCrit.Sts2.Core.Nodes.Screens.DailyRun.NDailyRunScreen..cctor()
   --- End of inner exception stack trace ---
   at HarmonyLib.PatchClassProcessor.Patch()
   at HarmonyLib.Harmony.PatchAll(Assembly assembly)
   ...
   at STS2Mobile.Patches.ModLoaderPatches.RunAndroidModInitializationSafely(IModManagerFileIo fileIo, ModSettings settings, Object[] args)
   ...
   at MegaCrit.Sts2.Core.Helpers.OneTimeInitialization.ExecuteVeryEarly()

以及后续游戏正式加载主菜单时被彻底带崩的附带报错:

[ERROR] System.TypeInitializationException: The type initializer for 'MegaCrit.Sts2.Core.Assets.AssetSets' threw an exception.
 ---> System.TypeInitializationException: The type initializer for 'MegaCrit.Sts2.Core.Nodes.Screens.DailyRun.NDailyRunScreen' threw an exception.

  1. 机理分析推测 (Mechanism Analysis)
    结合日志来看,这似乎不是某一个 Mod 作者代码写错了,而是安卓端 ModLoader 的加载时机与原版游戏界面的初始化顺序发生了严重冲突,触发了 C# 的“类中毒”机制。具体推演如下:
  2. 过早的加载时机:
    安卓端的加载环境在引擎极早期(ExecuteVeryEarly())就运行了 Mod 的初始化逻辑。
  3. Harmony 强制触发静态构造:
    这些大型 Mod 为了注入新模式,通常会使用 Harmony.PatchAll(),或者手动去 Patch NDailyRunScreen(每日挑战/大厅屏幕)的相关方法。在 C# 中,Harmony 只要反射碰到了这个类,就会强制触发该类的静态构造函数(.cctor())。
  4. 前置资源未就绪,导致空指针:
    由于此时 Godot 引擎的渲染或 UI 资源大概率还没有加载完毕,NDailyRunScreen 的静态构造函数去拿这些前置数据拿了个空,当场抛出了 NullReferenceException。
  5. C# 底层“类中毒”机制:
    根据 C# 虚拟机的规则,一旦某个类的静态构造函数(.cctor)抛出未处理的异常,这个类就会被永久标记为失效(抛出 TypeInitializationException)。
  6. 游戏彻底暴毙:
    随后,游戏主程序在继续执行 LaunchMainMenu -> LoadLogoAnimation 时,不可避免地需要用到这个已经失效的类,最终导致连环报错,整个游戏启动流程彻底卡死。
  7. 总结
    目前来看,只要是试图修改 NDailyRunScreen(或者使用了无差别 PatchAll 碰到了该类)的 PC 端 Mod,在目前的移动端环境下几乎都会触发必死闪退。
    期望的修复方向:不知是否可以考虑调整移动端环境注入 Mod 的时机(比如延后到引擎基本 UI 节点和静态资产准备完毕之后),或者在底层对这种过早触发的反射做一些规避处理?
    感谢开发者的辛勤移植与维护!如果需要测试修复方案,我很乐意帮忙。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions