尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
8、【MOD开发】【骑砍2】跨平台MOD构建环境(八)/:游戏根 bin 与模块 bin——运行程序集的两种归属
【声明】本博客所有内容均为个人业余时间创作所述技术案例均来自公开开源项目如GithubApache基金会不涉及任何企业机密或未公开技术如有侵权请联系删除8、【MOD开发】【骑砍2】跨平台MOD构建环境八/游戏根 bin 与模块 bin——运行程序集的两种归属背景上篇 【MOD开发】【骑砍2】跨平台MOD构建环境七/参考程序集 vs 运行程序集——两套 DLL 的分工 把一对容易混淆的概念拆开了运行程序集包含元数据、ILIntermediate Language中间语言和资源能够真正运行参考程序集只保留公开 API 的元数据方法体被剥离或替换成占位不能直接运行。上篇还给出两条实践结论编译期使用参考程序集运行期由游戏提供同名的运行程序集Privatefalse/Private阻止游戏 DLL 被复制进工程输出避免“用旧 DLL 编新代码”的假通过。不过上篇把游戏安装目录里的 DLL 统一归入运行程序集并列出了两个路径游戏安装目录/bin/Win64_Shipping_Client/*.dll 游戏安装目录/Modules/模块/bin/Win64_Shipping_Client/*.dll这两个路径里的 DLL 确实都是运行程序集但它们的归属、加载时机和依赖方向并不相同。上篇没有展开这层差异本篇继续把运行程序集拆成两种归属。骑砍2 先给结论维度结果用途共同点都是运行程序集都含元数据与 IL能参与运行根 bin游戏宿主与引擎的启动基础目录提供始终存在的核心 API 与原生依赖模块 bin单个模块的作用域目录承载该模块自己的程序集与私有依赖一句话概括根 bin 管启动模块 bin 管模块两者不是参考程序集与运行程序集的区别而是运行程序集的两种归属。 根 bin宿主与启动层根 bin 位于游戏可执行文件旁边游戏安装目录/bin/Win64_Shipping_Client/它是 .NET 应用的基础目录也是宿主程序集解析的默认来源。游戏启动链路需要的核心程序集从这里就绪例如TaleWorlds.Core.dll、TaleWorlds.CampaignSystem.dll、TaleWorlds.MountAndBlade.dll以及负责模块加载的TaleWorlds.ModuleManager.dll。根 bin 不只包含托管程序集。本次检查的 1.5.4 环境里根 bin 共有 85 个 DLL其中 57 个是托管程序集28 个是原生 DLL。原生部分包括 PhysX、Steam、EOS、音频和图形相关依赖它们和 C# 程序集一起构成游戏宿主。图 1 游戏根 bin 与模块 bin 的目录分工需要特别区分一个常见误解根 bin 不等于“全部引擎程序集”。例如TaleWorlds.MountAndBlade.GauntletUI.dll、TaleWorlds.MountAndBlade.View.dll位于Modules/Native/bin/Win64_Shipping_Client/。引擎的核心 API 在根 bin部分视图层和内容层程序集则按模块归属。因此根 bin 的边界不是“所有 TaleWorlds 开头的 DLL”而是启动宿主需要、且不依赖具体模块激活的程序集。 模块 bin模块作用域模块 bin 位于模块自己的目录下游戏安装目录/Modules/模块/bin/Win64_Shipping_Client/它不是社区 Mod 专用目录。Native、SandBox、StoryMode、NavalDLC、Multiplayer这些官方模块同样使用该路径。官方模块与社区模块的差别在ModuleType和发布者不在 bin 的位置。模块 bin 的内容由模块清单决定。以Treasury为例SubModule.xml在Modules/Treasury/SubModule.xml:26-36声明了 DLL 名称和入口类SubModulesSubModuleNamevalueTreasurySubModule/DLLNamevalueTreasury.dll/SubModuleClassTypevalueTreasury.TreasurySubModule//SubModule/SubModulesDLLName指向模块 bin 下的主程序集SubModuleClassType指向模块加载后要实例化的入口类型。模块目录还可以携带自己的依赖例如RTSCamera的 bin 中同时存在0Harmony.dll、MissionLibrary.dll和RTSCameraAgentComponent.dll。这类依赖与根程序集不同它们只服务于当前模块或者服务于一组需要一起加载的模块。把它们放进根 bin会让模块依赖变成全局存在破坏模块启用与禁用之间的边界。 本次安装的实测对比本次检查的安装环境包含原版模块、NavalDLC 和若干社区模块统计结果如下维度结果用途根 bin 文件数85其中 57 个托管程序集、28 个原生 DLL模块 bin 文件数61全部为托管程序集两边同名 DLL0未发现根 bin 与模块 bin 的文件名重叠模块 bin 的组成官方模块 社区模块说明该路径不是 Mod 专属“没有同名重叠”这一点很关键。它说明两个目录不是同一批 DLL 的复制关系而是两套不同的程序集集合。根 bin 提供宿主能力模块 bin 提供模块能力。这些数字会随游戏版本、DLC 和已安装 Mod 变化不能当作固定常量但“根 bin 是宿主基础目录、模块 bin 是模块作用域目录”这个结构关系不会变。 模块加载链路模块加载从可执行文件开始经过根 bin 进入模块管理器再读取模块清单。图 2 从启动到模块 DLL 的加载链路TaleWorlds.ModuleManager的元数据中有ModuleInfo、SubModuleInfo、GetSortedModules、GetActiveGameAssemblies等成员。ModuleInfo保存模块的FolderPath、SubModules、DependedModulesSubModuleInfo保存DLLPath、DLLName和SubModuleClassTypeName。加载链路可以概括为Bannerlord.exe → 根 bin 的核心程序集就绪 → ModuleManager 扫描 Modules → 读取 SubModule.xml → 按 DependedModules / ModulesToLoadAfterThis 排序 → 激活模块并加载模块 bin 中的 DLL根程序集先就绪模块程序集只在对应模块被激活后进入加载集合。模块清单中的依赖声明决定加载顺序也决定某个模块能否在缺少前置模块时启动。Treasury的SubModule.xml:8-25声明了Native、SandBoxCore、Sandbox、CustomBattle、StoryMode依赖并给Bannerlord.Harmony、Bannerlord.ButterLib、Bannerlord.MCM设置了可选的LoadBeforeThis顺序。模块管理器据此排序而不是按文件夹名的字典序加载。 引用方向决定依赖边界将根 bin 与模块 bin 中的程序集拆开看引用方向是单向的模块程序集引用根程序集根程序集不反向引用具体模块。图 3 模块程序集引用根程序集反向不成立抽查结果可以说明这一点维度结果用途TaleWorlds.CampaignSystem.dll引用TaleWorlds.Core、TaleWorlds.Library等根程序集核心系统不依赖SandBoxSandBox.dll引用TaleWorlds.CampaignSystem、TaleWorlds.MountAndBlade等根程序集内容模块依赖宿主 APIRTSCamera.dll引用根程序集同时引用SandBox、0Harmony、MissionLibrary模块可以使用其他模块和私有依赖这个方向是工程上的必然选择。引擎不可能在编译期知道未来会安装哪些模块因此根程序集不能引用模块程序集模块则可以引用稳定的宿主 API并在清单中声明自己需要的前置模块。反过来如果把模块 DLL 放进根 bin宿主仍然不会自动加载它没有模块清单就没有DLLName和SubModuleClassType的绑定关系即使因为其他引用被加载也会绕过模块启用状态和依赖排序。⚠️ 同名 DLL 与程序集身份模块 bin 可以携带私有依赖但这不意味着可以直接复制引擎 DLL。模块之间还出现了两组同名程序集0Harmony.dll出现在RTSCamera、RTSCamera.CommandSystem、UnlimitedClanLimits三个模块里三份文件哈希相同TaleWorlds.MountAndBlade.Multiplayer.dll出现在CustomBattle和Multiplayer两个模块里文件名、大小和版本都相同但 SHA-256 不同。后一组更能说明问题**同名、同版本不等于同一份内容。**仅凭文件名或版本号判断程序集身份并不可靠。抽查的 TaleWorlds 程序集没有强名称PublicKeyToken为空版本统一为1.0.0.0。在这样的前提下同名程序集一旦进入同一个加载上下文通常只能保留一份最终使用哪一份会受加载顺序影响。如果两份程序集进入不同的加载上下文还会出现更隐蔽的问题类型名称看起来相同但类型身份不同跨上下文传递对象时可能触发类型转换失败。表现形式可能是TypeLoadException、MissingMethodException或InvalidCastException而不是直观的“DLL 找不到”。因此模块 bin 可以放模块自己的依赖但不应该复制TaleWorlds.*根程序集。引擎 DLL 由游戏安装提供模块只引用其公开 API。 正确的产物放置规则根据上面的边界Mod 产物的放置规则可以整理成一张表维度结果用途Mod 主程序集Modules/模块/bin/Win64_Shipping_Client/由模块清单加载SubModule.xml模块根目录声明依赖、DLL 与入口类TaleWorlds.*不复制使用游戏根 bin 提供的运行时程序集第三方依赖模块 bin 或独立依赖模块跟随模块发布避免污染根 bin根 bin不修改由游戏自身维护随更新覆盖编译阶段继续使用参考程序集并通过Privatefalse/Private阻止引擎引用被复制到输出目录。这样产出的 ZIP 只包含 Mod 自己的 DLL、SubModule.xml和资产文件部署时直接解压到Modules/模块/。如果 Mod 依赖 Harmony、MCM 或某个前置模块正确做法是声明依赖并按该依赖自身的发布方式安装而不是把依赖 DLL 丢进根 bin。根 bin 是共享的启动目录任何额外写入都可能影响其他模块也会在游戏更新或完整性校验时被覆盖。 小结两个路径里的 DLL 都是运行程序集不是参考程序集。根 bin 是宿主与启动基础目录包含核心托管程序集和原生依赖。模块 bin 是模块作用域目录官方模块和社区 Mod 都使用它。模块程序集引用根程序集根程序集不反向引用具体模块。同名同版本的程序集也可能内容不同不能把文件名或版本号当作唯一身份。Mod 主程序集放模块 bin引擎 DLL 不复制依赖按模块方式交付。一句话记忆根 bin 提供宿主模块 bin 提供模块编译看参考程序集运行看两套运行程序集。OK本篇先到这里如有疑问欢迎评论区留言讨论祝各位功力大涨技术更上一层楼更多内容见下篇 blog【MOD开发】【骑砍2】运行内存OOM 现场与虚拟内存术语
RELATED

相关推荐

C++模板:泛型编程、函数模板与类模板详解

C++模板:泛型编程、函数模板与类模板详解

文章目录模版1. 泛型编程2. 函数模版2.1函数模版格式:2.2 函数模版原理2.3 函数模版的实例化2.4 模版参数的匹配原则3. 类模版3.1 类模版的定义格式3.2类模版的实例化模版 1. 泛型编程 在C语言中要写交换函数时要针对不同的参数类型来写多个对应的函数 void swap…

📅 2026/10/30 2:29:49
40秒生成3分钟半的完整歌曲:Yue2-3B-GGUF长音频性能实测全报告(RTX 5090)

40秒生成3分钟半的完整歌曲:Yue2-3B-GGUF长音频性能实测全报告(RTX 5090)

40秒生成3分钟半的完整歌曲:Yue2-3B-GGUF长音频性能实测全报告(RTX 5090) 📌 一句话总结:Yue2-3B-GGUF 是 Yue2-3B 文生音乐模型的 audio.cpp 原生 GGUF 权重包。实测在 RTX 5090 上,Q8_0 量化组合约 39 秒…

📅 2026/10/30 2:24:49
GGUF vs BF16 vs vLLM:NVIDIA-Nemotron-3.5-Bolt-74B-A9B-GGUF 全平台部署路径选择指南(llama.cpp/vLLM/24GB版)

GGUF vs BF16 vs vLLM:NVIDIA-Nemotron-3.5-Bolt-74B-A9B-GGUF 全平台部署路径选择指南(llama.cpp/vLLM/24GB版)

GGUF vs BF16 vs vLLM:NVIDIA-Nemotron-3.5-Bolt-74B-A9B-GGUF 全平台部署路径选择指南(llama.cpp/vLLM/24GB版) 如果你正在寻找 NVIDIA-Nemotron-3.5-Bolt-74B-A9B 的本地部署方案,本文帮你一次讲清三条路线怎么选:用…

📅 2026/10/30 2:24:49
MORE NEWS

更多资讯

📰

Axure原型设计入门:一张图掌握核心操作流程

很多刚接触原型设计的朋友,第一次打开Axure看到满屏的元件库、属性面板和交互面板,第一反应往往是“这玩意儿比写代码还复杂”。我见过太多人装了软件、看了两小时教程,然后默默关掉,转头继续用PPT画方块。问题不在于Axure难&…

📰

安全隔离网闸:物理隔离下的受控数据交换原理与实战配置

1. 安全隔离网闸到底在解决什么问题第一次接触“安全隔离网闸”这个概念,是在一个跨网络数据交换的项目里。当时业务方需要在两套物理隔离的网络之间同步一批文件,开发同学第一反应是“拉根网线不就行了”,运维同学则坚持“绝对不行&#xff…

📰

Transformer自注意力机制:置换不变性与位置编码解析

1. 理解Transformer自注意力的置换不变性当我们说Transformer的自注意力机制是"置换不变"的,本质上是在描述它对输入序列中元素的排列顺序不敏感的特性。想象你面前有一副扑克牌,无论你怎么洗牌(改变顺序),只…

📰

深入解析SQL COUNT函数:从基础语法到性能优化

1. 为什么我们需要深入了解COUNT函数?在日常数据库操作中,COUNT函数可能是最常用的聚合函数之一。但很多人只是停留在"SELECT COUNT(*) FROM table"这样的基础用法上,实际上这个简单的函数背后隐藏着许多值得深究的技术细节。我曾在…

📰

代码签名实战:密钥、时间戳与证书链验证指南

1. 代码签名到底在签什么:先把概念钉死我第一次接触“代码签名”这个词,是在给一个桌面工具做分发的时候。当时打包出来的安装包在别人电脑上双击就被拦下来了,弹窗提示“未知发布者”。我一开始以为是杀毒软件误报,换了几台机器测…

📰

5G毫米波与室内电波传播仿真技术解析

1. 项目背景与核心价值城市与室内电波传播仿真一直是无线通信系统设计中的关键难题。随着5G毫米波频段和物联网设备的普及,传统经验模型在复杂城市场景下的预测误差可达15dB以上。我们团队开发的这套高频电磁场仿真系统,专门针对280MHz-100GHz频段的传播…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬