这篇是完整可复现版。从空目录开始,照着做就能把 Project Diablo 2 跑在 NAS 上,
用浏览器直接玩,不用开 Windows 电脑。
测试环境:x86_64 架构的 NAS(16G 内存),游戏本体跑在容器里,浏览器直连。
看之前先把这几个换成你自己的:
-<NAS-IP>—— 你的 NAS 内网地址(路由器后台看,或者ping一下别的设备反查)
-anhei2—— 容器名和项目目录名,下文统一用它当例子,你可以随便改
-/volume1/docker/anhei2/—— 部署路径。这是某些 NAS 的存储挂载点约定,
你的可能是/mnt/xxx或/data/xxx,按实际来
全部文件(Dockerfile + 8 个脚本 + 自检脚本)已打包,站内注册用户下载区可直接取。
0. 先看结论:这个方案到底在干什么
一句话:把整个 Windows 游戏塞进 Linux 容器,用一个假显示器跑起来,再用网页把画面串流回你的浏览器。
你的浏览器 ──HTTP──> noVNC (6080) ──> x11vnc (5901) ──> Xvfb 虚拟屏
│
┌─────────────────┴────────────────┐
│ │
Wine + PD2 fluxbox 桌面
(游戏窗口) (装游戏用)各组件干什么:
| 组件 | 作用 | 为什么不能省 |
|---|---|---|
| Xvfb | 无头虚拟显示器 | Linux 没物理屏幕,游戏要有地方画 |
| x11vnc | 把虚拟屏变成 VNC 服务 | VNC 本身只传画面 |
| noVNC | 把 VNC 翻译成网页 | 让你不用装 VNC 客户端 |
| websockify | VNC → WebSocket 桥接 | 浏览器只认 WebSocket |
| Wine | 跑 Windows 版 PD2 | 官方只发 Windows 版 |
| fluxbox | 极简桌面 | 要有图形界面点安装器 |
| xterm | 终端 | 装游戏的过程要在里面敲命令 |
容器有两种模式,自动切换:
| 模式 | 触发条件 | 你看到什么 |
|---|---|---|
install | 没检测到 game/ProjectD2/Game.exe | Linux 桌面 + 终端,在这里装游戏 |
game | 检测到 game/ProjectD2/Game.exe | 直接进游戏画面 |
1. 先把三个硬门槛说清楚
1.1 你必须自己有正版
PD2 官方原话:
You must own legitimate copies of Diablo II and Diablo II: Lord of Destruction, purchased from Blizzard, in order to play Project Diablo II. Using cracked CD-Keys will result in a permanent ban.
本文不提供任何游戏本体下载或破解方案,只给官方渠道。
1.2 四个要准备的东西
| 要准备的东西 | 官方渠道 | 费用 |
|---|---|---|
| D2 经典版安装器 | 战网账号页 → Classic games 区 | 安装器免费,要 D2 正版 key |
| LoD 资料片安装器 | 同上 | 安装器免费,要 LoD 正版 key |
| PD2 启动器 | projectdiablo2.com 注册后下载 | 免费 |
| 1.14d 补丁 ×2 | 暴雪官方 FTP(见 1.4) | 免费 |
对应的文件名:
D2_Classic_Installer.exe
Diablo_II_Lord_of_Destruction_Installer.exe
ProjectD2Installer.exe
D2Patch_114d.exe
LODPatch_114d.exePD2 启动器直链(已验证 200,37MB):
https://storage.googleapis.com/project-diablo-2-website-public/installer/ProjectD2Installer.exe1.3 26 位 CD-Key 怎么拿
登录战网账号 → https://account.battle.net/games#classic-game-accounts
- 页面往下滚到 Classic games 区域
- 那里显示 26 位 的 D2 key 和 LoD key,各一个
- 兑换后可能要等几分钟才出现;超过一小时没出来就清浏览器缓存
- 手里是老的 16 位实体 key,先去账号管理页兑换
1.4 1.14d 补丁:最容易漏的一步
战网经典版安装器装出来的是 1.13c,PD2 强制要求 1.14d。 不打这个补丁,前面全白做。
暴雪官方 FTP 直链(我实测两个都返回 200):
- 基版 — http://ftp.blizzard.com/pub/diablo2/patches/PC/D2Patch_114d.exe — 10,671,384 字节
- 资料片 — http://ftp.blizzard.com/pub/diablo2exp/patches/PC/LODPatch_114d.exe — 6,343,200 字节
怎么确认补丁打上了:游戏目录里会出现Patch_D2.mpq这个文件,1.13c 没有它。
这是整个流程里唯一一个能明确判定"对了"的信号。
你不用手动下载 —— 本方案里的 download_patches.sh 会自动从上面两个地址拉下来。
1.5 其他两条铁律
- 必须英文版安装。PD2 官方安装页明确写了 "Install must be done in English"。
- D2R(Resurrected) 不能替代经典版,数据格式不同,PD2 用不了。
2. 目录结构
/volume1/docker/anhei2/
├── compose.yml 编排文件
├── nas.sh 宿主机管理脚本(全命令见第 8 节)
├── check_game.sh 游戏文件自检
├── check_running.sh 容器运行状态自检
├── fetch_files.sh 文件归集 + 校验
├── selftest.sh 全套自检(34 项,详见第 10 节)
├── 资源与教程.md 公开资源索引(35 条已验证链接)
├── build/
│ ├── Dockerfile 镜像定义
│ ├── start_game.sh 启动脚本(install / game 两种模式)
│ ├── install_all.sh 一键安装全流程
│ ├── install_game.sh 分步安装引导
│ ├── install_fonts.sh 运行时装中文字体
│ ├── download_patches.sh 下载暴雪官方 1.14d 补丁
│ ├── pick_installers.sh 安装器识别(被上面两个 source)
│ └── collect_game_files.sh 游戏文件归位
└── game/ ← bind mount,游戏文件都在这
├── installers/ 放三个安装器(补丁会自动出现在这)
├── ProjectD2/ 归位后:模组目录
├── *.mpq 归位后:游戏数据
├── Game.exe 归位后:主程序
└── Save/ 存档关键设计:./game:/game 是 bind mount,不是 COPY 进镜像。
这样以后更新游戏文件只需要 docker compose restart,不用重建镜像;重建镜像也不会丢游戏和存档。
3. 部署
3.1 建目录、传文件
mkdir -p /volume1/docker/anhei2/build
mkdir -p /volume1/docker/anhei2/game/installers把 compose.yml 放到 /volume1/docker/anhei2/,build/ 里 8 个文件放到 /volume1/docker/anhei2/build/。
3.2 compose.yml 全文
services:
anhei2:
build:
context: ./build
dockerfile: Dockerfile
container_name: anhei2
restart: unless-stopped
# 显式指定工作目录。
# docker exec 会把「调用者当前所在目录」当作容器内的工作目录;
# 而 bind mount 只挂了 ./game 这一个子目录,所以从 /volume1/docker/anhei2
# 执行 docker exec 会报:
# "current working directory is outside of container mount namespace root"
# 写死 working_dir 后容器内外行为一致,不会再踩这个坑。
working_dir: /game
# 游戏文件用 bind mount,改档/更新只需重启容器,不用重建镜像
volumes:
- ./game:/game
# 5901 = 原始 VNC(VNC 客户端直连)
# 6080 = noVNC 网页版(浏览器直接玩,推荐)
ports:
- "5901:5901"
- "6080:6080"
environment:
DISPLAY: ":1"
GAME_DIR: "/game"
VNC_PORT: "5901"
NOVNC_PORT: "6080"
SCREEN: "1280x800x16"
RES: "1280x800"
WINEPREFIX: "/root/.wine"
WINEARCH: "win32"
WINEDEBUG: "-all"
# auto = 有游戏文件就开玩,没有就进安装桌面(推荐,默认)
# install = 强制进安装桌面(装游戏用)
# game = 强制直接开玩
PD2_MODE: "auto"
# 3dfx glide 模拟走 CPU 软渲染,不占用你的 GPU
# 若想用 GPU 直通跑 D2GL,可在 compose 里加 devices 传 /dev/dri
shm_size: "512m"几个刻意的选择:
- 不用
depends_on.condition: service_healthy—— 部分 NAS 的 compose 解析器比标准版弱,这个写法支持不好。本方案单容器无依赖,直接绕开。 - 写了
working_dir: /game—— 见第 11 节第 4 条,docker exec的坑。 - 端口左侧可改 —— NAS 自带服务容易占 6080/5901,被占了就往左挪。
shm_size: 512m—— 容器默认 64MB 共享内存太小,图形程序会崩。
3.3 启动
cd /volume1/docker/anhei2
docker compose up -d --build
docker compose logs -f第一次构建 13~15 分钟,慢在 wine32 那层(要拉几十个 32 位依赖):

构建完成:

最终版本(v12,补丁下载与安装器识别已固化进镜像):

日志里出现这几行就是成了:
[pd2] mode: install
[pd2] VNC on :5901
[pd2] noVNC on :6080 -> http://<NAS-IP>:6080/vnc.html
[pd2] install mode: desktop is up, no game launched.
3.4 浏览器连上去
http://<NAS-IP>:6080/vnc.html第一次打开,点左上角设置,确认地址填 <NAS-IP>:5901,然后连接。连上是一个 fluxbox 桌面。
4. 装游戏(全程在 NAS 上完成)
4.1 传安装器
传到 /volume1/docker/anhei2/game/installers/:

3-4GB 级别,网页上传很慢。建议:
- NAS 厂商的客户端挂载共享目录直接拖(最快)
- Windows 资源管理器 → 映射网络驱动器
scp -r(NAS 开了 SSH 的话)
4.2 一键装
浏览器桌面里双击 "PD2 One-Click Install",或者终端里:
/usr/local/bin/install_all.sh宿主机上等价命令:bash nas.sh installall

这个脚本会依次弹窗口,每步装完在终端按回车继续:
| 步骤 | 内容 | 你要做的 |
|---|---|---|
| 0 | 检查安装器 | 确认三个都识别到了 |
| 1 | 准备 Wine 环境 | 装 mono / gecko / directx9(自动) |
| 2 | 装 Diablo II | 路径选 C:\Program Files (x86)\Diablo II,输入 26 位 key |
| 3 | 装 Lord of Destruction | 装到同一个目录,输入 LoD 的 key |
| 4 | 打 1.14d 补丁 | 自动下载补丁 → 弹两个补丁程序,目录选 D2 安装目录 |
| 5 | 装 PD2 启动器 | 注册/登录 projectdiablo2.com,目标目录选 D2 目录 |
| 6 | 归位文件 | 自动执行 collect_game_files.sh |
第 4 步的补丁程序要注意两件事:
- 让你选目录时,必须选
C:\Program Files (x86)\Diablo II(就是你装游戏的那个目录),选错了补丁不生效 - 问"要不要重启电脑" → 选 No(Wine 里不需要重启)
4.3 中文字体(可选但建议)
容器里如果没中文字体,终端里中文会显示成空白(不是乱码 —— UTF-8 字节是对的,只是没有字形可画):

两种解决方式:
bash nas.sh fonts
# 方式 B:镜像里已经带了(v12 起),重建即可
docker compose up -d --build装完关掉当前终端窗口重开一个,因为老终端已经缓存了字体列表。
5. 归位与启动
装完游戏,install_all.sh 第 6 步会自动归位。手动跑也行:
bash nas.sh collect这个脚本解决两个最容易出问题的地方:
- 把散落在 Wine prefix 各个角落的 mpq / dll / exe 统一复制到
/game - 修正
ProjectD2/层级 —— 必须是一级game/ProjectD2/Game.exe,多套一层(常见于ProjectD2/ProjectD2/Game.exe)容器就找不到
判断标准写得很死:目录里必须同时有 Game.exe 和 .mpq 数据文件才算 D2 根目录。
只看 Game.exe 会误判 —— Wine prefix 根下常有个独立的 ProjectD2/ 目录,里面也有 Game.exe。
而且兜底全盘扫描时会额外排除路径里带 ProjectD2 的目录,否则会把模组的 mpq 抄到 /game 根下,把游戏搞坏。
看到 全部就绪! 就成了:
[OK] d2data.mpq
[OK] d2char.mpq
[OK] d2exp.mpq
[OK] d2sfx.mpq
[OK] d2speech.mpq
[OK] D2.LNG
[OK] ProjectD2/Game.exe
[OK] 1.14 补丁 (Patch_D2.mpq)然后启动:
bash nas.sh restart刷新 http://<NAS-IP>:6080/vnc.html,这次直接进游戏。
6. Dockerfile 关键层解析
完整 Dockerfile 在部署包里,这里说几个为什么这么写的点:
6.1 绕开 universe 仓库
# NO: novnc/websockify 在 universe,ubuntu 官方镜像默认不开 → apt 会时好时坏
# YES: noVNC 用官方 release tarball,websockify 用 pip
RUN wget -qO /tmp/novnc.tar.gz \
https://github.com/novnc/noVNC/archive/refs/tags/v1.5.0.tar.gz \
&& mkdir -p /usr/share/novnc \
&& tar -xzf /tmp/novnc.tar.gz -C /usr/share/novnc --strip-components=1 \
&& test -f /usr/share/novnc/vnc.html
RUN pip3 install --no-cache-dir --break-system-packages websockify \
|| pip3 install --no-cache-dir websockify6.2 32 位 OpenGL 栈(3dfx 黑屏的根因)
PD2 默认用 3dfx(Glide)软件渲染,官方 FAQ 明确指出缺 32 位 libGL 会报 OpenGL loader failed 然后全黑:
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libgl1:i386 libgl1-mesa-dri:i386 libglx-mesa0:i386 libgl1-mesa-glx:i386 \
|| apt-get install -y --no-install-recommends \
libgl1:i386 libgl1-mesa-dri:i386 libglx-mesa0:i386 \
&& rm -rf /var/lib/apt/lists/*(libgl1-mesa-glx 在新版 mesa 已移除,所以做了 apt 失败兜底。)
6.3 wine32 需要先加架构
RUN dpkg --add-architecture i386 \
&& apt-get update \
&& apt-get install -y --no-install-recommends wine32 winetricks ...这层特别慢(5-8 分钟),构建别中断。
6.4 中文字体
xfonts-base fonts-dejavu-core \
fonts-wqy-microhei fonts-wqy-zenhei fontconfig \
...
RUN fc-cache -f || truefc-cache -f 不能省,不然字体装了也认不出来。
7. 装完之后:更新游戏 / 升级模组
游戏文件是 bind mount 进去的:
bash nas.sh restart更新完 PD2 模组、或者换了新版本的游戏文件,只需要 restart,不用重建镜像。
8. 日常运维:nas.sh 全命令表
宿主机上 cd /volume1/docker/anhei2 然后:
| 命令 | 作用 |
|---|---|
bash nas.sh status | 容器状态 + 最近日志 |
bash nas.sh install | 在容器里跑分步安装引导 |
bash nas.sh installall | 一键安装全流程(D2 → LoD → 1.14d → PD2 → 归位) |
bash nas.sh patch | 下载暴雪官方 1.14d 补丁 |
bash nas.sh fonts | 装中文字体(不用重建镜像) |
bash nas.sh collect | 归位游戏文件 |
bash nas.sh check | 校验游戏文件是否齐全 |
bash nas.sh logs | 跟踪容器日志 |
bash nas.sh restart | 重启容器 |
bash nas.sh stop | 停止容器 |
bash nas.sh up | 启动容器(不重新构建) |
bash nas.sh build | 重新构建镜像并启动(5-10 分钟) |
bash nas.sh files | 看宿主机上 game 目录的内容和占用 |
bash nas.sh shell | 进容器交互式 shell |
9. 故障排查表(16 条)
| 现象 | 原因 / 处理 |
|---|---|
容器起来报 mode: install 但你想直接玩 | 游戏文件没归位。跑 collect_game_files.sh 看缺什么 |
| noVNC 终端里中文全是空白 | 容器缺中文字体(不是编码问题)。bash nas.sh fonts 装完重开终端 |
| PD2 启动器说版本不对 / 拒绝启动 | 没打 1.14d 补丁。bash nas.sh patch 下补丁,再在 Wine 里跑一遍 |
补丁程序说"已是最新"但目录里没 Patch_D2.mpq | 补丁目录选错了,要选 C:\Program Files (x86)\Diablo II |
| noVNC 一直转圈 | 第一次启动在装 directx9,等 1-2 分钟 |
| 连上 noVNC 是黑屏 | 没开 fluxbox 桌面,或 Xvfb 没起来。docker compose logs 看 |
| 装游戏时 EULA 窗口没文字 | 缺 wine-gecko。winetricks -q gecko |
| 装游戏报缺少 DirectX | winetricks -q directx9 |
| 游戏画面黑但 VNC 能连 | 3dfx 渲染没起来。镜像已装 32 位 libGL;仍不行就 wine Game.exe -ns(去掉 -3dfx) |
日志报 OpenGL loader failed | 同上,确认构建时 libgl1:i386 那层没报错 |
| 找不到 ProjectD2/Game.exe | 层级错了。必须 game/ProjectD2/Game.exe |
| 画面太小 / 被裁切 | 改 compose 里 SCREEN 和 RES,两个值保持一致 |
| 键盘打不出字 | noVNC 页面按 Ctrl+Shift 切键盘布局 |
| 存档丢了 | game/Save 没挂出来,确认 volumes 里 ./game:/game 写对了 |
| 想清档重来 | 停容器 → 删 game/Save → 重启 |
docker exec 报 mount namespace 错误 | 见第 11 节第 4 条,加 -w /game |
10. 自检:34 项回归测试
部署包里带了一个自检脚本,可以在你自己的 NAS 上跑:
cd /volume1/docker/anhei2
bash selftest.sh覆盖:
- 全部 11 个 shell 脚本
bash -n语法检查 compose.ymlYAML 解析 + 卷挂载正确性- 安装器识别的 5 种命名场景(含"只有 LoD 时不能把资料片误认成基版"这个回归用例)
- 归位脚本的 5 种目录布局端到端(标准路径 / 非标准路径 / 多套一层 / prefix 根下独立 ProjectD2 陷阱 / 空 prefix 报错)
全部跑通应该是 通过 34 / 失败 0。
11. 实测踩坑清单(15 条)
3dfx缺 32 位 libGL → 黑屏(官方 FAQ 明确指出),镜像已装四个相关包- noVNC 官方发行包不含 websockify,必须另装(pip)
novnc/websockify在 universe 仓库,ubuntu:22.04 镜像默认不开 → 改用官方 tarball + pip 绕开docker exec报 "current working directory is outside of container mount namespace root" ——docker exec默认把调用者的 CWD 当容器工作目录,而 bind mount 的父目录没法映射进容器。解法:docker exec -it -w /game anhei2 ...,或 compose 里写working_dir: /game- 游戏文件不能 COPY 进镜像,否则每次更新都要重建 → 改 bind mount
ProjectD2/必须在game/一级,多套一层就找不到Game.exe- PD2 要 1.14d 不是 1.13c —— 而且战网经典版安装器装出来正好是 1.13c,必须补官方补丁。补丁有没有生效看
Patch_D2.mpq - CD-Key 是 26 位,16 位要先在战网兑换
- PD2 模组必须注册账号由启动器下载
- 暴雪经典版安装器没有可靠的静默安装参数 → 只能用 Wine 图形界面装
wine32需要dpkg --add-architecture i386- 找 D2 安装目录不能只看有没有
Game.exe—— prefix 根下的独立ProjectD2/也有Game.exe。判定标准必须是「同时有 Game.exe 和 .mpq」;兜底全盘扫描还要额外排除路径含ProjectD2的目录 - 基版 D2 和 LoD 的安装器文件名会互相误匹配 —— 找基版用的
*Diablo*II*也会命中Diablo_II_Lord_of_Destruction_Installer.exe,少了基版时会把资料片跑两遍。必须先按Lord/Destruction关键词分流 - 脚本里少定义一个函数不会报错退出,只会打印
command not found然后继续跑 —— 我就这么丢过一个warn函数 - 容器里中文显示成空白 ≠ 编码错。UTF-8 字节是对的,只是没 CJK 字体。装
fonts-wqy-microhei+fc-cache -f即可,不用重建镜像
12. 升级与卸载
升级
# 改了 build/ 里的脚本,才需要重建(会重置 Wine prefix,但 /game 不丢)
bash nas.sh build
# 只是更新了游戏文件/模组
bash nas.sh restart卸载
cd /volume1/docker/anhei2
docker compose down游戏文件和存档都在 ./game 里,删不删随你。
想清空重来
docker compose down
# 删掉 game 下的所有东西(游戏本体 + 存档)
# 然后重新 docker compose up -d --build13. 完整部署包
整套文件都在站内的注册用户下载区:
Dockerfile(全文,每层带注释)- 8 个脚本(
start_game/install_all/install_game/install_fonts/download_patches/pick_installers/collect_game_files+ 宿主机侧nas.sh) compose.ymlselftest.sh自检脚本资源与教程.md—— 35 条已验证的公开资源链接(wiki / GitHub / 社区 / 补丁直链)
注册后即可下载,不用手抄。
14. 参考链接
官方
- PD2 官网 https://www.projectdiablo2.com
- PD2 下载 https://www.projectdiablo2.com/download
- PD2 Wiki(Miraheze) https://wiki.projectdiablo2.com
- PD2 官方 GitHub 组织 https://github.com/Project-Diablo-2(含 d2gl 高分辨率重制 / LootFilters / pd2-tools)
- PD2 安装 FAQ(Wine 安装步骤 / 3dfx libGL)https://wiki.projectdiablo2.com/wiki/Support_FAQ
暴雪
- 经典版安装包 https://download.battle.net/?platform=pc
- 26 位 key https://account.battle.net/games#classic-game-accounts
- 1.14d 补丁(基版)http://ftp.blizzard.com/pub/diablo2/patches/PC/D2Patch_114d.exe
- 1.14d 补丁(资料片)http://ftp.blizzard.com/pub/diablo2exp/patches/PC/LODPatch_114d.exe
容器相关
如果这篇帮到你了,欢迎在评论区说一声。有报错的话把 bash nas.sh status 的输出贴上来,我看到就回。



