外观
启动引导
StudioModLoader.Bootstrapper.Initialize() 是整个框架的入口,由 Installer 通过 Cecil 注入到 Studio.Startup.dll 的初始化序列中,是 Studio 启动时最早执行的 Mod 相关代码。
入口不止一种
Initialize() 设计上不依赖特定的注入方式——Cecil 注入、Doorstop,或 Unity 原生清单调用都可以触发它。内部有 _initialized 幂等保护,重复调用会直接返回。
四个步骤
csharp
public static void Initialize()
{
if (_initialized) return;
_initialized = true;
SetupAssemblyResolver(); // 1
GameObject loaderHost = new GameObject("[StudioModLoader]");
UnityEngine.Object.DontDestroyOnLoad(loaderHost); // 2
loaderHost.AddComponent<LoaderManager>(); // 3
ApplyCorePatches(); // 4
}① 注册全局程序集解析器
挂 AppDomain.CurrentDomain.AssemblyResolve,在下面这些目录里按文件名查找缺失的程序集,并额外检查每个目录的第一层子目录:
| 搜索目录 |
|---|
<Studio 安装目录>\Studio_Data\Managed |
<Studio 安装目录>\Mods |
<Studio 安装目录>\deps |
%LOCALAPPDATA%\Studio 2.0\Mods |
<Studio 安装目录> |
这就是插件可以把自己的依赖 DLL 和主 DLL 放在同一个子目录里的原因。
② 创建全局宿主
创建一个名为 [StudioModLoader] 的 GameObject,标记 DontDestroyOnLoad——切换场景时不会被销毁,保证整个 Studio 会话期间 Loader 一直活着。
③ 挂载 LoaderManager
LoaderManager 是一个 MonoBehaviour,负责扫描插件、托管 F10 覆盖层、驱动自动更新与遥测服务。
④ 应用核心补丁
用独立的 Harmony 实例(Identifier = com.studiomodloader.core)打三个补丁:
| 补丁 | 目标 | 作用 |
|---|---|---|
SkipUpdatePrompt | Studio.Startup.Initialization.Steps.PromptForApplicationUpdateStep.ExecuteAsync | 跳过 Studio 官方强制版本更新弹窗,避免它把进程顶掉 |
SaveModel | BLStudio.SaveModelToLocalFileSystem(Postfix) | 捕获存盘事件并分发到 StudioAPI.OnModelSaved |
| 特性补丁扫描 | 当前程序集 | 自动应用 MenubarForWindow、MenuHandler 等声明式补丁 |
三个补丁各自独立 try/catch:某个目标类型在当前 Studio 版本里不存在时只记一条 Warning,不影响其余补丁与整体启动。
失败处理
Initialize() 整体包在 try/catch 里。出现严重异常时:
- 异常与堆栈写入
modloader.log(记录器前缀Bootstrapper); - 若
LoaderManager已实例化,调用ReportBootstrapError(ex),错误会显示在 F10 控制台的「已加载插件」页。
Studio 本身不会被这个异常带崩——引导失败的结果是「Mod 功能不可用」,而不是「打不开 Studio」。