From 3b240ec176f4fe79bcc4c5ea801c521fba08a5be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=81=B5=E5=AE=9D=E4=BB=99=E5=B0=8A?= Date: Thu, 5 Nov 2026 17:40:13 +0800 Subject: [PATCH] =?UTF-8?q?docs=EF=BC=9A=E6=96=B0=E5=A2=9E=20AGENTS.md=20?= =?UTF-8?q?=E4=BD=9C=E4=B8=BA=20AI=20/=20=E6=96=B0=E4=BA=BA=E6=8E=A5?= =?UTF-8?q?=E7=BB=AD=E5=BC=80=E5=8F=91=E7=9A=84=E5=85=A5=E5=8F=A3=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 一页纸解决「接手时不知道从哪读、哪些不能碰、改完怎么验」: - 0 组件与端口速览;1 必读顺序(踩坑记录 P-001/P-002/P-011 列为首要)+ 按改动选文档 - 2 硬规则 R1–R9:DLL 与 exe 成对、改 DLL 要补 CRC 并重编服务端、Z 盘只是部署副本、 gmweb 只能 IE11、静态 no-store、sys.executable 陷阱、不提交产物、版本号三处一致、声明与 GPL - 3 构建命令(服务端三个 exe / 启动器单文件 / 客户端补丁 / PVF 导出) - 4 代码地图:server.py 按功能给出行号区间,其余给模块职责 - 5 验证方法:进程端口、启动器日志、CRC verify、diff -rq, 以及本项目一直在用的「静态服务 + 无头浏览器截图/量 rect」手法,附分场景清单 - 6 发版七项清单;7 当前状态;8 提交约定;9 不要做的事 - CLAUDE.md 作为指向 AGENTS.md 的兼容入口;根 README 顶部与文档索引、docs/README 同步挂上 --- AGENTS.md | 236 +++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 10 +++ README.md | 3 + docs/README.md | 2 + 4 files changed, 251 insertions(+) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..74e1353 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,236 @@ +# AGENTS.md + +> **给接续开发的 AI 的入口文件。** 先读完这一页再看代码。 +> 这里只放三样东西:**哪些规则不能破**、**命令怎么跑**、**改完怎么验证**。 +> 背景与完整说明看 [README.md](README.md),历史踩过的坑看 [docs/踩坑记录.md](docs/踩坑记录.md)。 + +--- + +## 0. 三十秒认识这个仓库 + +《地下城与勇士》90 级版本的**本地单机模拟器**,非私服、不联网、不开服。monorepo,四个进程协作: + +| 组件 | 目录 | 技术栈 | 端口 / 产物 | +|------|------|--------|------------| +| 整合启动器 | `launcher/` | Python 3.15 **32 位** + pywebview + PyInstaller | `127.0.0.1:19527`,产物 `launcher/dist/DNF90启动器.exe` | +| 服务端 | `server/` | Go 1.26.5(模块 `longheng.io/server`) | `127.0.0.1:18111`(Admin),产物 `DNF90Server.exe` 等三个 | +| 游戏内 GM 控制台 | `gmweb/` | 纯 HTML/CSS/JS + Python 标准库 `serve.py` | `127.0.0.1:18112`,反代 18111 并注入 token | +| 客户端补丁 | `client-patch/` | C++ / MSVC vcxproj | 产物 `90CN.dll`,接管游戏面板弹 WebView2 窗口 | + +其余:`pvf/`(PVF 解析,Go,GPL-3.0)、`loadout-tool/`(换装工具,C#)、`gmtool/`(旧版 GM 工具,仅参考)。 +**仓库只存源码**:exe / dll / 美术 / PVF 资源都不在库里,成品走网盘。 + +当前版本 **v2026.11.05**,远程 `origin` = `https://git.hanfuit.com/Lingbao/90CNServer.git`,分支 `main`。 + +--- + +## 1. 开工前必读(按顺序,别跳) + +1. 本文件(硬规则 + 验证方法) +2. [docs/踩坑记录.md](docs/踩坑记录.md) —— **至少读 P-001 / P-002 / P-011**,这三条是「不知道就会重新踩一遍」的 +3. 然后按要改的地方选读: + +| 要改什么 | 读哪份 | +|---------|--------| +| 启动器 / GM 工具 / 打包 | [docs/开发文档.md](docs/开发文档.md) | +| 服务端 GM 接口、拍卖行、背包 | [docs/服务端开发文档.md](docs/服务端开发文档.md) | +| 客户端补丁 90CN.dll、CRC 自校验 | [docs/客户端补丁与CRC校验.md](docs/客户端补丁与CRC校验.md) | +| PVF 解析、离线物品名 | [docs/PVF解析开发文档.md](docs/PVF解析开发文档.md) | +| 发版 | [docs/release/README.md](docs/release/README.md) + [发布说明模板](docs/release/发布说明模板.md) | + +--- + +## 2. 硬规则(不是代码风格,违反了就出故障) + +**R1|`90CN.dll` 与 `DNF90Server.exe` 是一对,必须同批构建、同批发布。** +服务端编译期把 DLL 的 SHA-256 写进常量 `boundDllSHA256`,DLL 一换指纹就不配套, +客户端能启动但**登录阶段被踢**,服务端日志没有明显报错。只改其中一半是最常见的翻车方式。 + +**R2|改完 DLL 必须先补 CRC32,再重编服务端。** +`client-patch/tools/build_90cn.ps1` 第三步会自动补;带 `-SkipCrc` 出来的 DLL 不要发布。 +自校验失败的表现是客户端弹框退出(详见 P-003 / P-004:CRC 种子别手填、槽位只在 `.data`/`.rdata` 扫)。 + +**R3|仓库是唯一真源,`Z:\90server` 只是部署副本。** +调试可以直接在部署目录改,但**改完必须回灌仓库**,并用 `diff -rq` 核对(P-011 就是这么漏过 +`90CN_web.ini` 和整个 `gmweb/` 的)。提交前 `git status` 确认没有该进的 ini / 静态资源留在外面。 + +**R4|`gmweb/` 只能写 IE11 兼容代码。** +游戏内嵌的是老 WebView 控件:不用 flex、不用 CSS 变量、不用 `transform`。布局统一 +`float` + `box-sizing:border-box`;垂直居中用 `position:absolute; top:50%` + `-ms-transform:translateY(-50%)`。 +改完 CSS/JS **必须把 `?v=YYYYMMDDx` 加一版**,否则 IE11 缓存不刷新。 + +**R5|静态响应统一 `Cache-Control: no-store`。** +启动器侧已在 `server.py` 的 `_send` 里加,新增路由时别绕过它;否则 WebView2 会缓存旧页面, +现象是「改了没生效」,会浪费大量时间。 + +**R6|单文件打包会改变 `sys.executable` 的含义。** +源码运行时它是 `python.exe`,`--onefile` 打包后**是启动器自己**。任何 `Popen([sys.executable, ...])` +的地方打包前都要重新推一遍;拉起子进程后要**校验副作用**(端口真在监听)而不是校验返回值。 +这就是 P-001 的整条成因链,别再犯。 + +**R7|不提交编译产物。** +`*.exe`、`*.dll`、`launcher/dist/`、`launcher/build/`、`server/build_verify/`、`__pycache__/`、 +`launcher/dnf_items.json`、`*.pvf`、`*.log` 已在 `.gitignore`。不要为了「方便别人」把它们塞进库。 + +**R8|版本号三处一致**:`launcher/server.py` 的 `CURRENT_VERSION`、发布包根目录 `更新日志.txt`、 +客户端的版本显示。改一漏二会出现「首页还是旧版本号」。 + +**R9|品牌与声明不能动。** +「本模拟器资源禁止倒卖,请到官方微信公众号或社群下载」「禁止用于开服」这类声明必须保留; +`pvf/` 源自 pvfine,**GPL-3.0 有传染性**,改它之前先确认许可证影响。 + +--- + +## 3. 环境与构建 + +作者机器上的路径(其他环境自行替换,逻辑不依赖盘符): + +``` +Python 3.15 32 位 C:\Python315\python.exe # 32 位是硬要求,客户端/工具链都是 32 位 +Go ≥ 1.26.5 +MSVC Visual Studio(client-patch 用 vcxproj) +部署目录 Z:\90server # 只是副本 +桌面发布包 90CN灵宝小竹整合20261105更新补丁\ +``` + +```bash +# 服务端(三个 exe) +cd server +go env -w GOPROXY=https://goproxy.cn,direct +go env -w GOSUMDB=sum.golang.google.cn +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 + +# 启动器(单文件) +cd launcher +C:/Python315/python.exe -m pip install pywebview pythonnet clr-loader pyinstaller +C:/Python315/python.exe -m PyInstaller --clean --noconfirm "DNF90启动器.spec" +# → launcher/dist/DNF90启动器.exe + +# 客户端补丁(Release|Win32,自动补 CRC) +client-patch\tools\build_90cn.bat + +# PVF 物品名导出 +cd pvf && go build ./cmd/pvfexport/ && pvfexport -pvf Script.pvf -out dnf_items.json -compact +``` + +--- + +## 4. 代码地图 + +``` +launcher/ +├── server.py 2809 行,本地 HTTP 服务 + 全部 GM 逻辑 + 窗口启动(核心,占 80% 工作量) +│ ├── 202-360 日志(_logf) / 单实例互斥 / 多调用模式 run_script_instead() +│ ├── 369-430 start_gmweb():拉起 18112 + 就绪校验 + 进程内兜底 ← 高危区,见 P-001 +│ ├── 551-686 更新日志:仓库 Release + 本地 txt 合并去重 +│ └── 2481-2800 main() / webview2_installed() / show_window() / 窗口清理 +├── index.html 234 行,六页骨架(开始/GM工具/一键换装/游戏设置/游戏更新/关于我们) +├── shell.js 804 行,页面路由、更新日志渲染、二维码灯箱 +├── shell.css 447 行,页面样式(WebView2 环境,可用现代 CSS) +└── qr/ 四张二维码:alipay.png / wechat.png / gzh.jpg / qq.png + +gmweb/ 游戏内控制台,共约 1100 行 +├── serve.py 187 行,静态托管 + /api/* 反代 18111 + 注入 admin token + /qr/ 白名单 +├── index.html / style.css / app.js IE11 约束,别用现代语法 +└── qr/ 同上四张 + +client-patch/ +├── 90CN.cpp 客户端补丁主体(极大,改前先搜函数名定位) +├── 90CN_web.ini width=800 height=720 close_action=1 ← 内嵌 GM 页尺寸,改这里不是改代码 +└── tools/ build_90cn.ps1(构建+补CRC)/ patch_dll_crc.py(info|verify|patch) + +server/cmd/server/ dnf90(主体)/ control(控制台)/ doctor(自检)/ launcher / logtool / 等 +``` + +--- + +## 5. 改完怎么验证(**别只看代码,必须跑**) + +```bash +# 进程与端口(改启动器后必查:正常只有 19527 + 18112 两个监听) +tasklist | findstr DNF90 +netstat -ano | findstr "127.0.0.1:19527 127.0.0.1:18112 127.0.0.1:18111" + +# 启动器现场日志(出问题先看这个,比猜快得多) +type 启动器日志.txt # exe 同目录;应有 pid / argv / 端口 / 多调用模式 + +# DLL 自校验状态 +C:/Python315/python.exe client-patch/tools/patch_dll_crc.py verify Z:/90server/Client/90CN.dll +C:/Python315/python.exe client-patch/tools/patch_dll_crc.py info Z:/90server/Client/90CN.dll + +# 仓库 ↔ 部署目录一致性 +diff -rq gmweb Z:/90server/gmweb +diff -q client-patch/90CN_web.ini Z:/90server/Client/90CN_web.ini +``` + +**看界面的推荐做法**(比肉眼读像素可靠,本项目一直这么验): + +```bash +# 1) 起静态服务;2) 注入脚本切到目标页;3) 无头浏览器截图/量尺寸 +cd gmweb && C:/Python315/python.exe -m http.server 18198 +# 页面里注入:document.getElementById('pane-about').className='pane on'; +# (启动器则是给对应 .shell-page 加 active) +msedge --headless=new --disable-gpu --hide-scrollbars --window-size=800,720 \ + --screenshot=shot.png --virtual-time-budget=3000 http://127.0.0.1:18198/_t.html +# 量尺寸:把 getBoundingClientRect 结果写进 document.title,再用 --dump-dom | grep title +``` + +尺寸口径:GM 页内嵌窗口 **800×720**;启动器窗口 **1204×717**。改完两边都看一眼有没有滚动条。 + +**分场景清单** + +| 改了什么 | 必须确认 | +|---------|---------| +| 启动器页面/样式 | 打包 → 杀旧进程 → 启动 → 六页都能切、无滚动条、`启动器日志.txt` 无异常 | +| 启动器 Python 逻辑 | 先跑源码再打包:`cd launcher && DNF90_SERVER_ROOT=Z:/90server DNF90_NO_ELEVATE=1 python server.py`(`SERVER_ROOT` 靠 EXE 同级有 `Server/` 识别,脱离部署目录时用环境变量指定);确认没有新增 `sys.executable` 陷阱 | +| gmweb 页面 | `?v=` 加一版;800×720 截图三列/四页无溢出;`/api/*` 仍能通 | +| 服务端接口 | `go build` 通过;`DNF90Doctor.exe` 自检;启动器「GM工具」页能读到数据 | +| 客户端补丁 | `build_90cn.bat` 全流程(含补 CRC)→ **重编服务端** → 两边一起替换 → 登录验证 | +| 发版 | 按 [docs/release/README.md](docs/release/README.md) 七步走,见第 6 节清单 | + +--- + +## 6. 发版检查清单(缺一项就会有人装不上) + +- [ ] `Client\90CN.dll` 与 `Server\runtime\bin\DNF90Server.exe` **成对且指纹一致** +- [ ] `gmweb\` 整个目录(含 `qr\` 四张图) +- [ ] `Client\90CN_web.ini`(缺它回退 1040 宽、`close_action=0`) +- [ ] 启动器 exe + `更新日志.txt` +- [ ] 版本号三处一致(`CURRENT_VERSION` / 客户端显示 / 更新日志) +- [ ] **干净目录**(非中文、无空格)实跑一遍再对外发 +- [ ] Release 说明用模板;附件传 `docs/release/网盘下载.url`(别直传十几 G 的包) + +--- + +## 7. 当前状态(2026-11-05) + +- 版本 `v2026.11.05`;`main` 与 `origin/main` 同步 +- 启动器:六页(开始/GM工具/一键换装/游戏设置/游戏更新[当前|历史]/关于我们),关于页三列二维码 +- gmweb:四页,关于页三列(赞助我们 353 | 官方QQ群 178 | 团队公众号 179,面板宽 712) +- 已修复:启动器打包后自我套娃开窗(P-001),带单实例互斥 + 运行日志 + WebView2 固定数据目录 +- 已知待办:release 附件 `.url` 里的网盘链接换代时,要同步改 `发布说明模板.md` 表格;`gmtool/` 为旧版遗留,可择机归档 + +--- + +## 8. 提交约定 + +1. 提交信息格式:`范围:做了什么`,例如 `启动器:游戏更新拆分为当前/历史双子页,新增关于我们页` +2. 一次提交一件事;改了接口就同步改对应文档(服务端接口 → `docs/服务端开发文档.md`) +3. 只提交源码与文档;产物走 `.gitignore` +4. 新踩的坑,追加到 `docs/踩坑记录.md`(编号延续 P-0xx,格式照抄现有条目) +5. 推送前 `git status -sb` 确认工作区干净、与 `origin/main` 无分叉 + +--- + +## 9. 不要做的事 + +- ❌ 不要「为了省事」只替换 DLL 或只替换服务端 exe —— 见 R1 +- ❌ 不要用 `!important` 硬压 gmweb 的样式问题 —— 先看是哪个选择器赢了(P-005) +- ❌ 不要用现代 CSS/JS 写 `gmweb/` —— 见 R4 +- ❌ 不要把 `Z:\90server` 当工作区改完就完事 —— 见 R3 +- ❌ 不要在启动器里加「打包后会指向自己」的子进程调用而不做校验 —— 见 R6 +- ❌ 不要提交 exe/dll/`dnf_items.json`/PVF 资源 +- ❌ 不要把本机测试用的掉落改动带进对外发布的服务端 +- ❌ 不要公开分发或倒卖本仓库代码与资源(`pvf/` 除外,但受 GPL-3.0 约束) diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..35723f2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,10 @@ +# CLAUDE.md + +本仓库的统一入口是 **[AGENTS.md](AGENTS.md)**(硬规则 R1–R9、构建命令、验证方法、代码地图、发版清单)。 +开工前请先完整读一遍,再按其中的「开工前必读」选读 `docs/` 下的对应文档。 + +三条最容易被忽略、且会直接导致故障的规则: + +1. `90CN.dll` 与 `DNF90Server.exe` 指纹绑定,**必须成对构建、同批发布**(R1/R2) +2. `gmweb/` 是 IE11 环境,**不能用 flex 等现代 CSS**,改完资源要加 `?v=`(R4) +3. 单文件打包后 `sys.executable` 指向启动器自身,**任何拉起子进程的代码都要校验副作用**(R6) diff --git a/README.md b/README.md index 8048a5f..f9e59df 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ > 本模拟器资源禁止倒卖,请到官方微信公众号或社群下载。 +> **用 AI 接续开发?先读 [AGENTS.md](AGENTS.md)** —— 硬规则、构建命令、验证方法、代码地图一页看完。 + --- ## 一、它由哪几块组成 @@ -188,6 +190,7 @@ pvfexport -pvf Script.pvf -out dnf_items.json -compact | 文档 | 内容 | |------|------| +| [AGENTS.md](AGENTS.md) | **AI / 新人接续开发入口**:硬规则 R1–R9、构建与验证命令、代码地图、发版清单 | | [docs/README.md](docs/README.md) | 文档总索引与上手路径 | | [docs/开发文档.md](docs/开发文档.md) | 启动器 / GM 工具:环境、架构、API 契约、打包部署、常见问题 | | [docs/服务端开发文档.md](docs/服务端开发文档.md) | 服务端架构、GM 接口契约、拍卖行与背包改动 | diff --git a/docs/README.md b/docs/README.md index 4130a50..960116d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,6 +4,7 @@ | 文档 | 内容 | |------|------| +| [../AGENTS.md](../AGENTS.md) | **入口文件**:给 AI / 新人的一页纸——硬规则、构建命令、验证方法、代码地图、发版清单(开工前先读这份) | | [开发文档.md](开发文档.md) | **GM 启动器 / 整合工具** 完整开发文档(环境搭建、架构、API 契约、打包部署、常见问题) | | [服务端开发文档.md](服务端开发文档.md) | **服务端(Go)** 架构、GM 接口契约、拍卖行/背包改动、编译部署 | | [PVF解析开发文档.md](PVF解析开发文档.md) | **PVF 解析** 套件(pvfine)、pvfexport 工具、离线名称显示集成 | @@ -22,6 +23,7 @@ ## 快速上手路径 +0. **任何人(含 AI)接手**:先读 [../AGENTS.md](../AGENTS.md),再回来按场景选文档。 1. **只想跑起来**:看根目录 [README](../README.md) 的「快速开始」。 2. **改 GM 工具(启动器)**:看 [开发文档.md](开发文档.md)。 3. **改服务端 GM 接口**:看 [服务端开发文档.md](服务端开发文档.md) 的「GM 接口契约」。