diablo2-web/plugins/README.md

100 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# `plugins/` — 可拔插客户端扩展插件架构
`plugins/` 目录承载 **Diablo II: Lord of Destruction (v1.13c) Web Port** 的所有非侵入式客户端辅助扩展插件。所有插件均与核心客户端(`src/client`)严格解耦,仅通过只读插件宿主契约([`src/client/plugin-host/api.ts`](../src/client/plugin-host/api.ts))订阅 `ServerEvent` 与会话状态快照,支持用户在顶部工具栏 **🧩 Plugins(插件)** 弹窗中动态启用/禁用插件、控制工具栏快捷入口显隐,或打开插件独立配套页面。
---
## 1. 架构边界与工程约束
架构边界测试([`tests/arch/boundaries.test.ts`](../tests/arch/boundaries.test.ts))强制执行以下四条核心隔离规则:
1. **Rule 7 — 核心客户端零插件耦合**:
- 除客户端组合根 [`src/client/main.ts`](../src/client/main.ts) 导入静态插件注册表 [`plugins/index.ts`](./index.ts) 外,**`src/client/**` 下的任何文件严禁导入 `plugins/**`**。
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. 目录结构
```text
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` 数组中接入宿主:
```ts
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/`](./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 掉落参考。 |