- 新增 docs/客户端补丁与CRC校验.md:SelfCrcSlot 结构、CRC32 算法与种子坑、 槽位为什么只在 .data/.rdata 里找、info/verify/patch 用法与退出码、 build_90cn.ps1 集成、与「服务端 DLL 指纹」的区别、六类故障排查 - 新增 client-patch/tools/README.md:脚本清单与常用命令速查 - docs/README.md:文档索引与上手路径补一条 - 根 README:关键设计说明补「客户端自校验(CRC32)」小节,文档索引补一行 - 90CN.cpp:清掉过期的重复注释(旧的 patch_selfcrc.py / .text 段说法),与实现对齐
13 KiB
DNF90 灵宝小竹单机版 · 源码仓库
《地下城与勇士》90 级版本的本地单机模拟器(非私服、不联网、不开服)完整源码 monorepo:服务端、客户端补丁、整合启动器、游戏内 GM 控制台,以及 PVF 解析套件。
- 当前版本:v2026.11.05
- 维护:汉服电脑宅团队(灵宝仙尊 联合 淡竹茹 开发)
- 官方 QQ 群:1128511524
- 成品下载:百度网盘(提取码
s33r,链接已内置) - 仓库只存源码,不含 exe/dll、美术与 PVF 资源;发布包另走网盘
本模拟器资源禁止倒卖,请到官方微信公众号或社群下载。
一、它由哪几块组成
| 进程 / 组件 | 位置 | 监听 | 职责 |
|---|---|---|---|
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 |
调用关系(从玩家视角):
- 玩家双击启动器 → 启动器拉起
DNF90Server.exe,并顺带起gmweb/serve.py - 登录进游戏 → 客户端加载
90CN.dll,连本地服务端18111(DLL 指纹不匹配就断开) - 点小井盖 → 补丁弹自己的窗口,打开
http://127.0.0.1:18112/的 GM 控制台 - 控制台里的所有操作,经
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 只想要能玩的成品
- 从百度网盘(提取码
s33r)或官方社群下载发布包,解压到非中文、无空格路径,例如D:\DNF90 - 双击
DNF90启动器.exe - 点「开始」拉起服务端,等日志出现「服务已就绪」再进游戏(首次约 85 秒)
- 进游戏后点小井盖即可打开 GM 控制台
4.2 从源码编译
服务端(Go ≥ 1.26.5,建议国内镜像):
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 位):
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 物品名导出:
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/<name>是白名单路由,只认这四个文件名,防目录穿越
八、版本号与发布流程
- 改版本号:
launcher/server.py的CURRENT_VERSION(当前v2026.11.05),游戏内与启动器首页都读它 - 写更新日志:在发布包根目录的
更新日志.txt追加一条(日期 + 条目),启动器会自动合并进「游戏更新」 - 构建:服务端三个 exe + 启动器单文件 exe
- 打发布包:服务端 runtime、gmweb、启动器、
90CN_web.ini一起收齐;DLL 与 EXE 必须配套(见下) - 发 Release:在本仓库的 Release 里写发布说明(模板见
docs/release/发布说明模板.md) - 网盘地址:
docs/release/网盘下载.url已指向百度网盘真实分享(提取码s33r),作为 Release 附件上传——十几个 G 的包不适合直传,.url是最省事的缓兵之计;重新分享后记得同步改模板表格 - 验证:启动器首页版本号、更新日志条数、关于页二维码、GM 页三列是否都正常
⚠️ 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),指纹不配套会在登录阶段被踢
十、常见坑
| 现象 | 原因 | 处理 |
|---|---|---|
| 启动器还是旧版本号 / 旧品牌 | 旧进程没退,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 上 |
十一、文档索引
| 文档 | 内容 |
|---|---|
| docs/README.md | 文档总索引与上手路径 |
| docs/开发文档.md | 启动器 / GM 工具:环境、架构、API 契约、打包部署、常见问题 |
| docs/服务端开发文档.md | 服务端架构、GM 接口契约、拍卖行与背包改动 |
| docs/PVF解析开发文档.md | pvfine 与 pvfexport、离线名称集成 |
| docs/客户端补丁与CRC校验.md | 90CN.dll 自校验 CRC、patch_dll_crc.py 用法、与 DLL 指纹的区别 |
server/README.md、pvf/README.md、launcher/README.md、gmtool/README.md |
各子项目说明 |
| docs/release/ | 发布说明模板、网盘下载 .url 模板 |
十二、许可证与声明
server/、client-patch/、launcher/、gmweb/、loadout-tool/、gmtool/:私有 / 闭源,请勿公开分发或倒卖pvf/:源自开源项目 pvfine,遵循 GPL-3.0(见pvf/LICENSE),注意传染性- 本模拟器仅供本地单机体验,禁止用于开服运营;资源禁止倒卖,请到官方微信公众号或社群下载
- 对外发布服务端时不要附带本机测试用的掉落改动;远程换装工具不要直接暴露 Admin 端口
十三、提交约定
- 各子项目独立编译,无跨目录构建依赖
- 不提交编译产物:
launcher/dist/、launcher/build/、server/build_verify/、__pycache__、*.exe/*.dll、dnf_items.json、*.pvf均已在.gitignore - 提交信息用「范围:做了什么」的形式,例如
启动器:游戏更新拆分为当前/历史双子页,新增关于我们页 - 服务端 GM 接口有变化时,同步更新
docs/服务端开发文档.md的 API 契约章节 - 改完源码记得回灌运行目录(
Z:\90server与桌面发布包),否则跑的还是旧文件