首页 / 资讯中心 / 文章详情

YgoMaster 与 BepInEx 同进程共存:基于 Mono 运行时的加载机制与配置指南

YgoMaster 与 BepInEx 同进程共存:基于 Mono 运行时的加载机制与配置指南 ★ FEATURED ARTICLE
后端游戏开发逆向工程【免费下载链接】YgoMasterOffline Yu-Gi-Oh! Master Duel项目地址https://gitcode.com/gh_mirrors/yg/YgoMaster点击查看免费下载本文基于 YgoMaster 仓库中的 BepInEx 文档 展开说明如何让 YgoMaster 与 BepInEx 插件如 UnityExplorer 等 Unity 工具类插件在同一个 Master Duel 进程中同时工作以及背后的运行时选择机制。读完后你将能够独立完成mono/目录的部署与回退理解YgoMasterLoader.dll中 mono 与 CLR 双运行时的加载逻辑并能使用mono_log.txt对加载问题进行诊断。一、背景为什么两者同进程需要特殊处理BepInEx 是面向 Unity 游戏的 Mod 框架生态中有不少实用插件例如 UnityExplorer。YgoMaster 本身并不依赖 BepInEx——它的常规工作流是通过YgoMasterLoader.dll注入或启动masterduel.exe后在游戏进程内自建 .NET 运行时但确实存在希望把 BepInEx 插件与 YgoMaster 一起跑起来的场景。原文档给出的关键经验结论是.NET Framework / .NET 两个运行时在同进程内运行似乎会互相冲突原文用词是 seem to conflict至少发生在 Master Duel BepInEx YgoMaster 这一组合中。这是作者基于实际运行情况给出的经验性描述而非绝对性结论。解决办法让 YgoMaster 改走Mono 运行时。YgoMaster 在加载 .NET 侧代码前会检查游戏目录旁的mono/文件夹——若存在则优先使用其中的 mono 文件来承载YgoMasterClient从而规避 CLR 与其他托管运行时在同一进程内的冲突。二、部署步骤引入mono/目录按照 Docs/BepInEx.md 的操作说明从官方 releases 下载YgoMaster-Linux-Data-v1.zip即与 Linux 方案 共用的 mono 运行时数据包将其解压/合并到你的 YgoMaster 文件夹中merge不是覆盖整个目录像往常一样运行YgoMasterClient.exe或YgoMaster.exe。部署完成后的目录结构应当如下YgoMaster ├── Data/ ├── mono/ ← 本次新增的目录 ├── MonoRun.exe ├── YgoMasterClient.exe ├── YgoMaster.exe └── YgoMasterLoader.dll各条目说明条目作用Data/YgoMaster 数据目录卡牌数据、卡组、客户端定制内容等见 YgoMaster/Datamono/mono 运行时文件是本次部署新增的部分。YgoMaster 检查该目录存在即改用其中的 mono 文件运行MonoRun.exemono 宿主启动器面向 Linux / Wine 场景。Windows 下正常流程不会直接用它无需关心YgoMasterClient.exe客户端入口负责启动游戏、拉起本地服务器、注入加载器等YgoMaster.exe离线对战的本地服务器进程YgoMasterLoader.dll原生加载器负责 hook 游戏函数、在进程内创建并启动托管运行时回退方式删除或重命名mono文件夹即可停用 mono恢复默认的 CLR 加载路径。三、运行时选择机制源码视角mono 与 CLR 的切换逻辑位于原生加载器 YgoMasterLoader.cpp 的LoadDotNetImpl()中HRESULT LoadDotNetImpl() { #ifdef WITH_MONO wchar_t monoDllPath[MAX_PATH] {0}; if (MonoExists(monoDllPath) LoadMono(monoDllPath, YgoMasterClient.exe, runningLive ? live : )) { return 0; } #endif ICLRMetaHost* metaHost; ... result metaHost-GetRuntime(Lv4.0.30319, ...); ... result runtimeHost-ExecuteInDefaultAppDomain(binaryPath, LYgoMasterClient.Program, LDllMain, runningLive ? Llive : NULL, NULL); return result; }可以看到这是一个优先 mono、失败回退 CLR的结构两条路径对比如下路径触发条件具体行为Monomono\bin\mono-2.0-sgen.dll存在且LoadMono成功加载mono-2.0-sgen.dll通过 mono API 初始化域并调用YgoMasterClient.Program:DllMainCLR默认上述条件不满足CLRCreateInstance获取v4.0.30319运行时ExecuteInDefaultAppDomain执行同目录下的YgoMasterClient.exe其中MonoExists的判定非常直接——只检查一个文件是否存在mono.hBOOL MonoExists(wchar_t* dllPath) { GetRelativeFilePath(dllPath, L\\mono\\bin\\mono-2.0-sgen.dll); return FileExistsW(dllPath); }也就是说mono/目录只要缺少bin/mono-2.0-sgen.dll这一关键文件整个目录会被视为不存在静默回退到 CLR 路径——这是排查部署了但没生效时首先要确认的点。3.1 LoadMono 的初始化流程LoadMonomono.h的完整流程包括通过LoadLibraryW加载mono-2.0-sgen.dll并用GetProcAddress解析全部所需符号mono_config_parse、mono_jit_init_version、mono_runtime_invoke等任一缺失则返回失败设置 mono 目录程序集目录为mono\lib配置目录为mono\etcmono_set_dirs(assemblyDir, configDir)对应 mono.h 第 264-270 行。这也是官方数据包目录布局的由来lib/mono/4.5/含mscorlib.dll、etc/、bin/*.dll分别取自 mono Windows 稳定版安装的对应位置详见 Docs/Linux.md 中 Where themonofiles come from 一节以mono_jit_init_version(DefaultDomain, v4.0.30319)初始化域版本号与 CLR 路径保持一致调用mono_domain_set_config(monoDomain, ., )—— 源码注释说明这是为了规避ConfigurationErrorsExceptionThe ExeConfigFilename argument cannot be null的 workaround打开目标程序集定位方法YgoMasterClient.Program:DllMain通过mono_runtime_invoke调用。若抛异常则沿inner_ex链把完整异常信息写入日志文件后返回失败。3.2 LoadDotNet 的触发时机LoadDotNet在什么时刻被调用取决于当前运行方式YgoMasterLoader.cpp注入/正常启动模式hookLoadLibraryW检测到GameAssembly.dll被加载后立即对该模块导出的il2cpp_init建立 hookHook_LoadLibraryW/Hook_il2cpp_init在游戏 IL2CPP 运行时初始化返回后触发LoadDotNetlive向已运行游戏注入模式此时GameAssembly.dll已存在改为 hookTranslateMessageHook_TranslateMessage在游戏开始处理消息循环时触发LoadDotNet。CreateHooks()YgoMasterLoader.cpp中还包含一个与 BepInEx 直接相关的分支LPWSTR commandLine GetCommandLineW(); BOOL isBepInEx commandLine wcsstr(commandLine, L--YgoMasterDir\) ! NULL; if (!isBepInEx) { if (GetModuleHandleW(LGameAssembly.dll)) { runningLive true; } else if (!isBepInEx MH_CreateHook(LoadLibraryW, Hook_LoadLibraryW, ...) ! MH_OK) ... }当进程命令行中含--YgoMasterDir时加载器判定自己处于 BepInEx 场景跳过LoadLibraryW→GameAssembly.dll→il2cpp_init这条 hook 链——因为此时游戏已经在运行初始化时机由 BepInEx 插件接管见下一节。四、MonoRun.exe 的角色MonoRun.exe是一个极简的 mono 宿主启动器MonoRun.cpp它接收目标程序集名作为第一个参数确认mono/存在后调用LoadMono(monoDllPath, argv[1], MonoRun)完成加载加载失败时会提示Load mono failed. Create mono_log.txt in the current directory and run the command again to see the error log其入参MonoRun会一路传入Program.DllMainProgram.cs被识别为特殊标志位public static int DllMain(string arg) { if (arg MonoRun) { Console.WriteLine((MonoRun)); IsMonoRun true; Main(new string[0]); return 0; } ... }IsMonoRun true之后Program.Main中启动本地服务器YgoMaster.exe的方式也随之改变Program.cs检测到已存在的服务器进程时按MonoRun进程名匹配拉起新进程时则改为Process.Start(MonoRun.exe, YgoMaster.exe)即让服务器同样跑在 mono 宿主下而不是直接Process.Start(YgoMaster.exe)走 CLR。这正是 Linux / Wine 方案Docs/Linux.md的入口Steam 里添加非 Steam 游戏指向MonoRun.exeLaunch Options 填YgoMasterClient.exe整个 YgoMaster客户端与服务器都在 mono 下运行。Windows BepInEx 场景与 Linux 场景共用同一份YgoMaster-Linux-Data-v1.zip数据包区别只在于MonoRun.exe在 Windows 原生流程中并不直接参与。五、YgoMasterBepInEx 桥接插件仓库根目录提供了 BepInEx 插件源码 YgoMasterBepInEx.cs 与编译脚本 YgoMasterBepInEx.bat。从代码引用的BepInEx.Unity.IL2CPP、BasePlugin与[BepInPlugin]特性看该插件面向 BepInEx 6IL2CPP 版。5.1 插件在进程内做了什么插件的Load()方法YgoMasterBepInEx.cs流程解析进程命令行中的--YgoMasterDir参数得到 YgoMaster 所在目录目录不存在则直接返回校验目录下必须同时存在YgoMasterLoader.dll与YgoMasterClient.exe否则打印Failed to find ...后放弃用LoadLibrary加载YgoMasterLoader.dll此时其DllMain执行CreateHooks并按命令行判定为 BepInEx 模式Assembly.LoadFile加载YgoMasterClient.exe反射调用YgoMasterClient.Program.DllMain完成 YgoMaster 客户端在进程内的完整初始化。值得注意的是源码中的这行string dllMainArg null;//live;配合方法顶部的// TODO: Support live注释可以推断当前版本的桥接插件尚不支持 live向已运行游戏注入模式DllMain以null参数调用即走离线流程。5.2 启动端的配套GameLauncherMode.BepInEx客户端侧存在与之对应的启动模式GameLauncherMode.BepInExGameLauncher.cs/// summary /// Launch the process normally but provide args to be picked up by the YgoMasterBepInEx plugin /// /summary BepInEx该模式下 GameLauncher.Launch 不做任何注入而是正常启动masterduel.exe并附加参数Process.Start(exePath, --YgoMasterDir\ Program.CurrentDir \)即游戏照常启动由 BepInEx 插件在游戏内读取--YgoMasterDir参数接手。LaunchMode是 ClientSettings 中可配置的项从ClientSettings.json读取因此使用 BepInEx 方案时需要把LaunchMode设为BepInEx并保证游戏安装中已部署 BepInEx 6 及编译好的插件。5.3 插件编译YgoMasterBepInEx.bat 使用 .NET Framework 自带编译器直接编译引用 BepInEx 安装目录中的程序集call %WINDIR%\Microsoft.NET\Framework\v4.0.30319\csc.exe /target:library /platform:x64 YgoMasterBepinEx.cs /reference:BepInEx.Core.dll /reference:BepInEx.Unity.IL2CPP.dll /reference:Il2CppInterop.Runtime.dll /reference:netstandard.dll /reference:System.Runtime.dll使用前需先确保当前 BepInEx 安装中存在上述引用程序集编译产物放入游戏的 BepInEx plugins 目录即可。六、诊断与常见问题1. mono 没有生效目录被忽略MonoExists只认mono\bin\mono-2.0-sgen.dllmono.h。若压缩包解压后路径错位例如多套了一层目录整个mono/会被静默忽略并回退 CLR。核对解压结果是否符合第二节的目录结构是第一步。2. 使用 mono_log.txt 查看 mono 内部日志mono.h 中启用了MONO_VERBOSE_LOGGINGLoadMono开始时检查当前目录是否存在mono_log.txt文件存在则开启日志并把 mono 的 trace 级别设为debug、掩码设为allmono_trace_set_level_string/mono_trace_set_mask_string所有 mono 消息经OnMonoLog/OnMonoPrint以追加方式写入该文件。Linux 文档 建议同时在 Master Duel 根目录和YgoMaster目录下各创建一个空的mono_log.txt即可收集两个上下文的全部 mono 输出用于排查。MonoRun启动失败时的错误提示见第四节也指向同样的手段。3. BepInEx 插件报 Failed to find插件会依次检查YgoMasterLoader.dll与YgoMasterClient.exe是否存在于--YgoMasterDir指向的目录YgoMasterBepInEx.cs。请确认LaunchMode为BepInEx且 YgoMaster 文件夹与游戏主目录的相对位置正确YgoMasterClient.exe会校验../masterduel_Data/Plugins/x86_64/duel.dll是否存在见 Program.cs。4. 需要回到默认行为删除或重命名mono文件夹即可原文档明确给出的回退方式LoadDotNetImpl将自动回到 CLR 路径无需其他改动。七、小结YgoMaster 与 BepInEx 可以在同一游戏进程中共存前提是让 YgoMaster 改走 mono 运行时以规避 .NET Framework / .NET 在同进程内的经验性冲突运行时选择完全由mono\bin\mono-2.0-sgen.dll是否存在决定LoadDotNetImpl中实现了mono 优先、CLR 回退的加载结构MonoRun.exe服务于 Linux / Wine 与 mono 宿主场景YgoMaster.exe服务器在IsMonoRun下也会改由MonoRun.exe拉起BepInEx 场景由GameLauncherMode.BepInEx启动端附加--YgoMasterDir参数与 YgoMasterBepInEx.cs 插件游戏内加载加载器并反射调用Program.DllMain两端配合完成加载器通过命令行参数识别该模式并跳过自身的il2cpp_inithook 链。赞分享后端游戏开发逆向工程【免费下载链接】YgoMasterOffline Yu-Gi-Oh! Master Duel项目地址https://gitcode.com/gh_mirrors/yg/YgoMaster点击查看免费下载相关推荐TEngine 运行时配置加载指南Luban 二进制数据与 YooAsset 集成方案TEngine 运行时配置加载指南Luban 二进制数据与 YooAsset 集成方案 导读 本文基于 TEngine 仓库中 Luban 配置生成链路 C游戏开发yaitoo/xun配置热重载运行时配置变更的动态加载机制yaitoo/xun配置热重载运行时配置变更的动态加载机制 痛点传统配置管理为何成为开发效率的瓶颈 在Web应用开发过程中你是否经常遇到这样的场景每次后端Web框架Talon 配置加固模型基于运行时验证的角色防御、配置面管控与可回滚变更机制Talon 配置加固模型基于运行时验证的角色防御、配置面管控与可回滚变更机制 导读 configuration hardening 是 Talon libs人工智能大模型AI AgentAgent 框架自主智能体工具调用代码智能体MCP ClientsAI 技能创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站