# DNF90 灵宝小竹单机版 · 源码仓库 《地下城与勇士》90 级版本的**本地单机模拟器**(非私服、不联网、不开服)完整源码 monorepo:服务端、客户端补丁、整合启动器、游戏内 GM 控制台,以及 PVF 解析套件。 - 当前版本:**v2026.11.05** - 维护:汉服电脑宅团队(灵宝仙尊 联合 淡竹茹 开发) - 官方 QQ 群:**1128511524** - 成品下载:[百度网盘](https://pan.baidu.com/s/1j31dVPF_nPKSN2QYHU-Ygg?pwd=s33r)(提取码 `s33r`,链接已内置) - 仓库只存源码,不含 exe/dll、美术与 PVF 资源;发布包另走网盘 > 本模拟器资源禁止倒卖,请到官方微信公众号或社群下载。 > **用 AI 接续开发?先读 [AGENTS.md](AGENTS.md)** —— 硬规则、构建命令、验证方法、代码地图一页看完。 --- ## 一、它由哪几块组成 | 进程 / 组件 | 位置 | 监听 | 职责 | |------------|------|------|------| | `DNF90启动器.exe` | `launcher/` | `127.0.0.1:19527` | 拉起与停止服务端、内置 GM 工具、换装、游戏设置、更新日志、关于页 | | `DNF90Server.exe` | `server/` | `127.0.0.1:18111`(Admin) | 游戏逻辑、账号角色、背包拍卖行任务副本、SQLite 落库 | | GM 控制台网页 | `gmweb/` | `127.0.0.1:18112` | 游戏内嵌页面,`/api/*` 反代 18111 并注入 admin token | | `90CN.dll` | `client-patch/` | — | 客户端补丁,接管游戏自带面板,弹 WebView2 窗口指向 18112 | 调用关系(从玩家视角): 1. 玩家双击启动器 → 启动器拉起 `DNF90Server.exe`,并顺带起 `gmweb/serve.py` 2. 登录进游戏 → 客户端加载 `90CN.dll`,连本地服务端 `18111`(DLL 指纹不匹配就断开) 3. 点小井盖 → 补丁弹自己的窗口,打开 `http://127.0.0.1:18112/` 的 GM 控制台 4. 控制台里的所有操作,经 `serve.py` 反代到服务端 Admin 接口执行 ## 二、目录结构 ``` 90CNServer/ ├── server/ DNF90 服务端(Go,模块 longheng.io/server) │ ├── cmd/server/{dnf90,control,doctor} 主程序 / 控制台 / 自检 │ ├── internal/ pkg/ 业务与基础库 │ └── build_verify/ 构建校验脚本(不参与发布) ├── client-patch/ 客户端补丁(C++ / MSVC,含 90CN_web.ini) ├── launcher/ 整合启动器 + 内置 GM 工具(Python 3.15 + pywebview) │ └── qr/ 关于页二维码(alipay / wechat / gzh / qq) ├── gmweb/ 游戏内 GM 控制台网页(纯静态 + serve.py 反代) │ └── qr/ 控制台关于页二维码 ├── loadout-tool/ 一键换装工具(C# / .NET) ├── gmtool/ GM 工具旧版(FastAPI,已被 launcher 取代,仅留参考) ├── pvf/ PVF 解析套件(Go,模块 pvfine,GPL-3.0) │ └── cmd/pvfexport/ 物品名称表导出工具 ├── docs/ 开发文档 + 发布物料模板(release/) └── README.md ``` ## 三、子项目速览 | 目录 | 语言 / 技术栈 | 说明 | |------|--------------|------| | `server/` | Go 1.26.5(`longheng.io/server`) | 服务端主体;Admin/GM HTTP 接口、拍卖行、背包、任务、副本、点券 | | `client-patch/` | C++ / MSVC vcxproj | `90CN.dll`:面板接管、安图恩兼容、蛇腹剑修复、战斗面板布局等 | | `launcher/` | Python 3.15(32 位)+ pywebview 6 + PyInstaller | 单 EXE 启动器,六页单窗口,内置完整 GM 工具与更新日志 | | `gmweb/` | 纯 HTML/CSS/JS + Python 标准库 | 游戏内 GM 控制台页面,`serve.py` 反代 18111 并注入 admin token | | `loadout-tool/` | C# / .NET | 独立换装工具,通过 Admin API 操作 | | `gmtool/` | Python + FastAPI | GM 工具旧版,保留参考 | | `pvf/` | Go 1.26(`pvfine`,GPL-3.0) | PVF 解析库 + `pvfexport` 物品名导出 | ## 四、快速开始 ### 4.1 只想要能玩的成品 1. 从[百度网盘](https://pan.baidu.com/s/1j31dVPF_nPKSN2QYHU-Ygg?pwd=s33r)(提取码 `s33r`)或官方社群下载发布包,解压到**非中文、无空格**路径,例如 `D:\DNF90` 2. 双击 `DNF90启动器.exe` 3. 点「开始」拉起服务端,等日志出现「服务已就绪」再进游戏(首次约 85 秒) 4. 进游戏后点小井盖即可打开 GM 控制台 ### 4.2 从源码编译 **服务端**(Go ≥ 1.26.5,建议国内镜像): ```bash go env -w GOPROXY=https://goproxy.cn,direct go env -w GOSUMDB=sum.golang.google.cn cd server go build -buildvcs=false -trimpath -o DNF90Server.exe ./cmd/server/dnf90 go build -buildvcs=false -trimpath -o DNF90Control.exe ./cmd/server/control go build -buildvcs=false -trimpath -o DNF90Doctor.exe ./cmd/server/doctor ``` **启动器**(Python 3.15 32 位): ```bash cd launcher python -m pip install pywebview pythonnet clr-loader pyinstaller python -m PyInstaller --clean --noconfirm "DNF90启动器.spec" # 产物:launcher/dist/DNF90启动器.exe ``` > 单文件打包有代价:onefile 会先解包再启动,改完源码必须重新打包;旧进程未退出时会继续占用 19527 端口,看到旧界面先确认进程已杀干净。 **PVF 物品名导出**: ```bash cd pvf go build ./cmd/pvfexport/ pvfexport -pvf Script.pvf -out dnf_items.json -compact ``` **客户端补丁 / 换装工具**:详见 `docs/开发文档.md` 与 `client-patch/`、`loadout-tool/` 各自的工程文件。 ## 五、启动器要点 - **单窗口六页**:开始 / GM工具 / 一键换装 / 游戏设置 / 游戏更新 / 关于我们,导航栏左侧切换、右侧状态灯 - **本地端口** 19527,被占用时自动 +1 顺延(最多 20 次);页面为本地静态资源,响应统一 `Cache-Control: no-store`,避免 WebView2 缓存旧页面 - **更新日志双来源**:优先读仓库 `Lingbao/90CNServer` 的 Release 接口,再用 EXE 同目录的 `更新日志.txt` 补齐仓库还没发布的版本,按日期去重(仓库优先);断网时只用本地记录并在页面顶部给出黄色提示 - **当前更新 / 历史更新** 分成两个标签页,历史页显示条数 - **关于我们**:四行介绍文案 + 三列二维码(赞助我们:支付宝/微信 · 官方QQ群 · 团队公众号),点任意二维码可放大 ## 六、游戏内 GM 控制台(gmweb/) - 由 `serve.py` 托管在 `127.0.0.1:18112`,`/api/*` 反代到服务端 `127.0.0.1:18111` 并注入 admin token(token 不进页面 JS) - 窗口尺寸由 `client-patch/90CN_web.ini` 决定:`width=800 height=720` - 页面为 **IE11 兼容**写法:不用 flex,统一 `float` + `box-sizing:border-box`;关于页垂直居中用 `position:absolute; top:50%` + `-ms-transform` - 关于页结构:介绍卡片(712 宽)+ 组合面板三列 `赞助我们 353 | 官方QQ群 178 | 团队公众号 179`,列间一条竖线 - 静态资源带 `?v=YYYYMMDDx` 版本后缀,改 CSS/JS 后**必须加一版**,否则 IE11 缓存不刷新 ## 七、二维码与品牌素材替换 | 位置 | 用途 | 说明 | |------|------|------| | `launcher/qr/` | 启动器「关于我们」 | `alipay.png` / `wechat.png` / `gzh.jpg` / `qq.png`,同名覆盖即替换 | | `gmweb/qr/` | 游戏内控制台「关于」 | 同上四张 | | `launcher/sponsor.png` / `sponsor_qr.png` | 旧版赞助页素材 | 历史遗留,可忽略 | - 图片缺失不会破图:启动器与 GM 页都会显示「二维码未配置 + 期望路径」占位 - 启动器打包时 `qr/` 四张图会进 EXE;同时支持在 EXE 同目录放 `qr\` 覆盖(优先级高于内置) - `launcher/server.py` 的 `/qr/` 是**白名单**路由,只认这四个文件名,防目录穿越 ## 八、版本号与发布流程 1. **改版本号**:`launcher/server.py` 的 `CURRENT_VERSION`(当前 `v2026.11.05`),游戏内与启动器首页都读它 2. **写更新日志**:在发布包根目录的 `更新日志.txt` 追加一条(日期 + 条目),启动器会自动合并进「游戏更新」 3. **构建**:服务端三个 exe + 启动器单文件 exe 4. **打发布包**:服务端 runtime、gmweb、启动器、`90CN_web.ini` 一起收齐;**DLL 与 EXE 必须配套**(见下) 5. **发 Release**:在本仓库的 Release 里写发布说明(模板见 `docs/release/发布说明模板.md`) 6. **网盘地址**:`docs/release/网盘下载.url` 已指向百度网盘真实分享(提取码 `s33r`),作为 Release 附件上传——十几个 G 的包不适合直传,`.url` 是最省事的缓兵之计;重新分享后记得同步改模板表格 7. **验证**:启动器首页版本号、更新日志条数、关于页二维码、GM 页三列是否都正常 > **第 5、6 步可以一条命令搞定**(Gitea REST API,脚本只用标准库): > > ```bash > set GITEA_TOKEN=xxxx > python docs/release/publish_release.py --version v2026.11.06 --notes-file docs/release/发布说明模板.md > ``` > > 它会先核对本地/远端 tag 是否指向同一提交(防「Release 挂在旧代码上」),再建或更新 Release、上传 `.url`,可重复执行。加 `--dry-run` 只打印计划。详见 [docs/release/README.md](docs/release/README.md)。 > ⚠️ **DLL 指纹绑定**:`90CN.dll` 的 SHA-256 在编译期编进 `DNF90Server.exe`(`boundDllSHA256`)。换 DLL 必须重编服务端,否则客户端连接会被拒绝。发布包里 DLL 与 EXE 一定要成对。 ## 九、关键设计说明 ### GM 工具双数据源 - **Native API**(服务端在线):`127.0.0.1:18111` + `X-Admin-Token`,实时操作、事务一致 - **SQLite 直读**(服务端离线):直接读写 `Server/runtime/data/dnf90.db`,可预览与批量改 - 启动器通过探测 18111 是否监听来切换,界面上以状态灯区分 ### 物品名称离线显示 `pvf/cmd/pvfexport` 从 `Script.pvf` 提取 `item_id → 名称` 全量映射导出 `dnf_items.json`,放在 `GM配置/`;启动时对比 PVF 与名称表修改时间,PVF 更新则自动重新导出,离线也能显示中文物品名。 ### GM 拍卖行 服务端实现 `/local/dnf-gm/auction/*`:免押金上架(`Deposit=0`)、自动补足上架(背包不足时先发差额)、列背包 / 删除背包行(含穿戴装备)。 ### 客户端自校验(CRC32) `90CN.dll` 里有一个 `SelfCrcSlot` 结构(`magic=0xC0DEC0DE` + `offset` + `expected`),构建后由 `client-patch/tools/patch_dll_crc.py` 把「整个文件的 CRC32」与槽位偏移写进去;运行时读盘重算并比对,不符就弹框退出——所以「patch 掉校验分支」这条路是堵死的。 - 编译脚本 `build_90cn.ps1` 第三步会自动补 CRC,`-SkipCrc` 出来的 DLL 不要发布 - 定位槽位只认 `.data` / `.rdata`,因为 `0xC0DEC0DE` 在 `.text` 里也有一份(MSVC 的比较立即数),扫错会写坏机器码,表现为读槽位后立刻崩 - 改完 DLL 除了补 CRC,**还要重编服务端**:`DNF90Server.exe` 编译期绑定了 DLL 的 SHA-256(`boundDllSHA256`),指纹不配套会在登录阶段被踢 详见 [docs/客户端补丁与CRC校验.md](docs/客户端补丁与CRC校验.md)。 ## 十、常见坑 | 现象 | 原因 | 处理 | |------|------|------| | 启动器还是旧版本号 / 旧品牌 | 旧进程没退,19527 被占,页面连到旧服务 | 任务管理器结束所有 `DNF90启动器.exe` 后重开 | | 改了 CSS/JS 页面没变 | WebView2 或 IE11 缓存 | 启动器已发 `no-store`;GM 页需改 `?v=` 版本号 | | 二维码显示空框 | 图片没放进 `qr/` 或缺文件名 | 看占位提示里的期望路径,四个文件名固定 | | 关于页底部被日志栏盖住 | 内容高度超过可视区 | GM 页 `#body` 的 `bottom` 必须等于 `#log` 实高(含 padding/border) | | 客户端连不上服务端 | DLL 与 EXE 指纹不匹配 | 用配套的一对重新打包,别混用 | | 竖线不显示 | CSS 特异性:`.cols .box` 把 border 清掉了 | 分隔线写在 `.cols .box-r` / `.cols .box-m` 上 | | 启动器窗口越开越多、里面没内容 | 打包后 `sys.executable` 就是启动器自己,`start_gmweb()` 拉起的是新窗口而不是 `serve.py` | 已有「多调用模式 + 单实例互斥 + 就绪校验」,见 [docs/启动器窗口风暴排查.md](docs/启动器窗口风暴排查.md) | | 启动器窗口能开但一片空白 | WebView2 数据目录不可写(exe 在只读目录/网络盘) | 已固定到 `%LOCALAPPDATA%\DNF90Launcher\WebView2`;缺运行时看 `启动器日志.txt` | ## 十一、文档索引 | 文档 | 内容 | |------|------| | [AGENTS.md](AGENTS.md) | **AI / 新人接续开发入口**:硬规则 R1–R9、构建与验证命令、代码地图、发版清单 | | [docs/README.md](docs/README.md) | 文档总索引与上手路径 | | [docs/开发文档.md](docs/开发文档.md) | 启动器 / GM 工具:环境、架构、API 契约、打包部署、常见问题 | | [docs/服务端开发文档.md](docs/服务端开发文档.md) | 服务端架构、GM 接口契约、拍卖行与背包改动 | | [docs/PVF解析开发文档.md](docs/PVF解析开发文档.md) | pvfine 与 pvfexport、离线名称集成 | | [docs/客户端补丁与CRC校验.md](docs/客户端补丁与CRC校验.md) | 90CN.dll 自校验 CRC、`patch_dll_crc.py` 用法、与 DLL 指纹的区别 | | [docs/启动器窗口风暴排查.md](docs/启动器窗口风暴排查.md) | 启动器套娃开窗的根因与修复、日志判读、现场取证 | | [docs/踩坑记录.md](docs/踩坑记录.md) | **本项目踩过的坑合集**(13 条):成因、为什么没早发现、教训与发版清单 | | `server/README.md`、`pvf/README.md`、`launcher/README.md`、`gmtool/README.md` | 各子项目说明 | | [docs/release/](docs/release/) | 发布说明模板、网盘下载 `.url` 模板 | ## 十二、许可证与声明 - `server/`、`client-patch/`、`launcher/`、`gmweb/`、`loadout-tool/`、`gmtool/`:**私有 / 闭源**,请勿公开分发或倒卖 - `pvf/`:源自开源项目 pvfine,遵循 **GPL-3.0**(见 `pvf/LICENSE`),注意传染性 - 本模拟器仅供本地单机体验,禁止用于开服运营;资源禁止倒卖,请到官方微信公众号或社群下载 - 对外发布服务端时不要附带本机测试用的掉落改动;远程换装工具不要直接暴露 Admin 端口 ## 十三、提交约定 1. 各子项目独立编译,无跨目录构建依赖 2. 不提交编译产物:`launcher/dist/`、`launcher/build/`、`server/build_verify/`、`__pycache__`、`*.exe`/`*.dll`、`dnf_items.json`、`*.pvf` 均已在 `.gitignore` 3. 提交信息用「范围:做了什么」的形式,例如 `启动器:游戏更新拆分为当前/历史双子页,新增关于我们页` 4. 服务端 GM 接口有变化时,同步更新 `docs/服务端开发文档.md` 的 API 契约章节 5. 改完源码记得回灌运行目录(`Z:\90server` 与桌面发布包),否则跑的还是旧文件