diablo2-web/plugins
troytt ff76655d4d feat(plugins): implement top-level plugin architecture, toolbar plugin modal menu, and kill-stats plugin migration 2026-10-03 08:13:18 +00:00
..
kill-stats feat(plugins): implement top-level plugin architecture, toolbar plugin modal menu, and kill-stats plugin migration 2026-10-03 08:13:18 +00:00
README.md feat(plugins): implement top-level plugin architecture, toolbar plugin modal menu, and kill-stats plugin migration 2026-10-03 08:13:18 +00:00
index.ts feat(plugins): implement top-level plugin architecture, toolbar plugin modal menu, and kill-stats plugin migration 2026-10-03 08:13:18 +00:00

README.md

plugins/ — 可拔插客户端扩展插件架构

plugins/ 目录承载 Diablo II: Lord of Destruction (v1.13c) Web Port 的所有非侵入式客户端辅助扩展插件。所有插件均与核心客户端(src/client)严格解耦,仅通过只读插件宿主契约(src/client/plugin-host/api.ts)订阅 ServerEvent 与会话状态快照,支持用户在顶部工具栏 🧩 Plugins(插件) 弹窗中动态启用/禁用插件、控制工具栏快捷入口显隐,或打开插件独立配套页面。


1. 架构边界与工程约束

架构边界测试(tests/arch/boundaries.test.ts)强制执行以下四条核心隔离规则:

  1. Rule 7 — 核心客户端零插件耦合:
  2. Rule 8 — 服务端与烘焙管线零插件耦合:
    • src/common/**、src/netproto/**、src/server/** 与 src/baker/** 严禁导入 plugins/**。
  3. Rule 9 — 插件仅限访问公共只读宿主契约:
    • plugins/** 仅允许导入:
      • src/common/**(只读数据表 D2DataRegistry、二进制解析器等)
      • src/netproto/**(只读协议事件与常量定义)
      • src/client/plugin-host/api.ts(唯一允许导入的 src/client 模块)
      • 同一插件目录 plugins/<self>/** 内部模块
    • 严禁直接导入 src/client/world/**、src/client/session/**、src/client/ui/**、src/client/render/** 等客户端内部实现。
  4. Rule 10 — 插件间严格物理隔离:
    • 每个插件必须自包含在 plugins/<plugin-id>/ 子目录中,严禁跨插件导入(plugins/<a>/** 不得导入 plugins/<b>/**)。
    • 所有插件均运行在纯浏览器环境(tsconfig.client.json),严禁使用任何 Node.js 内置模块。

2. 目录结构

plugins/
├── README.md                  # 插件系统架构说明与开发指南
├── index.ts                   # BUILTIN_PLUGINS 静态注册表(唯一向 src/client/main.ts 暴露的入口)
└── kill-stats/                # [Built-in #714] 怪物击杀统计插件 (Kill Statistics)
    ├── index.ts               # killStatsPlugin 描述符、生命周期挂载与工具栏动作
    ├── index.html             # 独立统计看板页面入口 (/plugins/kill-stats/index.html)
    ├── page-main.ts           # 独立页面启动引导 (基于 bootPluginPage 统一处理语言与启停状态)
    ├── stats-page.ts          # 独立统计页控制器 (IndexedDB 实时监听、BroadcastChannel 同步、导入/导出)
    ├── kill-tracker.ts        # 实时击杀追踪器 (监听 unitDeath S2C 事件并批量落盘 IndexedDB)
    ├── kill-stats-model.ts    # G-counter CRDT 统计模型与 JSON 序列化/合并校验
    ├── kill-stats-store.ts    # IndexedDB (d2_kill_stats_v1) 存储实现
    ├── kill-stats-view.ts     # 独立统计页双语 DOM 渲染器
    ├── monster-kill-classes.ts# 基于 MonStats.txt (hcIdx / killable / align) 的可击杀怪物分类器
    └── stats-i18n.ts          # 插件专属中英双语词典

3. 插件接口契约(src/client/plugin-host/api.ts)

每个插件通过实现 D2WebPlugin<TInstance> 接口并注册到 plugins/index.ts 的 BUILTIN_PLUGINS 数组中接入宿主:

export interface D2WebPlugin<TInstance = unknown> {
  readonly descriptor: PluginDescriptor;
  create(ctx: PluginHostContext): PluginLifecycle<TInstance>;
}
  • PluginDescriptor:
    • id: 全局唯一 kebab-case 插件标识(如 'kill-stats'),必须与目录名 plugins/<id>/ 一致。
    • version: 语义化版本号(如 '1.0.0')。
    • name / description: { zh: string; en: string } 双语名称与简介(在工具栏 Plugins 模态框中展示)。
    • defaultEnabled: 默认是否启用。
    • capabilities: 声明插件能力标签('event-subscriber' | 'standalone-page' | 'toolbar-shortcut' | 'storage-indexeddb')。
    • standalonePagePath?: 若包含独立页面,声明相对路径 'plugins/<id>/index.html'(Vite 构建会自动扫描 plugins/*/index.html 并打包为多页入口 plugin-<id>)。
    • toolbarAction?: 工具栏快捷按钮与插件配置弹窗中的“打开页面”动作元数据。
  • PluginHostContext:
    • getLang() / setLang(lang): 读取或切换全局双语设置('zh' | 'en')。
    • getDataRegistry(): 获取已加载的 D2DataRegistry(若尚未加载完成则返回 null)。
    • getSessionView(): 获取只读会话视图 PluginSessionView(当前战网网关、账号名、角色名、角色职业/等级与存活怪物快照 getMonsterSnapshot(unitId))。
    • onServerEvent(listener): 订阅 D2GS ServerEvent 流。宿主会自动为每个事件调用包裹 try/catch 故障隔离:单个插件抛错不会中断游戏主循环或其他插件。
  • bootPluginPage(options):
    • 独立插件页面(plugins/<id>/page-main.ts)专用的标准引导辅助函数。自动读取 PluginConfigStore 的启用状态与全局语言,并监听跨标签页 window.addEventListener('storage', ...) 事件,实时触发 onEnabledChange 与 onLangChange。

4. 如何开发与接入新插件

  1. 创建插件目录:在 plugins/<plugin-id>/ 下创建 index.ts(若需要独立 HTML 页面,创建 plugins/<plugin-id>/index.html 与 page-main.ts)。
  2. 遵循只读与快速失败原则(Fail-Fast):
    • 严禁伪造或硬编码游戏规则数据;凡涉及怪物、物品、技能或关卡元数据,必须从 ctx.getDataRegistry() 的 1.13c 权威数据表中读取,缺失必需参数时立即抛出明确错误。
  3. 注册到 plugins/index.ts:
    • 将插件实例追加到 BUILTIN_PLUGINS 只读数组中。
    • vite.config.ts 会自动扫描 plugins/*/index.html,无需手工修改 Rollup input 列表。
  4. 编写单元测试与边界校验:
    • 在 tests/plugins/<plugin-id>/ 下编写单元测试。
    • 运行 npm run typecheck 与 npx vitest run 验证类型安全及 tests/arch/boundaries.test.ts Rules 1–10 零违规。

5. 内置与规划插件路线图(Gitea Issues)

插件目录 / 标识 状态 Gitea Issue 功能简述
plugins/kill-stats/ ✅ 已内置 #714 怪物击杀统计 (Monster Kill Statistics):按战网网关/账号/角色隔离追踪全部可击杀怪物(普通/精英/暗金/首领),基于 IndexedDB G-counter CRDT 支持多标签页实时同步与 JSON 导入合并。
plugins/loot-filter/ 📋 规划中 #715 掉落物品过滤与高价值掉落提醒 (Loot Filter & Drop Notifier):订阅地面物品掉落事件,支持自定义过滤规则、底材孔数/无形高亮与高号符文/暗金掉落提示音。
plugins/runeword-calc/ 📋 规划中 #716 符文之语与赫拉迪克方块配方查询器 (Runeword & Cube Recipe Calculator):基于 Runes.txt 与 CubeMain.txt 结合当前角色背包/储物箱符文快照,实时计算可合成的符文之语与升级打孔配方。
plugins/area-guide/ 📋 规划中 #717 85 级场景 (TC85) 与怪物抗性/免疫速查面板 (Area Level & Monster Immunity Guide):根据当前角色所在关卡 areaId 与难度,实时展示来自 Levels.txt / MonStats.txt 的场景等级、四系免疫分布与 Boss 掉落参考。