|
|
||
|---|---|---|
| .gitea/workflows | ||
| docs | ||
| public | ||
| samples/fixtures | ||
| src | ||
| tests | ||
| tools | ||
| .gitignore | ||
| AGENTS.md | ||
| PROJECT.md | ||
| README.md | ||
| ROADMAP.md | ||
| TEST_INFRA.md | ||
| TEST_READY.md | ||
| THIRD_PARTY_NOTICES.md | ||
| package-lock.json | ||
| package.json | ||
| play.html | ||
| tsconfig.baker.json | ||
| tsconfig.client.json | ||
| tsconfig.common.json | ||
| tsconfig.json | ||
| tsconfig.netproto.json | ||
| tsconfig.server.json | ||
| tsconfig.sw.json | ||
| vite.config.ts | ||
| vitest.config.ts | ||
README.md
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.dll64 位进位乘法随机数 (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 |
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/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/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/server |
D2Game.dll |
纯确定性模拟 (ES2022 + WebWorker),禁 DOM / 禁 Node / 禁非确定性随机与时钟 |
25fps (40ms/tick) 权威游戏循环、1.13c 战斗伤害与抗性管线、7 职业 221 个技能执行器、投射物与光环引擎、怪物 AI 与房间流式激活、TC 掉落管线与 .d2s 存档读写 |
src/server/README.md |
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 |
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 |
模块依赖拓扑图
┌──────────────────┐ ┌────────────────────┐
│ 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),所有战网账号认证、角色存档、房间管理与游戏权威战斗逻辑均由远端原版 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 端口:
浏览器 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:
sudo apt-get update && sudo apt-get install -y websockify nginx
2) 配置 systemd 守护服务管理 3 个桥接端口
创建环境变量配置目录 /etc/d2ws 及三个端点的配置文件(若 D2GS 运行在另一台内网 Windows 机器上,将 TARGET 中的 127.0.0.1 替换为对应的内网 IP 即可):
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 并启动服务:
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/ 路由规则:
# 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 配置并验证服务状态:
sudo nginx -t && sudo systemctl reload nginx
systemctl is-active d2ws@bnet d2ws@realm d2ws@game
提示:客户端的
createWsBridgeResolver(位于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) 安装项目依赖
npm install
2) 放置原版 1.13c MPQ 文件
将原版 Diablo II: Lord of Destruction v1.13c 的 MPQ 文件放入(或软链接至)samples/d2/ 目录:
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) 执行全量烘焙与契约校验
# 一键烘焙全部离线资源到 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)
npm run dev
- 启动后浏览器访问
http://127.0.0.1:5173/play.html(访问根路径/也会自动重写到/play.html)。 - 默认代理配置:
vite.config.ts已内置/d2netWebSocket 代理(默认转发至远端测试服wss://www.laiseek.xyz/d2net/*),因此本地启动后可直接登录进入游戏。 - 自定义切换 D2GS 服务器地址:
- URL 参数方式:访问
http://127.0.0.1:5173/play.html?wsBridge=wss://your-d2gs-domain.com/d2net(或本地桥ws://127.0.0.1:8080/d2net)。 - UI 设置面板方式:点击画面顶部 32px 工具栏右侧的 ⚙ Settings 按钮,修改 WS Bridge URL 并保存刷新。
- URL 参数方式:访问
方式 B:生产环境构建与静态部署
# 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 直接测试 TCP 桥与 D2GS 建房/进图流程:
npm run bot -- --bridge wss://your-d2gs-domain.com/d2net --user <账号> --pass <密码> --char <角色名>