|
|
||
|---|---|---|
| .. | ||
| kill-stats | ||
| README.md | ||
| index.ts | ||
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)强制执行以下四条核心隔离规则:
- Rule 7 — 核心客户端零插件耦合:
- 除客户端组合根
src/client/main.ts导入静态插件注册表plugins/index.ts外,src/client/**下的任何文件严禁导入plugins/**。
- 除客户端组合根
- Rule 8 — 服务端与烘焙管线零插件耦合:
src/common/**、src/netproto/**、src/server/**与src/baker/**严禁导入plugins/**。
- 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/**等客户端内部实现。
- 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): 订阅 D2GSServerEvent流。宿主会自动为每个事件调用包裹try/catch故障隔离:单个插件抛错不会中断游戏主循环或其他插件。
bootPluginPage(options):- 独立插件页面(
plugins/<id>/page-main.ts)专用的标准引导辅助函数。自动读取PluginConfigStore的启用状态与全局语言,并监听跨标签页window.addEventListener('storage', ...)事件,实时触发onEnabledChange与onLangChange。
- 独立插件页面(
4. 如何开发与接入新插件
- 创建插件目录:在
plugins/<plugin-id>/下创建index.ts(若需要独立 HTML 页面,创建plugins/<plugin-id>/index.html与page-main.ts)。 - 遵循只读与快速失败原则(Fail-Fast):
- 严禁伪造或硬编码游戏规则数据;凡涉及怪物、物品、技能或关卡元数据,必须从
ctx.getDataRegistry()的 1.13c 权威数据表中读取,缺失必需参数时立即抛出明确错误。
- 严禁伪造或硬编码游戏规则数据;凡涉及怪物、物品、技能或关卡元数据,必须从
- 注册到
plugins/index.ts:- 将插件实例追加到
BUILTIN_PLUGINS只读数组中。 vite.config.ts会自动扫描plugins/*/index.html,无需手工修改 Rollupinput列表。
- 将插件实例追加到
- 编写单元测试与边界校验:
- 在
tests/plugins/<plugin-id>/下编写单元测试。 - 运行
npm run typecheck与npx vitest run验证类型安全及tests/arch/boundaries.test.tsRules 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 掉落参考。 |