Go to file
troytt 40a3a12e67 Merge branch 'fix/lobby-create-game' 2026-10-01 13:46:12 +00:00
.gitea/workflows test: 引入 Vitest 测试框架、覆盖率统计与 CI 流水线 (fixes #14) 2026-09-14 12:25:28 +00:00
docs feat(drop): embed MonStats and SuperUniques with 1.13c multi-diff TC parity (Fixes #410, #411) 2026-09-25 13:44:35 +00:00
public fix(ui,fonts): hybrid DC6+CJK rendering, middle-dot atlas mask, TTF contour refinement, and recess centering (Issue #504 R2 review) 2026-09-28 12:53:32 +00:00
samples/fixtures feat(skills): authentic 1.13c Cast Overlay system and elemental spell animations (Fixes #386) 2026-09-23 03:53:57 +00:00
src Merge branch 'fix/lobby-create-game' 2026-10-01 13:46:12 +00:00
tests Merge branch 'fix/lobby-create-game' 2026-10-01 13:46:12 +00:00
tools docs: add dedicated module READMEs and rewrite root README with single-client guide 2026-10-01 13:31:15 +00:00
.gitignore feat(client,baker): implement incremental SHA-256 Service Worker asset caching 2026-10-01 01:09:17 +00:00
AGENTS.md fix(drlg,pack): pack XX minion art, fix L123 var3 waypoint, and pin exact DRLG tile variants (#500, #501, #502) 2026-09-28 09:23:12 +00:00
PROJECT.md refactor: split into isolated common/netproto/server/client/baker modules 2026-10-01 00:10:18 +00:00
README.md docs: add dedicated module READMEs and rewrite root README with single-client guide 2026-10-01 13:31:15 +00:00
ROADMAP.md chore: 移除 index.html、net.html、walk.html 历史原型入口并优化根路径路由 2026-09-18 12:04:07 +00:00
TEST_INFRA.md refactor: split into isolated common/netproto/server/client/baker modules 2026-10-01 00:10:18 +00:00
TEST_READY.md refactor: split into isolated common/netproto/server/client/baker modules 2026-10-01 00:10:18 +00:00
THIRD_PARTY_NOTICES.md 初始提交:从零实现的暗黑破坏神 II WebGL2 引擎(TypeScript + Vite,零运行时依赖) 2026-09-13 18:10:28 +08:00
package-lock.json chore(scripts): 使用 tsx 运行 TypeScript 脚本,解除对特定 Node 运行时的依赖 (fixes #12) 2026-09-14 11:44:43 +00:00
package.json chore(tools): drop acts.html-only single-player browser verifiers 2026-10-01 06:04:04 +00:00
play.html Merge branch 'feat/asset-type-display': display semantic asset resource type instead of filename during boot 2026-10-01 13:29:48 +00:00
tsconfig.baker.json refactor: split into isolated common/netproto/server/client/baker modules 2026-10-01 00:10:18 +00:00
tsconfig.client.json refactor: split into isolated common/netproto/server/client/baker modules 2026-10-01 00:10:18 +00:00
tsconfig.common.json refactor: split into isolated common/netproto/server/client/baker modules 2026-10-01 00:10:18 +00:00
tsconfig.json refactor: split into isolated common/netproto/server/client/baker modules 2026-10-01 00:10:18 +00:00
tsconfig.netproto.json refactor: split into isolated common/netproto/server/client/baker modules 2026-10-01 00:10:18 +00:00
tsconfig.server.json refactor: split into isolated common/netproto/server/client/baker modules 2026-10-01 00:10:18 +00:00
tsconfig.sw.json feat(client,baker): implement incremental SHA-256 Service Worker asset caching 2026-10-01 01:09:17 +00:00
vite.config.ts feat(client): render boot resource download progress bar with percentage and current filename 2026-10-01 01:19:27 +00:00
vitest.config.ts refactor: split into isolated common/netproto/server/client/baker modules 2026-10-01 00:10:18 +00:00

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.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 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 已内置 /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:生产环境构建与静态部署

# 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 <角色名>