暗黑2 PD2 部署在 NAS:完整可复现教程(Dockerfile 全文 + 全部脚本 + 排查表)

阅读 -- 次
这篇是完整可复现版。从空目录开始,照着做就能把 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 客户端
websockifyVNC → WebSocket 桥接浏览器只认 WebSocket
Wine跑 Windows 版 PD2官方只发 Windows 版
fluxbox极简桌面要有图形界面点安装器
xterm终端装游戏的过程要在里面敲命令

容器有两种模式,自动切换:

模式触发条件你看到什么
install没检测到 game/ProjectD2/Game.exeLinux 桌面 + 终端,在这里装游戏
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.exe

PD2 启动器直链(已验证 200,37MB):

https://storage.googleapis.com/project-diablo-2-website-public/installer/ProjectD2Installer.exe

1.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):

怎么确认补丁打上了:游戏目录里会出现 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 位依赖):

首次构建,i386 依赖层最耗时

构建完成:

镜像构建完成,容器启动

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

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 步的补丁程序要注意两件事:

  1. 让你选目录时,必须选 C:\Program Files (x86)\Diablo II(就是你装游戏的那个目录),选错了补丁不生效
  2. 问"要不要重启电脑" → 选 No(Wine 里不需要重启)

4.3 中文字体(可选但建议)

容器里如果没中文字体,终端里中文会显示成空白(不是乱码 —— UTF-8 字节是对的,只是没有字形可画):

noVNC 终端里中文全是空白

两种解决方式:


bash nas.sh fonts

# 方式 B:镜像里已经带了(v12 起),重建即可
docker compose up -d --build

装完关掉当前终端窗口重开一个,因为老终端已经缓存了字体列表。


5. 归位与启动

装完游戏,install_all.sh 第 6 步会自动归位。手动跑也行:

bash nas.sh collect

这个脚本解决两个最容易出问题的地方:

  1. 把散落在 Wine prefix 各个角落的 mpq / dll / exe 统一复制到 /game
  2. 修正 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 websockify

6.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 || true

fc-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
装游戏报缺少 DirectXwinetricks -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

覆盖:

  1. 全部 11 个 shell 脚本 bash -n 语法检查
  2. compose.yml YAML 解析 + 卷挂载正确性
  3. 安装器识别的 5 种命名场景(含"只有 LoD 时不能把资料片误认成基版"这个回归用例)
  4. 归位脚本的 5 种目录布局端到端(标准路径 / 非标准路径 / 多套一层 / prefix 根下独立 ProjectD2 陷阱 / 空 prefix 报错)

全部跑通应该是 通过 34 / 失败 0。


11. 实测踩坑清单(15 条)

  1. 3dfx 缺 32 位 libGL → 黑屏(官方 FAQ 明确指出),镜像已装四个相关包
  2. noVNC 官方发行包不含 websockify,必须另装(pip)
  3. novnc/websockify 在 universe 仓库,ubuntu:22.04 镜像默认不开 → 改用官方 tarball + pip 绕开
  4. 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
  5. 游戏文件不能 COPY 进镜像,否则每次更新都要重建 → 改 bind mount
  6. ProjectD2/ 必须在 game/ 一级,多套一层就找不到 Game.exe
  7. PD2 要 1.14d 不是 1.13c —— 而且战网经典版安装器装出来正好是 1.13c,必须补官方补丁。补丁有没有生效看 Patch_D2.mpq
  8. CD-Key 是 26 位,16 位要先在战网兑换
  9. PD2 模组必须注册账号由启动器下载
  10. 暴雪经典版安装器没有可靠的静默安装参数 → 只能用 Wine 图形界面装
  11. wine32 需要 dpkg --add-architecture i386
  12. 找 D2 安装目录不能只看有没有 Game.exe —— prefix 根下的独立 ProjectD2/ 也有 Game.exe。判定标准必须是「同时有 Game.exe 和 .mpq」;兜底全盘扫描还要额外排除路径含 ProjectD2 的目录
  13. 基版 D2 和 LoD 的安装器文件名会互相误匹配 —— 找基版用的 *Diablo*II* 也会命中 Diablo_II_Lord_of_Destruction_Installer.exe,少了基版时会把资料片跑两遍。必须先按 Lord/Destruction 关键词分流
  14. 脚本里少定义一个函数不会报错退出,只会打印 command not found 然后继续跑 —— 我就这么丢过一个 warn 函数
  15. 容器里中文显示成空白 ≠ 编码错。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 --build

13. 完整部署包

整套文件都在站内的注册用户下载区:

  • Dockerfile(全文,每层带注释)
  • 8 个脚本(start_game / install_all / install_game / install_fonts / download_patches / pick_installers / collect_game_files + 宿主机侧 nas.sh)
  • compose.yml
  • selftest.sh 自检脚本
  • 资源与教程.md —— 35 条已验证的公开资源链接(wiki / GitHub / 社区 / 补丁直链)

注册后即可下载,不用手抄。


14. 参考链接

官方

暴雪

容器相关


如果这篇帮到你了,欢迎在评论区说一声。有报错的话把 bash nas.sh status 的输出贴上来,我看到就回。

消息盒子

# 暂无消息 #

只显示最新10条未读和已读信息