249 lines
14 KiB
Markdown
249 lines
14 KiB
Markdown
# Diablo II: Lord of Destruction (v1.13c) — Web Port
|
||
|
||
## 1. 项目概述
|
||
|
||
本项目是 **《暗黑破坏神 II:毁灭之王》(Diablo II: Lord of Destruction v1.13c)** 的高精度 Web 移植实现。项目严格遵循原版 1.13c 反编译二进制汇编(`D2Common.dll`、`D2Client.dll`、`D2Game.dll`、`D2Net.dll`、`Bnclient.dll`、`D2MCPClient.dll`、`Fog.dll`)与官方 MPQ 数据表(`Levels.txt`、`LvlTypes.txt`、`SuperUniques.txt`、`Skills.txt`、`TreasureClassEx.txt` 等)作为唯一事实基准(Ground Truth):
|
||
|
||
- **1:1 原生网络协议兼容**:完整实现 BNCS(战网登录/聊天,端口 `6112`)、MCP(Realm 角色管理/建房加房,端口 `6113`)与 D2GS(Huffman 压缩帧、167 个 S2C 操作码、C2S 指令及物品比特流,端口 `4000`),浏览器客户端可通过轻量 TCP-to-WebSocket 桥直接连入原版 C++ **PvPGN + D2GS (1.13c)** 战网服务器。
|
||
- **种子驱动的确定性 DRLG 地图重建**:1:1 复刻 `D2Common.dll` 64 位进位乘法随机数 (`0x6FDAA9E0`) 与三大 DRLG 拓扑算法(Preset 预设、Maze 地牢迷宫、Outdoors 野外生成),客户端仅凭 D2GS 下发的 32 位 `mapSeed` 即可在浏览器端还原与 C++ 服务端逐格一致的瓦片地图与碰撞网格。
|
||
- **WebGL2 高性能等距渲染与离线资产管线**:通过 Node.js 离线烘焙管线 (`src/baker`) 将原版 MPQ 中的 DT1/DS1 瓦片、DC6/DCC 角色与怪物动画、COF 复合图层、PL2 调色板与 TBL 字符串表预编译为静态图集与二进制包,配合 Service Worker 增量缓存与 WebGL2 合批渲染实现 60fps 流畅体验。
|
||
- **严格的模块化架构隔离**:全仓按原版 DLL 职责划分为五大核心代码模块与独立工具集,由多套独立 `tsconfig.*.json` 与架构边界测试(`tests/arch/boundaries.test.ts`)强制保障零跨层污染。
|
||
|
||
---
|
||
|
||
## 2. 模块概述与 README 链接
|
||
|
||
项目源码按职责边界严格拆分为以下模块,点击各模块的 `README.md` 可查看详细的子目录结构、核心算法与验证命令:
|
||
|
||
| 模块路径 | 对标原版组件 | 运行环境与边界约束 | 核心职责简述 | 详细文档 |
|
||
| :--- | :--- | :--- | :--- | :--- |
|
||
| [`src/common`](./src/common) | `D2Common.dll` `Storm.dll` `D2Lang.dll` | 纯 TypeScript (`ES2022`),零 DOM / 零 Node.js 依赖 | MPQ 与原生二进制格式解析器 (DC6/DCC/DT1/DS1/COF/PL2/TBL)、`D2DataRegistry` 数据表中心、1.13c DRLG 随机地图生成算法、64 位 `D2Rng` 与烘焙校验契约 (`pack-contract`) | [src/common/README.md](./src/common/README.md) |
|
||
| [`src/netproto`](./src/netproto) | `D2Net.dll` `Bnclient.dll` `D2MCPClient.dll` `Fog.dll` | 纯协议栈模块,零外部模块依赖,唯一入口 `src/netproto/index.ts` | BNCS (`6112`) / MCP (`6113`) / D2GS (`4000`) 二进制协议编解码、XSHA-1 与 CheckRevision 认证、Fog.dll Huffman 解压、167 个 S2C 操作码解析、物品比特流与 WebSocket/TCP 传输层抽象 | [src/netproto/README.md](./src/netproto/README.md) |
|
||
| [`src/client`](./src/client) | `D2Client.dll` `D2Gfx.dll` `D2Win.dll` `D2Launch.dll` | 纯浏览器客户端 (`DOM` + `WebGL2`),零 Node / 零 `server` / 零 `baker` 依赖 | 唯一页面入口 `play.html`、战网登录/选人/大厅前端界面、`OnlineSession` 与 `ClientWorld` 状态镜像、种子驱动的实时 DRLG 地图重建、移动预测与内插、WebGL2 渲染器、全套经典 HUD 面板与抓包检查器 | [src/client/README.md](./src/client/README.md) |
|
||
| [`src/server`](./src/server) | `D2Game.dll` | 纯确定性模拟 (`ES2022` + `WebWorker`),禁 DOM / 禁 Node / 禁非确定性随机与时钟 | 25fps (40ms/tick) 权威游戏循环、1.13c 战斗伤害与抗性管线、7 职业 221 个技能执行器、投射物与光环引擎、怪物 AI 与房间流式激活、TC 掉落管线与 `.d2s` 存档读写 | [src/server/README.md](./src/server/README.md) |
|
||
| [`src/baker`](./src/baker) | 离线资源构建管线 | 纯 Node.js CLI 环境 (`types: ["node"]`),禁 DOM / 禁 `client` / 禁 `server` | 从原版 1.13c MPQ 归档中提取并烘焙瓦片图集 (`tiles`)、角色/怪物/NPC 实体 (`entities`)、投射物 (`missiles`)、覆盖层 (`overlays`)、HUD/前端 UI、动画表 (`animdata`) 与 SHA-256 资产清单 (`asset-manifest.json`) | [src/baker/README.md](./src/baker/README.md) |
|
||
| [`tools/`](./tools) | 开发者与 QA 工具集 | Node.js / Playwright CLI | 无头协议测试机器人 (`d2-bot.ts`)、`.d2cap` 抓包离线重放器 (`netproto-replay.ts`)、`.d2s` 存档全解锁工具 (`d2s-unlock.ts`)、全谱系 136 关卡浏览器审计 (`audit-levels-browser.ts`) 与 DRLG 差分对比工具 | [tools/README.md](./tools/README.md) |
|
||
|
||
### 模块依赖拓扑图
|
||
|
||
```text
|
||
┌──────────────────┐ ┌────────────────────┐
|
||
│ src/common │ │ src/netproto │
|
||
│ (Pure TS Base) │ │ (1.13c Wire Stack) │
|
||
└────────┬─────────┘ └─────────┬──────────┘
|
||
│ │
|
||
┌─────────┼──────────────────┐ │
|
||
▼ ▼ ▼ ▼
|
||
┌─────────────┐ ┌──────────────┐ ┌─────────────────────────┐
|
||
│ src/baker │ │ src/server │◄──┤ src/client │
|
||
│ (Node CLI) │ │ (D2Game Sim) │ │ (Browser WebGL2 Client) │
|
||
└─────────────┘ └──────────────┘ └─────────────────────────┘
|
||
*(注:src/client 与 src/server 之间完全解耦,仅各自依赖 src/common 与 src/netproto)*
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 单客户端模式使用方法(Single-Client Mode Guide)
|
||
|
||
在**单客户端模式**下,Web 前端作为纯粹的 1.13c 在线客户端运行([`play.html`](./play.html)),所有战网账号认证、角色存档、房间管理与游戏权威战斗逻辑均由远端原版 **PvPGN (`bnetd` + `d2cs` + `d2dbs`) + D2GS (v1.13c)** 服务器承载。
|
||
|
||
完整跑通单客户端模式包含以下三个核心步骤:
|
||
|
||
---
|
||
|
||
### 3.1 第一步:在 D2GS / PvPGN 机器上设置 TCP-to-WebSocket 桥
|
||
|
||
由于浏览器只支持 WebSocket (`ws://` / `wss://`) 而不支持直接建立原始 TCP Socket,需要在运行 PvPGN 与 D2GS 的服务器(或同内网网关机)上部署 **`websockify` + Nginx** 反向代理,将浏览器的三路 WebSocket 子路径桥接到对应的原生 TCP 端口:
|
||
|
||
```text
|
||
浏览器 Web 客户端 (src/netproto WsStream)
|
||
│
|
||
├─ wss://<host>/d2net/bnet ──► Nginx :443 ──► websockify 127.0.0.1:7001 ──► PvPGN bnetd (TCP :6112)
|
||
├─ wss://<host>/d2net/realm ──► Nginx :443 ──► websockify 127.0.0.1:7002 ──► PvPGN d2cs (TCP :6113)
|
||
└─ wss://<host>/d2net/game ──► Nginx :443 ──► websockify 127.0.0.1:7003 ──► D2GS 1.13c (TCP :4000)
|
||
```
|
||
|
||
#### 1) 安装 `websockify`
|
||
在 Linux 网关/宿主机(例如 Ubuntu/Debian)上安装 `websockify`:
|
||
```bash
|
||
sudo apt-get update && sudo apt-get install -y websockify nginx
|
||
```
|
||
|
||
#### 2) 配置 `systemd` 守护服务管理 3 个桥接端口
|
||
创建环境变量配置目录 `/etc/d2ws` 及三个端点的配置文件(若 D2GS 运行在另一台内网 Windows 机器上,将 `TARGET` 中的 `127.0.0.1` 替换为对应的内网 IP 即可):
|
||
|
||
```bash
|
||
sudo mkdir -p /etc/d2ws
|
||
|
||
# 1. BNCS (bnetd, TCP 6112)
|
||
sudo tee /etc/d2ws/bnet.env >/dev/null <<'EOF'
|
||
LISTEN=127.0.0.1:7001
|
||
TARGET=127.0.0.1:6112
|
||
EOF
|
||
|
||
# 2. MCP Realm (d2cs, TCP 6113)
|
||
sudo tee /etc/d2ws/realm.env >/dev/null <<'EOF'
|
||
LISTEN=127.0.0.1:7002
|
||
TARGET=127.0.0.1:6113
|
||
EOF
|
||
|
||
# 3. D2GS Game Server (D2GS.exe 1.13c, TCP 4000)
|
||
sudo tee /etc/d2ws/game.env >/dev/null <<'EOF'
|
||
LISTEN=127.0.0.1:7003
|
||
TARGET=127.0.0.1:4000
|
||
EOF
|
||
```
|
||
|
||
创建模板单元文件 `/etc/systemd/system/d2ws@.service` 并启动服务:
|
||
|
||
```bash
|
||
sudo tee /etc/systemd/system/d2ws@.service >/dev/null <<'EOF'
|
||
[Unit]
|
||
Description=Diablo II WebSocket-to-TCP Bridge (%i)
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
EnvironmentFile=/etc/d2ws/%i.env
|
||
ExecStart=/usr/bin/websockify --heartbeat=30 ${LISTEN} ${TARGET}
|
||
Restart=always
|
||
RestartSec=2
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable --now d2ws@bnet d2ws@realm d2ws@game
|
||
```
|
||
|
||
#### 3) 配置 Nginx `/d2net/*` WebSocket 反向代理
|
||
在 Nginx 站点配置(如 `/etc/nginx/sites-enabled/default`)的 `server` 块中加入 `/d2net/` 路由规则:
|
||
|
||
```nginx
|
||
# BNCS (端口 6112)
|
||
location = /d2net/bnet {
|
||
proxy_pass http://127.0.0.1:7001;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_read_timeout 3600s;
|
||
proxy_send_timeout 3600s;
|
||
proxy_buffering off;
|
||
}
|
||
|
||
# MCP Realm (端口 6113)
|
||
location = /d2net/realm {
|
||
proxy_pass http://127.0.0.1:7002;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_read_timeout 3600s;
|
||
proxy_send_timeout 3600s;
|
||
proxy_buffering off;
|
||
}
|
||
|
||
# D2GS Game Server (端口 4000)
|
||
location = /d2net/game {
|
||
proxy_pass http://127.0.0.1:7003;
|
||
proxy_http_version 1.1;
|
||
proxy_set_header Upgrade $http_upgrade;
|
||
proxy_set_header Connection "upgrade";
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_read_timeout 3600s;
|
||
proxy_send_timeout 3600s;
|
||
proxy_buffering off;
|
||
}
|
||
```
|
||
|
||
重载 Nginx 配置并验证服务状态:
|
||
```bash
|
||
sudo nginx -t && sudo systemctl reload nginx
|
||
systemctl is-active d2ws@bnet d2ws@realm d2ws@game
|
||
```
|
||
|
||
> **提示**:客户端的 `createWsBridgeResolver`(位于 [`src/netproto/transport/endpoint.ts`](./src/netproto/transport/endpoint.ts))会自动根据协议握手阶段返回的端口(`6112`、`6113`、`4000`)将连接重写至 `<wsBridgeUrl>/bnet`、`/realm` 与 `/game`,因此无需修改 D2GS 内部下发的 IP,只需确保 PvPGN 的 `realm.conf` / `address_translation.conf` 中配置的端口保持标准 `6113` 与 `4000` 即可。
|
||
|
||
---
|
||
|
||
### 3.2 第二步:如何烘焙离线美术资源(Offline Asset Baking)
|
||
|
||
Web 客户端运行所需的瓦片地图图集、角色/怪物 DCC 精灵、HUD 面板与动画速度表均通过 `src/baker` 从原版 1.13c MPQ 离线烘焙生成到 `public/` 目录。
|
||
|
||
#### 1) 安装项目依赖
|
||
```bash
|
||
npm install
|
||
```
|
||
|
||
#### 2) 放置原版 1.13c MPQ 文件
|
||
将原版 **Diablo II: Lord of Destruction v1.13c** 的 MPQ 文件放入(或软链接至)`samples/d2/` 目录:
|
||
|
||
```bash
|
||
mkdir -p samples/d2
|
||
# 确保以下 4 个核心 MPQ 存在(音频 MPQ 如 d2sfx.mpq / d2music.mpq / d2xmusic.mpq 可选):
|
||
ls -lh samples/d2/
|
||
# d2data.mpq d2char.mpq d2exp.mpq Patch_D2.mpq
|
||
```
|
||
|
||
#### 3) 执行全量烘焙与契约校验
|
||
```bash
|
||
# 一键烘焙全部离线资源到 public/act-packs/ 并生成 public/asset-manifest.json
|
||
npm run bake -- all
|
||
|
||
# 烘焙完成后执行严格完整性校验(验证 233+3 个 DT1 瓦片、实体图集与 0 缺失文件)
|
||
npm run bake:verify
|
||
```
|
||
|
||
如果需要单独重新烘焙某一部分资源,也可以使用细粒度命令:
|
||
- `npm run pack:tiles`:仅烘焙全部 5 幕瓦片图集与 `drlg-bundle.{json,bin}`
|
||
- `npm run pack:frontend`:仅烘焙战网登录、角色选择与大厅前置界面资源
|
||
- `npm run pack:ui`:仅烘焙游戏内 HUD 面板、技能图标与鼠标指针
|
||
- `npm run pack:animdata`:仅提取 `AnimData.d2` 动画帧率表
|
||
- `npm run pack:entities`:仅烘焙怪物、SuperUniques(含随从)与场景交互物图集
|
||
- `npm run pack:missiles` / `npm run pack:overlays`:仅烘焙投射物或光环/状态覆盖层特效
|
||
- `npm run pack:manifest`:重新扫描 `public/` 并更新 `public/asset-manifest.json`(触发客户端 Service Worker 增量更新)
|
||
|
||
---
|
||
|
||
### 3.3 第三步:如何启动服务与连接游戏
|
||
|
||
#### 方式 A:本地开发模式启动(Vite Dev Server)
|
||
|
||
```bash
|
||
npm run dev
|
||
```
|
||
|
||
- 启动后浏览器访问 **`http://127.0.0.1:5173/play.html`**(访问根路径 `/` 也会自动重写到 `/play.html`)。
|
||
- **默认代理配置**:[`vite.config.ts`](./vite.config.ts) 已内置 `/d2net` WebSocket 代理(默认转发至远端测试服 `wss://www.laiseek.xyz/d2net/*`),因此本地启动后可直接登录进入游戏。
|
||
- **自定义切换 D2GS 服务器地址**:
|
||
1. **URL 参数方式**:访问 `http://127.0.0.1:5173/play.html?wsBridge=wss://your-d2gs-domain.com/d2net`(或本地桥 `ws://127.0.0.1:8080/d2net`)。
|
||
2. **UI 设置面板方式**:点击画面顶部 32px 工具栏右侧的 **⚙ Settings** 按钮,修改 **WS Bridge URL** 并保存刷新。
|
||
|
||
#### 方式 B:生产环境构建与静态部署
|
||
|
||
```bash
|
||
# 1. 全仓类型检查(同时校验 common / netproto / server / client / baker 五套 tsconfig)
|
||
npm run typecheck
|
||
|
||
# 2. 构建生产静态产物(输出至 dist/,若需部署在 /diablo2/ 子路径下可执行 npm run build:game 输出至 dist-game/)
|
||
npm run build
|
||
|
||
# 3. 本地预览生产构建包
|
||
npm run preview
|
||
```
|
||
|
||
在生产环境 Nginx 中,只需将构建生成的 `dist/` 目录挂载为静态站点根目录(或子路径 `/diablo2/`),并与 **3.1 节** 中的 `/d2net/{bnet,realm,game}` WebSocket 反向代理配置在同一个域名下,即可通过浏览器直接访问完整的在线暗黑 II 体验。
|
||
|
||
#### 方式 C:使用命令行无头 Bot 快速验证服务连通性
|
||
|
||
无需打开浏览器,可在终端使用 [`tools/d2-bot.ts`](./tools/d2-bot.ts) 直接测试 TCP 桥与 D2GS 建房/进图流程:
|
||
|
||
```bash
|
||
npm run bot -- --bridge wss://your-d2gs-domain.com/d2net --user <账号> --pass <密码> --char <角色名>
|
||
```
|