为 BML+ 开发 Mod¶
编写 Mod 时应使用已发布的 BML+ SDK。只有修改 Loader、SDK、脚本宿主或内建服务时 才需要构建 BML+ 仓库。
不确定选择哪条路线时,先从脚本 Mod 开始。它不需要 C++ 构建,并具有最短的编辑、 热重载和诊断循环。只有明确需要原生 Hook、原生内存、生成式 IMC Provider 或 性能敏感循环时,再进入原生路线。
选择开发路线¶
| 路线 | 适用场景 | 主要代价 |
|---|---|---|
| 脚本 Mod | 希望快速修改和测试,需要命令、配置、UI、玩法脚本或 CKAngelScript 引擎访问,但不想配置 C++ 构建。 | 不能提供自定义 IMC Provider,也不应承担不安全 Hook 或性能敏感的原生循环。 |
| 原生 Mod | 需要 C++20、直接 Virtools 集成、原生 Hook、生成式 IMC Provider,或必须严格控制热路径。 | 需要兼容 MSVC ABI 的 Win32 构建,并明确处理 DLL ABI 和所有权。 |
| 原生插件加 CKAngelScript 扩展 | 不安全或性能敏感的服务由原生代码负责,但脚本需要小型、类型化的控制接口。 | 原生插件必须通过 CKAngelScript 注册并维护该脚本接口。 |
不要在 BML 中重新封装一套 CKAngelScript 已有的 Scene、Behavior、Component、 Message 或 Async API。CK/Vx 操作使用 CKAngelScript;Mod 身份、生命周期、配置、 命令、Loader UI 和 Mod 级服务使用 BML+。
开始编写脚本 Mod¶
- 在
ModLoader/Mods中打开 PowerShell,用 SDK 模板创建 Mod:
& "<BML-SDK>/scripts/New-BMLScriptMod.ps1" `
-Id "yourname.my-mod" -Name "My Mod" -Author "Your Name"
命令会创建目标目录、合法的类名和入口文件名,并写入你的元数据。也可以手动复制
templates/script-mod-template。
- 打开生成的目录和 README。
- 确认配套的
BuildingBlocks/AngelScript.dll已安装。 - 不修改生成的源码,直接启动 Player,同时确认游戏内问候语和
ModLoader/ModLoader.log中的加载日志。 - 保持生成的 id 稳定;以后修改 id 会成为另一个 Mod,并且需要重启 Player。
- 保持 Player 运行。保存已加载目录中的源码会自动热重载;只有新增入口、修改 id 或修改依赖时才需要重启。
- 在 Mod 目录中运行
scripts/Pack-BMLScriptMod.ps1;产物位于dist/<目录名>.zip。再在没有开发目录副本的环境中测试该 zip。
先阅读脚本 Mod 教程导读,
需要准确声明时使用其中的 API 参考。支持脚本 Mod 的 SDK 会把同一套页面安装到
share/BML/docs/zh-CN。
开始编写原生 Mod¶
- 在存放源码项目的目录中打开 PowerShell,用 SDK 模板创建 Mod:
& "<BML-SDK>/scripts/New-BMLNativeMod.ps1" `
-Id "yourname.my-mod" -Name "My Mod" -Author "Your Name"
命令会让 CMake target、C++ 类名、源文件名和元数据保持一致。也可以手动复制
templates/native-mod-template。
- 打开生成的 README。先运行
cmake --help,明确选择本机已安装的 Visual Studio 生成器,再配置它的 Win32 目标,并让CMAKE_PREFIX_PATH指向解压后的 BML+ SDK。源码和构建目录应尽量短,例如C:\Mods\MyMod,避免 MSBuild 在过深 目录中出现文件跟踪错误。 - 让
VIRTOOLS_SDK_PATH指向 Virtools SDK 2.1。 - 构建
RelWithDebInfo,并将 Mod 安装到ModLoader/Mods。原生接口会让 C++ 对象 跨越 DLL 边界,因此原生 Mod 必须与装载它的 Loader 链接同一套 MSVC 运行库。BMLPlus-<version>.zip中的运行时基于 Release 版 MSVC 运行库构建,Debug.bmodp与之不具备 ABI 兼容性;RelWithDebInfo在使用兼容运行库的同时保留调试 信息。bml_add_mod会把 Mod 的运行库固定为所配置 SDK 使用的那一套,并在CMAKE_MSVC_RUNTIME_LIBRARY与之冲突时直接让配置失败,所以两者不会在无声中错位。BMLPlus-SDK-<version>-Debug.zip是受支持的例外。它包含 Debug 版bin/BMLPlus.dll及其.pdb,只要同时用这个 Debug Loader 覆盖BuildingBlocks/BMLPlus.dll,Debug Mod 就是有效的。Loader 和所有已安装的原生 Mod 必须处于同一侧;测试待发布产物前要换回 Release 版 Loader。 - 启动该 Ballance 安装目录中的 Player,等待主菜单出现,然后同时确认游戏内提示、
ModLoader/ModLoader.log中的加载日志和示例命令。只看到 BML+ 版本并不代表目标 Mod 已经加载。 - 构建 Release,并测试准备发布的同一个产物。
SDK 的 CMake 入口为:
增加所有权、回调、UI 或跨 Mod 服务前,先阅读原生 Mod API 总览; 某项能力有不止一种写法时,参见原生 API 该走哪条路。
两条路线共同遵守的规则¶
- 保持 Mod id 稳定。其他 Mod 会用它声明依赖和服务所有权。
- 在加载前声明依赖,不要在逐帧回调中临时寻找必需的 Mod。
- CK 借用对象不转移所有权;关卡和对象变化后要重新验证。
- 限制逐 Tick、渲染、引擎 Hook 和同步 RPC 中的工作量。
- 开发期间记录一条明确的启动日志,并在干净的
ModLoader/Mods目录中测试发布包。 - 在 Mod README 中写明所需的 BML+、CKAngelScript、原生插件和依赖版本。
选择跨 Mod 通信方式¶
| 需求 | 使用 |
|---|---|
| 同一进程内少量具名标量或字节值 | DataShare |
| Mod 之间的类型化请求与响应、异步结果、Topic、版本化数据或高吞吐 | 由原生 Mod 实现的生成式 IMC 接口 |
| BML+ 内建的运行时、玩法、事件、UI 或速通服务 | 对应语言已有的 BML+ 类型化 API |
| CKAngelScript runtime script 或 Component 之间通信 | 在执行模型合适时使用 CKAngelScript Message 或 Async |
不要自定义 JSON 消息格式,也不要手写字段编号。编写 .imc 接口,由
bml_target_imc_api 生成 C++ 绑定,并让 schema lock 与接口一起维护。参见
跨 Mod 通信和创建类型化 IMC API。
脚本 Mod 可以使用 BML+ 类型化脚本 API 和 DataShare。自定义 IMC Provider 仍由 原生 Mod 提供。如果脚本必须调用原生服务,应由该原生插件提供小型、类型化的 CKAngelScript 扩展。
性能与所有权¶
BML 回调和同步 IMC handler 通常在游戏线程执行。稳定的查询结果应缓存为 id 或可 重新验证的引用;准备工作移出热回调;Topic 使用有界容量并监控丢弃数。
需要修改引擎内部、高频执行、控制原生内存或提供高吞吐服务时使用原生 Mod。策略、 配置和低频控制调用可以留在脚本层,使开发和调试更直接。
发布¶
- 原生 Mod 通常发布为
.bmodp,并同时导出BMLEntry(IBML*)和BMLExit(IMod*)。 - 脚本 Mod 发布为单个
*.mod.as文件,或包含且只包含一个入口的 zip;.bmodp只属于原生 Mod。 - 测试实际发布的产物,而不只是开发目录或
RelWithDebInfo构建。 - Mod README 应写明依赖、支持版本、安装、配置和有效的问题反馈方式。