设计插件

设计生命周期安全、仅依赖 SDK 的 Turboism 插件,并在能力不可用时安全地失败关闭。

一个持久可靠的 Turboism 插件具有一个用户可见的用途,仅依赖 SDK,并将主机集成、调度、安全与清理工作交由 Runtime 负责。

保持边界精简

Plugin -> SDK -> Runtime policy -> versioned Adapter/Provider -> Cubism/Editor

不要为 Runtime 相关事项添加插件本地抽象。请复用 PluginContext 服务来处理配置、存储、任务、事件、UI、用户文件和 Cubism 访问。

避免:

  • 直接依赖 Runtime 或 com.live2d.*
  • 反射和原始主机对象;
  • 不受管理的 executor、线程和定时器;
  • 遍历 Swing/AWT 主机小部件;
  • 全局可变注册表;
  • 推测性的工厂和扩展层。

规划生命周期

使用实际存在的四个生命周期方法:

  1. init — 保留 context、注册 schema,并构建插件私有状态。
  2. enable — 注册操作、事件、UI 和任务。
  3. disable — 停止活动行为,但不假定会热卸载。
  4. shutdown — 在 Runtime 生命周期稳定后释放插件私有状态。

除非有意提前关闭,否则将每个可关闭的注册项或句柄放入 DisposableScope

将权限与可用性分开

权限回答插件是否可以跨越风险边界。能力回答 Runtime 和当前主机版本是否可以执行该操作。

请同时设计两条路径:

  • 通过受支持的 Provider 成功执行;
  • 明确的不可用、不受支持、过期、拒绝、超时、背压或拒绝执行结果。

绝不要将权限视为 Provider 存在的证明。

围绕 Editor 所有权设计写入

对于附加到 Editor 的模型,Editor 创作状态是写入的唯一事实来源。一次安全写入可能需要:

  • 验证;
  • 主机线程调度;
  • 事务与 Undo;
  • 脏状态和刷新处理;
  • 回滚或明确的部分失败;
  • generation/过期目标拒绝;
  • 若声称数据持久化,则进行保存/重新打开验证。

不要把 Cubism Core 变异为第二个独立的创作状态,也不要围绕只用于求值的变异伪造 Undo 条目。

在边界处使用不可变值

事件、UI 描述符、快照、选择、配置值和任务请求都应是不可变的 SDK 或插件自有值。

不要泄漏:

  • 主机对象;
  • 主机 UI 控件;
  • 原生句柄;
  • 主机 ClassLoader;
  • 可变的 Cubism 自有数组;
  • 不受限制的文件系统路径。

一个操作对应一个贡献项

将用户行为注册为 Action,然后把菜单、工具栏、面板或上下文菜单贡献项绑定到其操作 ID。这样无需主机 UI 也可以测试行为。

测试最小的真实契约

仅有生命周期日志的插件需要构造函数/生命周期测试。带注册项的插件应使用记录型/伪造的 PluginContext,并验证:

  • 请求的权限和不可用路径;
  • 注册与 scope 清理;
  • disable/shutdown 行为;
  • 不可变事件和 DTO 行为;
  • 配置迁移和修订冲突;
  • 在适用时任务取消或拒绝。

主机版本声明需要项目中风险更高的验证工作流;伪造测试不能证明已为真实主机做好准备。

设计检查清单

  • 插件有一个明确用途。
  • 生产代码仅依赖 SDK。
  • Manifest v2 声明准确的入口点、资源、依赖和最低权限。
  • 注册项和句柄可关闭且受 scope 管理。
  • 不可用的 Provider 以有用诊断信息安全地失败关闭。
  • 配置、存储和用户文件使用正确的服务。
  • 不存在不受管理的线程、executor、定时器、主机对象或反射桥接。
  • 写入保持 Editor 所有权和 Undo 语义。
  • 最小的聚焦测试覆盖实际契约。