Files
90CNServer/AGENTS.md
T
hanfuit 3b240ec176 docs:新增 AGENTS.md 作为 AI / 新人接续开发的入口文件
一页纸解决「接手时不知道从哪读、哪些不能碰、改完怎么验」:
- 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 同步挂上
2026-11-05 17:40:13 +08:00

237 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/<name> 白名单
├── 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 约束)