十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MelonLoader 双架构模组加载机制拆解

MelonLoader 双架构模组加载机制拆解 MelonLoader 双架构模组加载机制拆解【免费下载链接】MelonLoaderThe Worlds First Universal Mod Loader for Unity Games compatible with both Il2Cpp and Mono项目地址: https://gitcode.com/gh_mirrors/me/MelonLoaderMelonLoader 是一个同时支持 Il2Cpp 与 Mono 双编译架构的 Unity 游戏模组加载器也是目前社区中跨平台覆盖最广的 Unity 模组加载方案之一。多数通用模组框架只处理 Mono 游戏而 MelonLoader 把 Il2Cpp 游戏作为一级公民来对待——这两条路径在实现上几乎没有共享代码理解它们各自如何钻进游戏进程是读懂这个项目的关键。本文按源码结构从注入、运行时适配到模组加载逐层拆解。Unity 两种编译架构决定了加载器的两条路Unity 发布游戏时提供两种后端。Mono 架构下C# 代码编译为 IL 字节码Managed/目录里是一堆可以正常反射、正常用 Harmony 打补丁的 .dll加载器要做的事相对直接。Il2Cpp 架构下IL 先被 AOT 编译成 C最终产物是GameAssembly.dllLinux 下为.so、macOS 下为.dylib这个纯原生二进制——没有托管元数据、没有可加载的程序集传统的往 Mono 域里塞个程序集思路完全不成立。这就是 MelonLoader 代码库整体结构的设计依据MelonLoader.Bootstrap/是原生引导层负责在不同平台把代码送进游戏进程其下RuntimeHandlers/按 Mono、Il2Cpp、Dotnet 三条线分别处理运行时差异MelonLoader/Melons/则是托管域之上的模组管理层。双架构不是两个分支开关而是两套独立的接入方案。 注入层三种平台各走各的路引导层要解决的第一问题是如何进入游戏进程。入口在MelonLoader.Bootstrap/Exports.cs它先做一步启发式判断进程目录下是否存在Data/或GameName_Data/目录以此确认目标确实是 Unity 游戏避免误伤普通程序。确认之后分平台走三条路。Windows伪装成系统 DLL 的代理库Windows 下 MelonLoader 的引导库被改名为一个系统常见的 DLL 文件名默认version.dll也可以叫winhttp.dll、d3d9.dll、dsound.dll等 14 种名字利用游戏启动时对系统库的依赖完成加载。Proxy/ProxyResolver.cs的工作是找到同名真实模块本地_original.dll备份优先其次系统目录然后把该模块的每一个导出函数重建到代理库的导出表中逐一转发给真实模块。游戏以为自己在加载 winhttp实际上DllMain里跑的是 MelonLoader 的初始化代码——对游戏侧完全透明。Linux 与 macOS预加载与自清理Linux 通过LD_PRELOADmacOS 为DYLD_INSERT_LIBRARIES在main之前挂钩引导库。这里有一个容易被忽略的细节游戏运行时会派生子进程Steam overlay、crash handler 等如果预加载环境一直挂着子进程会重复进入引导逻辑。Exports.cs里的RemoveLibraryPreloadEnv会在确认 Unity 即将进入main后把自己从LD_PRELOAD/DYLD_LIBRARY_PATH中摘除——代码注释直言hook libc 的 pre-main 天生危险挂得越短越好。Mono 路径在 JIT 初始化里劫持程序集搜索路径Mono 游戏的接入点选在mono_jit_init_version——Mono 运行时初始化的必经函数。RuntimeHandlers/Mono/MonoHandler.cs用 Dobby 函数钩子静态库位于MelonLoader.Bootstrap/Deps/把它重定向到自己的 detour 中处理链条是解析运行时版本号判断是旧版 Mono≤3还是新版 MonoBleedingEdge重写程序集搜索路径把BaseLibs/NetStandardPatches/旧 Mono或BaseLibs/MonoBleedingEdgePatches/新 Mono里的补丁版 BCL 前置到路径最前面若开启了 Debug 模式初始化--debugger-agent调试服务器调回原函数让 Mono 正常完成初始化。第 2 步是双架构通用承诺的底层支撑Unity 自带的 Mono BCL 存在不少已知缺陷MelonLoader 不替换整个运行时而是用逐文件前置覆盖的方式只修坏掉的那几个程序集System.dll、System.Xml.dll等把兼容面控制到最小。同一文件里对mono_image_open_from_data_with_name的 detour 则支持--melonloader.monosearchpathoverride参数允许用户指定路径整体替换某个程序集的加载来源。Il2Cpp 路径进程内启动 .NET 6 并从 C 里重建程序集Il2Cpp 游戏里没有可用的托管环境MelonLoader 的策略是在原生进程里再开一个 .NET 运行时。RuntimeHandlers/Il2Cpp/Il2CppHandler.cs钩住两个函数il2cpp_init运行时初始化和il2cpp_runtime_invoke托管方法调用入口。从 GameAssembly 重建托管程序集C# 元数据虽然被 AOT 掉了但 C 产物中保留了类型与方法的符号结构。Dependencies/Il2CppAssemblyGenerator/Core.cs下称 AGF的工作流程计算GameAssembly的 SHA-512 哈希与上次缓存比对哈希变化才触发重新生成然后依次执行 Cpp2IL 反编译、Il2CppInterop 程序集重建、Unity 依赖库下载与去混淆正则映射产出可以正常反射的托管程序集。生成结果有本地缓存且支持--melonloader.agfoffline强制离线运行、--melonloader.agfregenerate强制重建——游戏更新后首次启动通常最慢卡在这里属于预期行为而非故障。在原生进程里拉起 .NET 运行时il2cpp_init的 detour 调完原函数后交给RuntimeHandlers/Dotnet/DotnetHandler.cs。这里的输入是一段级联搜索逻辑依次尝试配置文件指定的便携运行时、游戏目录下的dotnet/文件夹、引导库自带目录、系统安装的 hostfxr、Windows 自动安装安装包就存在仓库Dependencies/Dotnet/里、最后才走在线下载。找到 hostfxr 后用MelonLoader/net6/MelonLoader.runtimeconfig.json初始化一个独立 .NET 域加载MelonLoader.NativeHost.dll作为托管入口。值得注意的时机设计真正的模组启动被推迟到il2cpp_runtime_invoke第一次观测到Internal_ActiveSceneChanged方法调用时——这标志着游戏已完成场景激活、运行时状态稳定。太早跑托管代码是 Il2Cpp 游戏崩溃的高频原因用这个活体信号做闸门是这套方案能稳定的关键。托管域之上模组的发现、排序与兼容层托管域起来之后MelonLoader/Melons/目录接管剩余流程。MelonHandler.Setup创建Plugins/旧式插件与Mods/两个目录并扫描其中的程序集每个带MelonInfoAttribute的类型经MelonTypeBase注册后按MelonPriorityAttribute排序实例化加载顺序因此可预测、可控制。Harmony 补丁系统有独立封装Fixes/Harmony/日志级别由--melonloader.harmonyloglevel控制可打到 IL 级别。配置走 TOML首次运行后生成UserData/Loader.cfg关键项都有对应启动参数[loader] debug_mode true harmony_log_level Warn disable_start_screen false另一层设计是Dependencies/CompatibilityLayers/IPA、Muse.Dash、StressLevelZero 等流行框架的适配层都放在这里老模组无需改动就能在新加载器上跑。这是双架构之外MelonLoader 维持生态延续的第二个抓手。边界、限制与下一步把已知限制摆清楚比罗列功能更有实用价值Il2Cpp 游戏强依赖 .NET 6 运行时。级联搜索虽然兜底能力强最坏情况在线下载但企业网络环境下需要预先把Dependencies/Dotnet/里的便携运行时放到游戏目录Windows 下代理 DLL 文件名可能冲突。游戏如果自带同名 DLL如自实现的version.dll或启用了严格的 DLM 反作弊version.dll默认名可能失效需要按 README 的列表换名重试首次运行 Il2Cpp 游戏需要网络用于 AGF 远程 API 与运行时下载离线环境需提前用--melonloader.agfoffline准备缓存Linux 的 LD_PRELOAD 方式依赖游戏以原生方式启动通过 Proton/Wine 跑 Steam 游戏时行为会有差异仓库MelonLoader.Bootstrap/OSXEntry/与Utils/WineUtils.cs针对这些场景做了识别但并非所有 Wine 配置都能自动生效。如果你想深入某个环节建议的切入路径很明确先跑git clone https://gitcode.com/gh_mirrors/me/MelonLoader拿到源码读MelonLoader.Bootstrap/RuntimeHandlers/下三个 Handler 的 detour 函数每个都只有几十行核心逻辑再顺着Dependencies/Il2CppAssemblyGenerator/Core.cs的Run()方法走一遍 Il2Cpp 的生成管线——这条主线走完整个加载器进程怎么被拿到、程序集怎么被造出来的问题就都闭环了。【免费下载链接】MelonLoaderThe Worlds First Universal Mod Loader for Unity Games compatible with both Il2Cpp and Mono项目地址: https://gitcode.com/gh_mirrors/me/MelonLoader创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表