跳转至

跨 Mod 通信

IMC 用于一个 Mod 发布给其他 Mod 的接口,不承载任何 Loader 能力。脚本 Mod 直接使用 Loader 提供的类型化能力接口;IMC Provider 只能由原生 Mod 实现。

脚本 Mod 如何选择

脚本中读取 BML 自带能力时,直接使用有类型的接口:

BML::Runtime::State runtime = BML::Runtime::GetState();
if (runtime.InLevel) {
  // 使用 runtime 的复制快照。
}

array<BML::Gameplay::Checkpoint>@ checkpoints;
if (BML::Gameplay::ReadCheckpoints(checkpoints) == BML::ERROR_OK) {
  for (uint i = 0; i < checkpoints.length(); ++i) {
    CKObject@ checkpoint = checkpoints[i].BorrowObject();
  }
}

可用的内置命名空间包括 BML::RuntimeBML::GameplayBML::UIBML::Speedrun。Virtools 场景查找和对象标识应使用 CKAngelScript 的 Scene 命名空间及其可重新验证的引用类型。其中 Runtime 状态、时钟和分数读取直接返回 Loader 进程内状态的值,不经过任何传输层, 也不要求脚本处理传输状态码。在有效脚本回调之外调用这些函数会触发脚本 异常。Gameplay 读取也直接复用进程内的数据读取器,但对应的 Ballance 数据数组可能尚不可用或布局不受支持,因此仍返回明确的状态码。脚本只处理 类型化数据,不直接管理原始消息或原生 IMC 句柄。ReadCatalogReadCheckpointsReadResetpoints 返回标准 AngelScript 数组形式的完整 快照;读取失败时输出句柄为 null,不需要游标、Next 循环或显式 Close。 每次调用都会重新读取源数据并创建快照,因此数据稳定时应复用已返回的数组, 不要每帧重复构建。

Loader 事件通过 OnGameEvent 同步回调到达。需要跨回调保留信息时,只复制 Mod 后续真正需要的状态;脚本侧没有需要打开或轮询的事件队列。

两个脚本 Mod 只需交换少量状态时,使用 DataShare。DataShare 适合有明确 类型和所有权的一次性或延迟读取,不应被包装成通用函数调用机制。

何时需要原生 IMC Provider

IMC 只用于一个 Mod 发布给其他 Mod 的接口。这类服务满足下面任一条件时,应把它 实现为原生 Mod:

  • 需要请求/响应 RPC;
  • 需要高频或有背压策略的事件流;
  • 需要显式选择 caller thread 或 game thread;
  • 需要稳定的跨 DLL ABI,供多个独立 Mod 使用。

原生实现流程是:编写版本化 .imc 接口定义,用 imc_codegen.pybml_target_imc_api() 生成 C++ 绑定,实现生成的 provider,并让消费者 使用生成的 client。不要手写字段编码,也不要跨 DLL 传递 C++ 对象、STL 容器、allocator 所有权或 CKObject*

完整示例与兼容演进规则见: