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

13 KiB
Raw Permalink Blame History

AGENTS.md

给接续开发的 AI 的入口文件。 先读完这一页再看代码。 这里只放三样东西:哪些规则不能破、命令怎么跑、改完怎么验证。 背景与完整说明看 README.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 —— 至少读 P-001 / P-002 / P-011,这三条是「不知道就会重新踩一遍」的
  3. 然后按要改的地方选读:
要改什么 读哪份
启动器 / GM 工具 / 打包 docs/开发文档.md
服务端 GM 接口、拍卖行、背包 docs/服务端开发文档.md
客户端补丁 90CN.dll、CRC 自校验 docs/客户端补丁与CRC校验.md
PVF 解析、离线物品名 docs/PVF解析开发文档.md
发版 docs/release/README.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更新补丁\
# 服务端(三个 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. 改完怎么验证(别只看代码,必须跑)

# 进程与端口(改启动器后必查:正常只有 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

看界面的推荐做法(比肉眼读像素可靠,本项目一直这么验):

# 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 七步走,见第 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 约束)