DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
This commit is contained in:
commit
645b64fe21
4388 files changed
+1405457
No files matched your search
@@ -0,0 +1,129 @@
|
||||
# PVF 解析套件开发文档
|
||||
|
||||
> 覆盖 PVF 解析库(pvfine)、物品名称表导出工具(pvfexport)、以及与 GM 工具的离线名称显示集成。
|
||||
>
|
||||
> 最后更新:2026-11-03
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
DNF 的 `Script.pvf` 是专有的二进制容器(加密 + 压缩),物品名称(`.equ`/`.stk` 的 `[name]` 段)只存在于 PVF 内。为了让 GM 工具**离线(服务端关闭)也能显示物品中文名**,我们引入了一个 PVF 解析方案。
|
||||
|
||||
---
|
||||
|
||||
## 二、pvfine 项目(`pvf/`)
|
||||
|
||||
`pvf/` 是开源项目 **pvfine**(GPL-3.0)的核心源码,一个 Go 编写的 PVF 编辑器/解析套件,支持 86/90/100/110/115/TW 全版本 `Script.pvf`。
|
||||
|
||||
### 结构
|
||||
|
||||
```
|
||||
pvf/
|
||||
├── internal/pvf/ 核心解析包(Open / Find / Text / ScriptMetadata / ParseListText)
|
||||
├── internal/ani/ buildmode/ rendering/ ... pvf 依赖的其他内部包
|
||||
├── internal/mcpserver/ npk/ mod/ script/ ... 编辑器相关
|
||||
├── config/ go:embed 的索引数据(paths.json 等,编译必需)
|
||||
├── cmd/
|
||||
│ ├── annotation-editor/ 注解编辑器
|
||||
│ └── pvfexport/ 物品名称表导出工具(本仓库新增)
|
||||
├── services/ frontend/ 编辑器后端/前端
|
||||
├── main.go 桌面 GUI 入口(Wails)
|
||||
└── go.mod module pvfine, go 1.26.0
|
||||
```
|
||||
|
||||
### 核心 API(`internal/pvf`)
|
||||
|
||||
```go
|
||||
import "pvfine/internal/pvf"
|
||||
|
||||
archive, err := pvf.Open("Script.pvf") // 打开归档(自动识别版本)
|
||||
defer archive.Release()
|
||||
|
||||
idx, found := archive.Find("stackable/stackable.lst") // 找文件索引
|
||||
text, err := archive.Text(idx) // 读脚本文本
|
||||
pairs, err := pvf.ParseListText(text) // 解析 .lst -> [{ID, Path}]
|
||||
|
||||
meta, err := archive.ScriptMetadata(idx) // 读物品元数据(Name/Rarity)
|
||||
// meta.Name 即物品名称
|
||||
```
|
||||
|
||||
> 注意:`internal/pvf` 依赖 `internal/ani`、`internal/buildmode`、`internal/rendering`、
|
||||
> `internal/annotations`、`internal/ruleconfig`、`config` 等包,**不能单独抽出**,
|
||||
> 需要以整个 `pvfine` 模块作为依赖(或直接在其中添加 cmd 子命令)。
|
||||
|
||||
---
|
||||
|
||||
## 三、pvfexport 工具(`pvf/cmd/pvfexport/`)
|
||||
|
||||
从 `Script.pvf` 提取 `item_id → 名称` 全量映射,导出 JSON。
|
||||
|
||||
### 编译
|
||||
|
||||
```bash
|
||||
cd pvf
|
||||
go build -o pvfexport.exe ./cmd/pvfexport/
|
||||
```
|
||||
|
||||
### 用法
|
||||
|
||||
```bash
|
||||
pvfexport -pvf Script.pvf -out items.json # 完整 JSON(含 path/rarity/kind)
|
||||
pvfexport -pvf Script.pvf -out dnf_items.json -compact # 紧凑 {item_id: name}(推荐)
|
||||
```
|
||||
|
||||
- `-compact`:只输出 `{item_id: name}` 映射(仅保留有名称的项),体积约 5.7MB(14.6 万条)。
|
||||
- 完整模式:输出 `{pvf_path, total, items:[{id,name,rarity,path,kind}]}`,约 35MB。
|
||||
|
||||
### 实现逻辑
|
||||
|
||||
1. `pvf.Open` 打开归档。
|
||||
2. 读 `stackable/stackable.lst`(堆叠道具)和 `equipment/equipment.lst`(装备),用 `ParseListText` 解析出 `item_id → 路径`。
|
||||
3. 对每个 item,用 `resolveItemFile` 找文件(路径候选策略复刻服务端 `readDungeonDropText`):
|
||||
- 直接用 lst 里的路径
|
||||
- 尝试 `root/` 前缀(`stackable/` / `equipment/`)
|
||||
- 尝试 `(r)` 修订文件名(90 版本特有)
|
||||
4. `ScriptMetadata` 读 `[name]` 段得到名称。
|
||||
|
||||
---
|
||||
|
||||
## 四、与 GM 工具的集成(离线名称显示)
|
||||
|
||||
### 名称表存放
|
||||
|
||||
名称表 `dnf_items.json` 与 `pvfexport.exe` 放在服务端根目录下的 **`GM配置/`** 目录:
|
||||
|
||||
```
|
||||
90server/
|
||||
└── GM配置/
|
||||
├── pvfexport.exe
|
||||
└── dnf_items.json
|
||||
```
|
||||
|
||||
### 自动重导出
|
||||
|
||||
GM 工具(launcher)启动时,`db.py` 的 `_load_item_names()` 会:
|
||||
|
||||
1. 定位 `GM配置/dnf_items.json` 和 `Client/Script.pvf`。
|
||||
2. 对比两者修改时间:
|
||||
- 名称表**已是最新** → 秒加载(约 0.09 秒)。
|
||||
- **PVF 被更新**(或名称表缺失)→ 自动调 `pvfexport.exe -compact` 重导出(约 5 秒),再加载。
|
||||
3. 加 `_EXPORT_LOCK` 锁防并发重复触发。
|
||||
|
||||
> 因此:**PVF 新增物品后,只需替换 `Client/Script.pvf`,GM 工具下次启动自动重导名称表,
|
||||
> 无需重新打包 GM 工具。**
|
||||
|
||||
### 注入方式
|
||||
|
||||
`server.py` 启动时调用 `db.set_gm_config(GM_CONFIG_DIR, PVF_PATH)` 注入目录与 PVF 路径;
|
||||
`db.py` 的 `item_name(item_id)` 据此惰性加载名称表。
|
||||
|
||||
---
|
||||
|
||||
## 五、许可证与合规
|
||||
|
||||
- `pvf/` 遵循 **GPL-3.0**(见 `pvf/LICENSE`)。
|
||||
- 若将 pvfine 与闭源的 server/launcher 合并发布,需注意 **GPL 传染性**:
|
||||
- 单独分发 pvfexport(独立 Go 进程)可隔离传染;
|
||||
- 若将 `internal/pvf` 直接 link 进闭源服务端,会触发 GPL 义务。
|
||||
- 建议:保持 pvfine 作为**独立子模块/子仓库**,通过 CLI 工具(pvfexport)与主项目解耦。
|
||||
@@ -0,0 +1,25 @@
|
||||
# 开发文档索引
|
||||
|
||||
本目录汇总各子项目的开发文档。
|
||||
|
||||
| 文档 | 内容 |
|
||||
|------|------|
|
||||
| [开发文档.md](开发文档.md) | **GM 启动器 / 整合工具** 完整开发文档(环境搭建、架构、API 契约、打包部署、常见问题) |
|
||||
| [服务端开发文档.md](服务端开发文档.md) | **服务端(Go)** 架构、GM 接口契约、拍卖行/背包改动、编译部署 |
|
||||
| [PVF解析开发文档.md](PVF解析开发文档.md) | **PVF 解析** 套件(pvfine)、pvfexport 工具、离线名称显示集成 |
|
||||
| [launcher-README.md](launcher-README.md) | 整合启动器 + GM 工具的原始 README |
|
||||
|
||||
其余子项目的 README 分散在各自目录:
|
||||
|
||||
- `server/README.md` — 服务端源码说明
|
||||
- `pvf/README.md` — pvfine 项目说明
|
||||
- `gmtool/README.md` — GM 工具旧版说明
|
||||
|
||||
---
|
||||
|
||||
## 快速上手路径
|
||||
|
||||
1. **只想跑起来**:看根目录 [README](../README.md) 的「快速开始」。
|
||||
2. **改 GM 工具(启动器)**:看 [开发文档.md](开发文档.md)。
|
||||
3. **改服务端 GM 接口**:看 [服务端开发文档.md](服务端开发文档.md) 的「GM 接口契约」。
|
||||
4. **改 PVF 解析 / 物品名**:看 [PVF解析开发文档.md](PVF解析开发文档.md)。
|
||||
@@ -0,0 +1,95 @@
|
||||
# DNF90 单机模拟器 · 整合启动器 + GM 管理工具
|
||||
|
||||
一个**单 EXE** 的精美启动器,内置完整的 GM 管理工具(角色管理、删角色、删档、无限疲劳、道具发放等)。
|
||||
|
||||
## 一、交付成果
|
||||
|
||||
### 1. 单 EXE 整合启动器(`launcher/dist/DNF90启动器.exe`)
|
||||
|
||||
纯 Python + pywebview 内嵌窗口,PyInstaller 打包成单文件 EXE(14MB),双击即用。
|
||||
|
||||
**窗口化界面**:不再打开浏览器,而是直接弹出一个独立的桌面窗口(基于 Edge WebView2 渲染),
|
||||
界面就是截图里的那个登录器样式。
|
||||
|
||||
**启动器主界面(4 个按钮)**:
|
||||
- ▶ **开始游戏**:configure-client → 启动服务端 → 注册账号 → 自动登录进游戏
|
||||
- ⚙ **打开 GM**:切换到内置 GM 管理工具界面(不再打开独立 exe)
|
||||
- ■ **关闭服务**:安全停止服务端
|
||||
- ♥ **赞助我们**:弹出赞助二维码
|
||||
|
||||
**内置 GM 工具(点击「打开 GM」进入)**,包含 10 个功能页:
|
||||
- 📊 控制台(服务端/数据库/Native API 状态总览)
|
||||
- 👥 角色管理(查看/编辑等级、金币、职业,含**无限疲劳**、**删除角色**)
|
||||
- 🎁 道具发放(发放道具到背包)
|
||||
- ✉️ 邮件系统 · 🎒 背包装备
|
||||
- 📜 任务管理 · ⚔️ 副本控制 · 💰 钱包点券
|
||||
- 🗑️ 删档管理(删除角色/删除账号)
|
||||
- ⚙️ 连接设置(SQLite / Native 数据源切换)
|
||||
|
||||
### 2. 界面特色
|
||||
|
||||
深色 DNF 风格 + 紫青渐变 + 毛玻璃质感 + 动画光斑 + 危险操作二次确认(输入 DELETE)。
|
||||
|
||||
## 二、技术原理
|
||||
|
||||
### 服务端架构
|
||||
- **DNF90 单机模拟器**(非私服),单账号本地模式
|
||||
- 核心控制:`Server/runtime/bin/DNF90Control.exe`(start/stop/configure-client/launch-client/account)
|
||||
- 数据库:**SQLite**,位于 `Server/runtime/data/dnf90.db`(首次启动服务端时创建)
|
||||
|
||||
### GM 工具双数据源
|
||||
- **Native API**:服务端运行时(`127.0.0.1:18111` + `X-Admin-Token`),实时操作
|
||||
- **SQLite 直读**:服务端停止时直接读写数据库,可删角色/删档/改疲劳
|
||||
|
||||
### 关键功能实现
|
||||
- **无限疲劳**:把 `stats` 里的 `fatigue` 和 `fatigue_limit` 都设为超大值(999999)
|
||||
- **删除角色**:直接删除 SQLite 中 `character`/`equipment`/`inventory`/`mailbox`/`quest`/`skill` 等记录,**需先停止服务端**
|
||||
|
||||
## 三、使用方法
|
||||
|
||||
### 直接运行
|
||||
```
|
||||
双击 DNF90启动器.exe
|
||||
```
|
||||
> 首次运行会请求管理员权限(UAC),因为启动服务端需要管理员权限。
|
||||
|
||||
### 部署位置
|
||||
EXE 需放在 `Z:\90server` 目录下(或旁边),会自动向上查找 `Server/` 和 `Client/` 目录。若找不到会 fallback 到 `Z:\90server`。
|
||||
|
||||
### 赞助二维码
|
||||
`sponsor.png` 已打包进 EXE。如需更换,替换源码目录的 `sponsor.png` 后重新打包即可。
|
||||
|
||||
## 四、源码结构
|
||||
|
||||
```
|
||||
launcher/
|
||||
├── server.py # 整合后端(启动器 + GM 工具,纯标准库)
|
||||
├── db.py # SQLite 数据访问层(删角色/删档/疲劳)
|
||||
├── native.py # Native API 客户端(完整 GM 端点)
|
||||
├── index.html # 双视图界面(启动器 + GM 工具)
|
||||
├── style.css # 统一精美深色主题
|
||||
├── app.js # 前端逻辑(启动器 + GM 路由)
|
||||
├── sponsor.png # 赞助二维码
|
||||
└── dist/
|
||||
└── DNF90启动器.exe # 打包产物
|
||||
```
|
||||
|
||||
## 五、重新打包
|
||||
|
||||
```bash
|
||||
cd launcher
|
||||
python -m PyInstaller --onefile --noconsole --name "DNF90启动器" \
|
||||
--add-data "index.html;." --add-data "style.css;." \
|
||||
--add-data "app.js;." --add-data "sponsor.png;." \
|
||||
--add-data "db.py;." --add-data "native.py;." \
|
||||
--collect-all webview --hidden-import clr_loader --hidden-import pythonnet \
|
||||
server.py
|
||||
```
|
||||
|
||||
## 六、重要提示
|
||||
|
||||
1. **删除角色/删档需停服**:先点「关闭服务」再操作,否则数据库被锁定
|
||||
2. **改疲劳需停服**:疲劳值只能 SQLite 离线直改
|
||||
3. **管理员权限**:启动服务端需要管理员权限
|
||||
4. **数据库首次创建**:首次「开始游戏」后,数据库才会在 `Server/runtime/data/dnf90.db` 生成
|
||||
5. **依赖 WebView2**:内嵌窗口基于 Windows 自带的 Edge WebView2 运行时(Win10/11 已预装);若系统缺失会自动降级为打开浏览器
|
||||
+586
@@ -0,0 +1,586 @@
|
||||
# DNF90 单机模拟器 · 整合启动器 + GM 工具 开发文档
|
||||
|
||||
> 本文档用于**换机后快速恢复开发环境**,覆盖环境搭建、项目结构、架构原理、接口清单、打包部署、常见问题等全部关键信息。
|
||||
>
|
||||
> 最后更新:2026-11-03
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [项目概述](#一项目概述)
|
||||
2. [开发环境搭建](#二开发环境搭建)
|
||||
3. [项目结构与关键文件](#三项目结构与关键文件)
|
||||
4. [技术架构与原理](#四技术架构与原理)
|
||||
5. [服务端 API 契约](#五服务端-api-契约)
|
||||
6. [启动器后端 API 清单](#六启动器后端-api-清单)
|
||||
7. [关键业务逻辑与约定](#七关键业务逻辑与约定)
|
||||
8. [打包与部署](#八打包与部署)
|
||||
9. [参考资料位置](#九参考资料位置)
|
||||
10. [常见问题与坑](#十常见问题与坑)
|
||||
|
||||
---
|
||||
|
||||
## 一、项目概述
|
||||
|
||||
一个**单 EXE** 的 DNF90 单机模拟器启动器,内置完整的 GM 管理工具。
|
||||
|
||||
- **技术栈**:Python 3.15 + pywebview 6.x(WebView2 内嵌窗口)+ 纯标准库 HTTP 后端 + 原生 HTML/CSS/JS 前端
|
||||
- **交付物**:`launcher/dist/DNF90启动器.exe`(单文件,约 14.57MB)
|
||||
- **部署位置**:EXE 始终放在 `Z:\90server\` 根目录(服务端目录内)
|
||||
|
||||
### 核心功能
|
||||
|
||||
**启动器主界面(4 按钮)**:
|
||||
- ▶ 开始游戏:configure-client → 启动服务端 → 注册账号 → 自动登录进游戏
|
||||
- ⚙ 打开 GM:切换到内置 GM 管理工具界面
|
||||
- ■ 关闭服务:安全停止服务端
|
||||
- ♥ 赞助我们:弹出赞助二维码
|
||||
- 🔄 一键换装:启动 LoadoutTool/DNFLoadout.exe
|
||||
|
||||
**内置 GM 工具(功能页)**:
|
||||
- 控制台(服务端/数据库/Native API 状态)
|
||||
- 角色管理(等级/金币/职业转职/无限疲劳/删除角色)
|
||||
- 道具发放(装备/道具两分区 + 武器子类型筛选 + 品级筛选)
|
||||
- 邮件系统 / 背包装备 / 任务管理 / 副本控制 / 钱包点券(含黑钻)
|
||||
- 删档管理 / 连接设置(SQLite / Native 数据源切换)
|
||||
|
||||
---
|
||||
|
||||
## 二、开发环境搭建
|
||||
|
||||
### 2.1 Python 环境
|
||||
|
||||
**本机有两个 Python,务必使用 Python 3.15:**
|
||||
|
||||
| Python | 路径 | 用途 |
|
||||
|--------|------|------|
|
||||
| **Python 3.15.2** | `C:\Python315-32\python.exe` | **主开发环境(必须用这个)** |
|
||||
| Python 3.13.14 | `python`(PATH 默认) | WorkBuddy 内置,勿用于本项目 |
|
||||
|
||||
> ⚠️ 所有命令用 `C:\Python315-32\python.exe`,不要用裸 `python`(会指向 3.13)。
|
||||
|
||||
### 2.2 依赖包安装
|
||||
|
||||
```bash
|
||||
# 切到 Python 3.15
|
||||
C:/Python315-32/python.exe -m pip install pywebview pythonnet clr-loader pyinstaller pillow requests fastapi
|
||||
```
|
||||
|
||||
关键依赖及版本:
|
||||
|
||||
| 包 | 版本 | 用途 |
|
||||
|----|------|------|
|
||||
| pywebview | 6.2.1 | 内嵌 WebView2 窗口 |
|
||||
| pythonnet | 3.2.1 | .NET CLR 互操作(pywebview 依赖) |
|
||||
| clr-loader | (latest) | pythonnet 的 CLR 加载器 |
|
||||
| pyinstaller | 6.22.3 | 打包单 EXE |
|
||||
| pillow | 13.0.0 | 二维码裁剪处理 |
|
||||
| requests | 2.34.2 | Native API 客户端(native.py 用) |
|
||||
| fastapi | 0.162.0 | 仅 gmtool 旧版用(launcher 主项目不用) |
|
||||
|
||||
### 2.3 pip 国内源(换机后需重新配置)
|
||||
|
||||
`C:\Users\Administrator\AppData\Roaming\pip\pip.ini`:
|
||||
|
||||
```ini
|
||||
[global]
|
||||
index-url = https://pypi.tuna.tsinghua.edu.cn/simple
|
||||
extra-index-url = https://mirrors.aliyun.com/pypi/simple/
|
||||
trusted-host = pypi.tuna.tsinghua.edu.cn
|
||||
```
|
||||
|
||||
### 2.4 换机后检查清单
|
||||
|
||||
- [ ] 安装 Python 3.15(任意位置,更新 `.bat` 和打包命令里的路径)
|
||||
- [ ] 配置 pip 清华源
|
||||
- [ ] 安装上述依赖包
|
||||
- [ ] 确认目标电脑有 **WebView2 运行时**(Win10/11 自带;Win7 需手动装)
|
||||
- [ ] 将整个项目目录拷贝到新机(含 `launcher/` 源码和 `90server/` 运行环境)
|
||||
|
||||
---
|
||||
|
||||
## 三、项目结构与关键文件
|
||||
|
||||
### 3.1 工作区目录(开发源码所在地)
|
||||
|
||||
```
|
||||
C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/
|
||||
├── launcher/ # ★ 主项目(启动器 + GM 工具)
|
||||
│ ├── server.py # ★ 整合后端(启动器动作 + GM HTTP 服务,纯标准库)
|
||||
│ ├── native.py # ★ Native API 客户端(封装服务端全部 GM 端点)
|
||||
│ ├── db.py # ★ SQLite 数据访问层(删角色/删档/疲劳)
|
||||
│ ├── index.html # 双视图界面(启动器 + GM 工具)
|
||||
│ ├── style.css # 深色 DNF 风格主题
|
||||
│ ├── app.js # ★ 前端逻辑(启动器 + GM 路由)
|
||||
│ ├── sponsor.png # 赞助二维码(已裁剪)
|
||||
│ ├── sponsor_full_backup.png# 二维码原图备份
|
||||
│ ├── sponsor_qr.png # 二维码中间产物
|
||||
│ ├── DNF90启动器.spec # ★ PyInstaller 打包配置
|
||||
│ ├── build/ # 打包中间产物(可删)
|
||||
│ └── dist/ # 打包产物 DNF90启动器.exe
|
||||
├── gmtool/ # 旧版 FastAPI GM 工具(已弃用,仅参考)
|
||||
│ ├── main.py
|
||||
│ ├── gm_client.py
|
||||
│ └── static/
|
||||
├── README.md # 项目简介(较简略)
|
||||
├── 开发文档.md # 本文档
|
||||
├── 2026-11-03.md # 当日详细工作日志(历史记录)
|
||||
├── 启动启动器.bat # 源码方式运行启动器
|
||||
├── 启动GM工具.bat # 源码方式运行旧 GM 工具(已弃用)
|
||||
└── .workbuddy/ # WorkBuddy 项目数据(勿删)
|
||||
└── memory/ # 工作记忆(日志)
|
||||
```
|
||||
|
||||
### 3.2 运行环境(服务端所在,非源码)
|
||||
|
||||
```
|
||||
Z:/90server/ # ★ 可运行模拟器 + 启动器部署位置
|
||||
├── DNF90启动器.exe # ★ 打包后的启动器(放这里运行)
|
||||
├── Server/ # 服务端
|
||||
│ └── runtime/
|
||||
│ ├── bin/DNF90Control.exe # 服务端控制器(start/stop/configure-client/launch-client/account)
|
||||
│ ├── config/instance.json # Native API token 配置
|
||||
│ └── data/dnf90.db # SQLite 数据库(首次启动生成)
|
||||
├── Client/ # 游戏客户端
|
||||
└── LoadoutTool/DNFLoadout.exe # 一键换装工具
|
||||
```
|
||||
|
||||
### 3.3 服务端源码(Go,仅供参考 API 契约)
|
||||
|
||||
```
|
||||
C:/Users/Administrator/Downloads/90轮回源码/90轮回源码/go-server/
|
||||
└── internal/
|
||||
├── app/dnf90/
|
||||
│ ├── gm_tool_admin.go # ★ GM 工具 HTTP 路由(GET 查询类)
|
||||
│ └── gm_admin.go # ★ 黑钻等写操作
|
||||
├── services/dnfbridge/
|
||||
│ ├── gm_tool.go # GM 业务逻辑(职业/任务结构定义)
|
||||
│ ├── gm_tool_extended.go # 角色详情、分类逻辑
|
||||
│ ├── gm_tool_items_extra.go# 道具发放 grant-options
|
||||
│ ├── gm_tool_dungeon.go # 副本难度
|
||||
│ └── gm_tool_item_customization.go
|
||||
└── modules/dnf/
|
||||
├── itemquality/quality.go # 品级定义
|
||||
└── profession/profile.go # 职业 Option 结构
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、技术架构与原理
|
||||
|
||||
### 4.1 整体架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ DNF90启动器.exe(单文件,pywebview WebView2) │
|
||||
│ │
|
||||
│ ┌──────────────┐ HTTP(127.0.0.1:19527) │
|
||||
│ │ 前端 app.js │ ◄──────────────────────────► │
|
||||
│ │ index.html │ │
|
||||
│ └──────────────┘ │
|
||||
│ ▲ │
|
||||
│ │ api()/post() │
|
||||
│ ┌───────┴────────────────────────────────────┐ │
|
||||
│ │ server.py(纯标准库 BaseHTTPRequestHandler)│ │
|
||||
│ │ ├─ 启动器动作 flow_*() │ │
|
||||
│ │ ├─ GM HTTP 路由 _api_*() │ │
|
||||
│ │ ├─ SQLite 直读(DB, db.py) │ │
|
||||
│ │ └─ Native 客户端(NATIVE, native.py) │ │
|
||||
│ └───────┬──────────────────┬─────────────────┘ │
|
||||
└──────────┼──────────────────┼───────────────────┘
|
||||
│ SQLite 直读 │ HTTP(127.0.0.1:18111, X-Admin-Token)
|
||||
▼ ▼
|
||||
Server/runtime/data/dnf90.db DNF90Control.exe(运行中的服务端)
|
||||
```
|
||||
|
||||
### 4.2 双数据源机制(核心设计)
|
||||
|
||||
GM 工具支持两种数据源,通过「连接设置」页切换:
|
||||
|
||||
| 数据源 | 条件 | 特点 |
|
||||
|--------|------|------|
|
||||
| **Native API** | 服务端运行时 | 实时操作,`127.0.0.1:18111` + `X-Admin-Token` |
|
||||
| **SQLite 直读** | 服务端停止时 | 直接读写 `dnf90.db`,可删角色/删档/改疲劳 |
|
||||
|
||||
- 判断逻辑:`_use_sqlite()` 返回 `True` 时走 SQLite,否则走 Native
|
||||
- **删角色/删档/改疲劳必须停服**(服务端运行时数据库被锁)
|
||||
|
||||
### 4.3 路径自动识别(重要)
|
||||
|
||||
`SERVER_ROOT` 按以下优先级自动定位(`server.py` 模块加载时执行一次):
|
||||
|
||||
1. 环境变量 `DNF90_SERVER_ROOT`(手动覆盖)
|
||||
2. EXE 自身所在目录(若其下存在 `Server/`)—— **正常场景命中**
|
||||
3. 向上逐级查找含 `Server/` 的目录(最多 4 层)
|
||||
4. 兜底 EXE 所在目录 `BASE_DIR`
|
||||
|
||||
派生路径(全部基于 SERVER_ROOT):
|
||||
|
||||
```python
|
||||
CONTROL = SERVER_ROOT + r"\Server\runtime\bin\DNF90Control.exe"
|
||||
CLIENT_DIR = SERVER_ROOT + r"\Client"
|
||||
LOADOUT_EXE = SERVER_ROOT + r"\LoadoutTool\DNFLoadout.exe"
|
||||
INSTANCE_PATH = SERVER_ROOT + r"\Server\runtime\config\instance.json"
|
||||
DB_PATH = SERVER_ROOT + r"\Server\runtime\data\dnf90.db"
|
||||
```
|
||||
|
||||
**关键约定**:启动器 EXE 永远放在 `90server` 根目录,路径解析完全锚定 EXE 自身位置,不依赖任何写死盘符。
|
||||
|
||||
### 4.4 Native API 认证机制
|
||||
|
||||
- 服务端 GM API 监听 `127.0.0.1:18111`
|
||||
- 认证头:`X-Admin-Token`
|
||||
- 两套路由:`/dnf/gm/*`(新版)、`/local/dnf-gm/*`(兼容版)
|
||||
- token 通过 `AUTO_GENERATE` 机制生成,存于 `instance.json`
|
||||
- 危险写操作需 `X-Admin-Confirm` + `X-Admin-Idempotency-Key` 头(`_post` 默认 `confirm=True`)
|
||||
|
||||
---
|
||||
|
||||
## 五、服务端 API 契约
|
||||
|
||||
> 服务端是 Go 编写的二进制,源码在 `90轮回源码/go-server/`。以下契约是从源码反推,供 native.py 和 server.py 对接。
|
||||
|
||||
### 5.1 职业转职
|
||||
|
||||
```
|
||||
GET /dnf/gm/characters/{id}/career → GMCareerState
|
||||
POST /dnf/gm/characters/{id}/career {job, first, awakening}
|
||||
```
|
||||
|
||||
`GMCareerState` 字段:`job`、`first_grow`(分支,0=未转职)、`awakening`、`profession_name`、`options[]`、`skills_reset`、`quest_reset_ids`
|
||||
|
||||
`options` 层级结构:
|
||||
```
|
||||
Option { job, name, branches[] }
|
||||
BranchOption { value, name, awakenings[] }
|
||||
AwakeningOption { value, name }
|
||||
```
|
||||
|
||||
### 5.2 任务查询
|
||||
|
||||
```
|
||||
GET /dnf/gm/characters/{id}/quests → {character, active[], completed[]}
|
||||
GET /dnf/gm/quests/search?query=... → GMQuestSummary[]
|
||||
GET /dnf/gm/quests/{id}/detail?quest_id=... → 详情
|
||||
POST(任务操作 accept/ready/give-up/complete/uncomplete)
|
||||
```
|
||||
|
||||
`GMQuestSummary` 字段:`quest_id`、`name`、`grade`、`type`、`level_min`、`level_max`、`status`、`active`、`completed`、`acceptable`
|
||||
|
||||
### 5.3 爆率 / 副本 / 黑钻
|
||||
|
||||
```
|
||||
# 爆率(游戏玩法设置)
|
||||
GET /dnf/gm/gameplay → GMGameplaySettings
|
||||
POST /dnf/gm/gameplay/hell-drop-rate {percent}
|
||||
POST /dnf/gm/gameplay/epic-potion-test-multiplier {percent}
|
||||
|
||||
# 副本
|
||||
GET /dnf/gm/characters/{id}/dungeons → {difficulty_dungeons, difficulty_dungeons_unlocked, hell_areas, hell_permission_quests, hell_permission_quests_cleared}
|
||||
POST /dnf/gm/characters/{id}/dungeons/unlock-difficulties
|
||||
POST /dnf/gm/characters/{id}/dungeons/unlock-hell
|
||||
|
||||
# 黑钻
|
||||
GET /local/dnf-gm/character/black-diamond
|
||||
POST /local/dnf-gm/character/black-diamond {character_id, days}
|
||||
```
|
||||
|
||||
`GMGameplaySettings` 字段:`hell_epic_drop_rate_percent`(当前爆率%)、`minimum_percent`、`maximum_percent`(上限 15000%)、`epic_drop_rate_potion_test_multiplier_percent`
|
||||
|
||||
黑钻返回:`active`、`expires_at`(时间戳,0=未开启)、`remaining_days`、`fatigue_limit`、`black_diamond_fatigue_limit`
|
||||
|
||||
### 5.4 品级(rarity)枚举
|
||||
|
||||
**rarity 0~6 共 7 档,权威映射:**
|
||||
|
||||
| 值 | 品级 | 示例装备 |
|
||||
|----|------|---------|
|
||||
| 0 | 普通 | 粗布衬衫 |
|
||||
| 1 | 高级 | 棉布衬衫 |
|
||||
| 2 | 稀有 | 荣誉者长袍 |
|
||||
| 3 | 神器 | (旧) 雕翎长袍 |
|
||||
| 4 | 史诗 | (旧) 银月少女T恤衫 |
|
||||
| 5 | 传说 | 贤者的智慧之衣 |
|
||||
| 6 | 神话 | 首领萨斯的阴影长袍 |
|
||||
|
||||
> ⚠️ **注意**:4=史诗、5=传说、6=神话(顺序易记错)。「勇者」不是独立品级档,是异界装备称呼,实际归类于神器(3)。
|
||||
> 物品搜索的 `rarity` 参数是**精确匹配**(`item.Rarity != rarity` 过滤),范围 -1~6,-1=不限。
|
||||
|
||||
### 5.5 品级机制(装备发放时)
|
||||
|
||||
- `equipment_quality` 仅接受 `default` / `random` / `top` 三个值
|
||||
- 品级本质是 `quality_seed` 数值:1=最下级,999999998(TopSeed)=最上级
|
||||
- **直接进背包路径(grant-options)忽略品级参数**,只有**邮件(mail)路径**品级才真正生效
|
||||
- 服务端不支持「最下级」,只能最上级(top)或随机(random)
|
||||
|
||||
### 5.6 装备分类与武器子类型
|
||||
|
||||
- 服务端 `kind` 值:`"equipment"`(装备)、`"stackable"`(道具)
|
||||
- 武器子类型通过 `q=/weapon/<sub>/` 匹配 PVFPath(如 `equipment/character/swordman/weapon/katana/`)
|
||||
|
||||
---
|
||||
|
||||
## 六、启动器后端 API 清单
|
||||
|
||||
> `server.py` 提供,前端通过 `api()`(GET)和 `post()`(POST)调用。端口 `19527`。
|
||||
|
||||
### 6.1 GET 路由
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `/api/status` | 启动器状态(含 server_root/client_dir/各文件是否存在/native_available) |
|
||||
| `/api/logs` | 运行日志 |
|
||||
| `/api/gm/health` | GM 健康检查 |
|
||||
| `/api/accounts-config` | 账号配置 |
|
||||
| `/api/accounts` | 账号列表 |
|
||||
| `/api/characters` | 角色列表(支持 ?account= 筛选) |
|
||||
| `/api/character` | 角色详情 |
|
||||
| `/api/character/inventory` | 背包 |
|
||||
| `/api/character/equipment` | 装备 |
|
||||
| `/api/character/mailbox` | 邮箱 |
|
||||
| `/api/character/fatigue` | 疲劳(mode=query) |
|
||||
| `/api/character/career` | 职业信息(转职查询) |
|
||||
| `/api/character/dungeons` | 副本状态 |
|
||||
| `/api/character/black-diamond` | 黑钻状态 |
|
||||
| `/api/gameplay` | 爆率设置查询 |
|
||||
| `/api/quests` | 任务概览 |
|
||||
| `/api/quests/search` | 任务搜索 |
|
||||
| `/api/quests/detail` | 任务详情 |
|
||||
| `/api/wipe/preview` | 删档预览 |
|
||||
| `/api/items/categories` | 道具分类 |
|
||||
| `/api/item/search` | 道具搜索 |
|
||||
|
||||
### 6.2 POST 路由
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `/api/action` | 启动器动作(start/stop/gm/loadout) |
|
||||
| `/api/accounts-config` | 账号配置写操作 |
|
||||
| `/api/source` | 切换数据源(SQLite/Native) |
|
||||
| `/api/character/save` | 保存角色 |
|
||||
| `/api/character/stat` | 改属性 |
|
||||
| `/api/character/fatigue` | 改疲劳 |
|
||||
| `/api/character/gold` | 改金币 |
|
||||
| `/api/character/level` | 改等级 |
|
||||
| `/api/character/skill-points` | 改技能点 |
|
||||
| `/api/character/career` | 应用转职 |
|
||||
| `/api/dungeon/unlock` | 解锁副本难度 |
|
||||
| `/api/black-diamond/activate` | 开启黑钻 |
|
||||
| `/api/gameplay/hell-drop-rate` | 设置爆率 |
|
||||
| `/api/gameplay/epic-potion-multiplier` | 设置药剂倍率 |
|
||||
| `/api/quests/action` | 任务操作 |
|
||||
| `/api/character/grant` | 发放道具 |
|
||||
| `/api/character/delete` | 删除角色 |
|
||||
| `/api/account/delete` | 删除账号 |
|
||||
| `/api/item/search` | 道具搜索(POST body) |
|
||||
| `/api/item/grant` | 发放道具 |
|
||||
| `/api/item/grant-options` | 发放道具(高级选项) |
|
||||
| `/api/item/mail` | 邮件发放道具 |
|
||||
| `/api/mail/send` | 发送邮件 |
|
||||
| `/api/mailbox/clear` | 清空邮箱 |
|
||||
| `/api/inventory/clear` | 清空背包 |
|
||||
| `/api/inventory/delete` | 删除背包物品 |
|
||||
| `/api/wallet/query` | 钱包查询 |
|
||||
| `/api/cera` | 点券充值 |
|
||||
|
||||
### 6.3 native.py 方法清单(服务端 GM 端点封装)
|
||||
|
||||
`native.py` 是服务端 GM API 的完整 Python 客户端封装,方法列表:
|
||||
|
||||
- **账号**:`list_accounts` / `get_account` / `set_account_value`
|
||||
- **角色**:`list_characters` / `get_character` / `get_character_detail`
|
||||
- **物品/背包**:`get_inventory` / `get_equipment` / `get_mailbox` / `get_item_detail` / `get_item_mail_options` / `get_item_categories` / `search_items` / `search_avatar_sets`
|
||||
- **道具发放**:`grant_item` / `grant_item_options` / `grant_item_with_emblems` / `send_item_mail`
|
||||
- **删除**:`delete_item` / `delete_inventory_item` / `clear_inventory` / `unequip_equipment` / `delete_equipment`
|
||||
- **属性修改**:`set_gold` / `set_level` / `set_skill_bonus` / `set_career` / `get_career` / `set_cera` / `adjust_revive_coin`
|
||||
- **钱包**:`query_wallet` / `update_wallet` / `set_account_cube` / `get_account_cubes`
|
||||
- **邮件**:`send_system_mail` / `clear_mailbox` / `delete_mailbox_mails`
|
||||
- **任务**:`get_quest_overview` / `search_quests` / `get_quest_detail` / `accept_quest` / `mark_quest_ready` / `give_up_quest` / `complete_quest` / `uncomplete_quest` / `complete_quest_batch`
|
||||
- **副本**:`get_dungeons` / `unlock_dungeon_difficulties` / `unlock_hell_challenges`
|
||||
- **黑钻**:`get_black_diamond` / `activate_black_diamond`
|
||||
- **爆率**:`get_gameplay_settings` / `set_hell_drop_rate` / `set_epic_potion_multiplier`
|
||||
- **宠物**:`update_pet`
|
||||
|
||||
---
|
||||
|
||||
## 七、关键业务逻辑与约定
|
||||
|
||||
### 7.1 疲劳值机制
|
||||
|
||||
- 角色 `stats` 里的字段:`fatigue`(当前疲劳)、`fatigue_limit`(上限,默认156/黑钻188)、`fatigue_display_update`(已用)
|
||||
- 每天 6 点重置回上限
|
||||
- **无限疲劳** = 把 `fatigue` 和 `fatigue_limit` 都设为超大值(999999)
|
||||
- 服务端**无独立疲劳查询 GM 接口**,从 `get_character_detail()` 的 `stats` 里读
|
||||
|
||||
### 7.2 账号配置
|
||||
|
||||
- `accounts.json` 存 EXE 同目录(`BASE_DIR`)
|
||||
- `load_accounts` / `save_accounts` / `get_default_account`
|
||||
- 默认账号 `dnfplayer` 不可删除
|
||||
|
||||
### 7.3 窗口与 UI 约定
|
||||
|
||||
- **GM 窗口打开直接最大化**,但**不用 `maximize()`**(会遮挡任务栏)
|
||||
- 用 `_get_work_area()`(`ctypes` 调 `SystemParametersInfoW(SPI_GETWORKAREA=0x0030)`)获取扣除任务栏的工作区,再 `move()` + `resize()`
|
||||
- 主窗口和 GM 窗口都屏幕居中(`webview.screens[0].width/height` 计算 x/y)
|
||||
- 深色 DNF 风格 + 紫青渐变 + 毛玻璃质感
|
||||
|
||||
### 7.4 危险操作约定
|
||||
|
||||
- 删除角色/删档/改疲劳:**需先停服**(SQLite 直读模式)
|
||||
- 危险操作二次确认:输入确认词(如 `DELETE`、`转职`)
|
||||
|
||||
### 7.5 进程回收
|
||||
|
||||
- 关闭后需 `os._exit(0)` 强制退出 + `_kill_webview2_children()`(清理 WebView2 子进程)
|
||||
- 打包前需先 `taskkill /F /IM "DNF90启动器.exe"`(否则 dist exe 被锁定)
|
||||
|
||||
---
|
||||
|
||||
## 八、打包与部署
|
||||
|
||||
### 8.1 打包命令
|
||||
|
||||
用 spec 文件打包(推荐,配置已固化):
|
||||
|
||||
```bash
|
||||
cd "C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/launcher"
|
||||
|
||||
# 先杀掉残留进程(避免文件锁定)
|
||||
taskkill /F /IM "DNF90启动器.exe"
|
||||
|
||||
# 打包
|
||||
C:/Python315-32/python.exe -m PyInstaller --clean --noconfirm "DNF90启动器.spec"
|
||||
```
|
||||
|
||||
产物:`launcher/dist/DNF90启动器.exe`
|
||||
|
||||
### 8.2 拷贝到运行目录
|
||||
|
||||
```bash
|
||||
cp -f "C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/launcher/dist/DNF90启动器.exe" "Z:/90server/DNF90启动器.exe"
|
||||
```
|
||||
|
||||
### 8.3 spec 文件要点
|
||||
|
||||
`DNF90启动器.spec` 关键配置:
|
||||
- `datas`:打包 `index.html`、`style.css`、`app.js`、`sponsor.png`、`db.py`、`native.py`
|
||||
- `hiddenimports`:`clr_loader`、`pythonnet`
|
||||
- `collect_all('webview')`:pywebview 全部资源
|
||||
- `console=False`:windowed 模式(无控制台)
|
||||
- `upx=True`:UPX 压缩
|
||||
- `name='DNF90启动器'`
|
||||
|
||||
> 如果不用 spec,等价命令行(注意 Windows 分隔符是 `;`)见 `README.md`。
|
||||
|
||||
### 8.4 部署要求
|
||||
|
||||
- EXE 放在 `90server` 根目录
|
||||
- 目标电脑需 WebView2 运行时(Win10/11 自带)
|
||||
- 首次运行请求管理员权限(UAC,启动服务端需要)
|
||||
- 无需安装 Python 或其他依赖(单文件已全打包)
|
||||
|
||||
### 8.5 验证方法
|
||||
|
||||
打包后在同批命令内验证(后台进程在命令结束时会被回收):
|
||||
|
||||
```bash
|
||||
cd "Z:/90server" && ./DNF90启动器.exe > /dev/null 2>&1 &
|
||||
sleep 8
|
||||
curl -s --noproxy "*" "http://127.0.0.1:19527/api/status"
|
||||
```
|
||||
|
||||
预期返回:`server_root=Z:\90server`、`db_exists=true`、`native_available=true`、`control_exists=true`、`server_dir_exists=true`、`client_dir_exists=true`。
|
||||
|
||||
---
|
||||
|
||||
## 九、参考资料位置
|
||||
|
||||
| 资料 | 位置 |
|
||||
|------|------|
|
||||
| 服务端源码(Go) | `C:/Users/Administrator/Downloads/90轮回源码/90轮回源码/go-server/` |
|
||||
| 旧 GM 工具源码备份 | WorkBuddy 资料库节点 `sfEHKi5MJIh0D9lSykGVoT`(DNF90工具与资料备份 web 页) |
|
||||
| 旧 GM 工具(运行版) | `Z:/90server/gm_tool/`(已打包 DNF90GM工具.exe) |
|
||||
| 可运行模拟器 | `Z:/90server/` |
|
||||
| 当日工作日志 | `C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/2026-11-03.md` |
|
||||
|
||||
### 读取资料库参考资料的方法
|
||||
|
||||
```bash
|
||||
# 查询节点信息
|
||||
python3 "C:/Users/Administrator/AppData/Local/Programs/WorkBuddy/resources/app.asar.unpacked/resources/plugins/workbuddy-builtin/skills/library/space_api.py" space.workspace.node-info --node-id sfEHKi5MJIh0D9lSykGVoT
|
||||
|
||||
# 下载 artifacts
|
||||
python3 ".../library/page/download_page_artifacts.py" --node-id "sfEHKi5MJIh0D9lSykGVoT" --out-dir "/tmp/gmref" --path "..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十、常见问题与坑
|
||||
|
||||
### 10.1 打包相关
|
||||
|
||||
| 问题 | 解决 |
|
||||
|------|------|
|
||||
| 打包报 OSError safe-delete 失败 | dist 的 exe 被占用,先 `taskkill /F /IM "DNF90启动器.exe"` |
|
||||
| `--add-data` 分隔符 | Windows 用 `;`(不是 `:`) |
|
||||
| 打包后窗口变浏览器 | pywebview `transparent=True` 在 Windows 不支持,会导致降级 |
|
||||
|
||||
### 10.2 pywebview 相关
|
||||
|
||||
| 问题 | 解决 |
|
||||
|------|------|
|
||||
| 无边框窗口 maximize 遮挡任务栏 | 用 `_get_work_area()` 获取工作区再 resize,不要用 `maximize()` |
|
||||
| 关闭后 WebView2 子进程残留 | `os._exit(0)` + `_kill_webview2_children()` |
|
||||
| `create_window()` 报错 | 必须在 `webview.start()` 之前调用 |
|
||||
|
||||
### 10.3 后端参数解析
|
||||
|
||||
**重要教训**:`_api_character_fatigue` 曾有个 bug——用 `if "id" in data_or_qs` 判断,导致 GET 请求的 `parse_qs` 结果(值是 list)被当成标量,`char_id` 变成 `['1']` 列表,报「角色 ['1'] 不存在」。
|
||||
|
||||
**正确写法**:GET 请求统一用 `qs.get("id", [""])[0]` 取第一个值;POST body 才直接 `data.get("id")`。
|
||||
|
||||
### 10.4 数据源相关
|
||||
|
||||
| 问题 | 解决 |
|
||||
|------|------|
|
||||
| 删角色/删档报数据库锁定 | 需先停服(点「关闭服务」) |
|
||||
| 疲劳值读不出 | 服务端无独立疲劳接口,用 `get_character_detail()` 的 `stats.fatigue` |
|
||||
| 道具搜索 404 | 前端用 `post('/api/item/search')` 但后端原来只有 GET 路由,需补 POST 路由 |
|
||||
|
||||
### 10.5 品级相关
|
||||
|
||||
- rarity 精确匹配(`-1` = 不限),枚举 0~6:普通/高级/稀有/神器/史诗/传说/神话
|
||||
- 品级 `equipment_quality` 只接受 default/random/top
|
||||
- 品级只有邮件路径生效,直接进背包路径忽略品级
|
||||
|
||||
### 10.6 测试相关
|
||||
|
||||
- **后台进程在命令结束时会被回收**(非交互模式),验证必须把启动和测试放同一批命令内
|
||||
- 服务端 `DNF90Server.exe` 是常驻进程,测试时**不要**误杀
|
||||
- 默认测试角色:`character_id=1`,名「测试」,职业神枪手,1级
|
||||
|
||||
---
|
||||
|
||||
## 附:快速上手速查
|
||||
|
||||
```bash
|
||||
# 1. 源码方式运行启动器(开发调试)
|
||||
cd "C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/launcher"
|
||||
C:/Python315-32/python.exe server.py
|
||||
|
||||
# 2. 校验语法
|
||||
C:/Python315-32/python.exe -c "import ast; ast.parse(open('server.py',encoding='utf-8').read())"
|
||||
node --check app.js
|
||||
|
||||
# 3. 打包 + 拷贝(换机后重复此流程)
|
||||
taskkill /F /IM "DNF90启动器.exe"
|
||||
cd "C:/Users/Administrator/WorkBuddy/DNF启动器和GM管理工具/launcher"
|
||||
C:/Python315-32/python.exe -m PyInstaller --clean --noconfirm "DNF90启动器.spec"
|
||||
cp -f dist/DNF90启动器.exe "Z:/90server/DNF90启动器.exe"
|
||||
|
||||
# 4. 用默认测试角色验证(同批命令内)
|
||||
cd "Z:/90server" && ./DNF90启动器.exe > /dev/null 2>&1 &
|
||||
sleep 8 && curl -s --noproxy "*" "http://127.0.0.1:19527/api/status"
|
||||
```
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
# DNF90 服务端开发文档
|
||||
|
||||
> 覆盖服务端(Go)的架构、GM 接口契约、编译部署、以及本次新增的拍卖行 / 背包管理改动。
|
||||
>
|
||||
> 最后更新:2026-11-03
|
||||
|
||||
---
|
||||
|
||||
## 一、架构概览
|
||||
|
||||
服务端是 Go 单模块(`module longheng.io/server`),采用分层结构:
|
||||
|
||||
```
|
||||
server/
|
||||
├── cmd/
|
||||
│ ├── server/dnf90/ 产品入口(主服务进程,产出 DNF90Server.exe)
|
||||
│ ├── server/control/ 部署控制器(DNF90Control.exe,start/stop/configure)
|
||||
│ ├── server/doctor/ 部署预检(DNF90Doctor.exe)
|
||||
│ ├── companionpvf/ PVF 伴随进程
|
||||
│ └── devtools/ scratch/ 开发调试工具(非产品)
|
||||
├── internal/
|
||||
│ ├── app/dnf90/ 单一组合根(路由注册、依赖装配)
|
||||
│ ├── services/dnfbridge/ 客户端传输 + GM bridge 层(本次改动重点)
|
||||
│ ├── modules/dnf/ DNF 领域模块(auction、dungeon、character 等)
|
||||
│ ├── platform/ 框架层(含 platform/pvf PVF 解析)
|
||||
│ ├── services/logic/dnf/ MySQL 仓储装配
|
||||
│ └── ...
|
||||
└── pkg/ 对外包
|
||||
```
|
||||
|
||||
关键入口:
|
||||
- 主服务:`cmd/server/dnf90/main.go`
|
||||
- GM 路由:`internal/app/dnf90/gm_tool_compat.go`(兼容层,`/local/dnf-gm/*`)
|
||||
- GM bridge:`internal/services/dnfbridge/gm_*.go`
|
||||
|
||||
---
|
||||
|
||||
## 二、编译与部署
|
||||
|
||||
### 环境要求
|
||||
|
||||
- **Go ≥ 1.26**(`go.mod` 声明 `go 1.26.5`)
|
||||
- 依赖走国内镜像(加速):`GOPROXY=https://goproxy.cn,direct`、`GOSUMDB=sum.golang.google.cn`
|
||||
|
||||
### 编译命令
|
||||
|
||||
```bash
|
||||
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
|
||||
```
|
||||
|
||||
编译产物部署到 `90server/Server/runtime/bin/`。
|
||||
|
||||
> 若 `go build` 报 "package unsafe is not in std",说明本地 Go SDK 的 src 不完整,
|
||||
> 需改用完整 toolchain(见 `docs/开发文档.md` 的「Go 环境问题」章节)。
|
||||
|
||||
---
|
||||
|
||||
## 三、GM 接口契约(`/local/dnf-gm/*`)
|
||||
|
||||
GM 接口由 `internal/app/dnf90/gm_tool_compat.go` 注册,bridge 层实现在 `internal/services/dnfbridge/`。
|
||||
|
||||
### 认证
|
||||
|
||||
所有接口走 admin HTTP 端口(默认 `127.0.0.1:18111`),请求头带 `X-Admin-Token`(token 来自 `instance.json` 的 `adminToken`)。
|
||||
|
||||
### 返回结构
|
||||
|
||||
统一约定:`{"ok": true, "data": <结果>}`;错误时 `{"error": "<msg>"}`。
|
||||
|
||||
### 拍卖行接口(本次新增)
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/local/dnf-gm/auction/listings` | 列出所有挂单,返回 `{listings, escrow_gold}` |
|
||||
| POST | `/local/dnf-gm/auction/list` | 上架(支持自动补足) |
|
||||
| POST | `/local/dnf-gm/auction/cancel` | 下架 |
|
||||
|
||||
上架请求体(`GMAuctionListRequest`):
|
||||
|
||||
```json
|
||||
{
|
||||
"seller_id": "角色ID",
|
||||
"item_id": 10001,
|
||||
"count": 1,
|
||||
"unit_price": 10000,
|
||||
"start_price": -1, // 每件起拍价,-1=不设竞拍
|
||||
"buyout_price": -1, // 每件一口价,-1=不设一口价
|
||||
"duration_hours": 24, // 24 或 48
|
||||
"grant_if_missing": true // 背包不足时自动凭空发放差额再上架
|
||||
}
|
||||
```
|
||||
|
||||
### 背包管理接口(本次新增)
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| GET | `/local/dnf-gm/inventory/list?character_id=xxx` | 列角色背包 + 穿戴装备,每行 `{collection, entry_key, item_id, count, bind, item_name}` |
|
||||
| POST | `/local/dnf-gm/item/delete` | 删除指定背包行(`entry_key` + `collection`,支持 slots/wear) |
|
||||
|
||||
---
|
||||
|
||||
## 四、本次关键改动说明
|
||||
|
||||
### 1. GM 拍卖行(线上上架通道)
|
||||
|
||||
- **新增** `internal/services/dnfbridge/gm_auction.go`:
|
||||
- `GMAuctionListings` / `GMAuctionList` / `GMAuctionCancel` 三个方法。
|
||||
- 直接调用 `dnfauction.Owner` 的纯数据层 `Register`/`Cancel`/`LoadListing`,**不依赖玩家 session**。
|
||||
- **新增** `GMInventoryList` 方法(列背包全部道具 + 穿戴装备)。
|
||||
|
||||
### 2. 上架免押金
|
||||
|
||||
`gm_auction.go` 的 `GMAuctionList` 中,`Register` 的 `Deposit` 设为 `0`(原为 `dnfauction.ListingDepositGold = 10000`)。
|
||||
服务端 `owner.go` 里是 `if cmd.Deposit > 0` 才扣款,因此 `Deposit=0` 直接跳过押金逻辑。
|
||||
|
||||
### 3. 自动补足上架(发放式)
|
||||
|
||||
`GMAuctionList` 支持 `grant_if_missing`:卖家背包物品不足时,先调 `GMGrantItem`(`dnfdungeon.Owner.GrantPickupItem`)凭空发放差额到背包,再 `Register` 上架。实现「像发放道具那样直接上架」。
|
||||
|
||||
### 4. 路由层
|
||||
|
||||
`gm_tool_compat.go` 新增常量与路由:
|
||||
- `gmAuctionListingsPath` / `gmAuctionListPath` / `gmAuctionCancelPath`
|
||||
- `gmInventoryListPath`
|
||||
|
||||
---
|
||||
|
||||
## 五、GM bridge 层约定
|
||||
|
||||
- GM 方法都在 `internal/services/dnfbridge/` 包,方法签名形如 `func (s *Service) GMXxx(ctx, req) (resp, error)`。
|
||||
- 数据访问用 `s.repositoryGroup()`(返回 `dnfrepo.Group`,含 `Inventory`、`Equipment`、`Character` 等仓储)。
|
||||
- 物品名解析用 `s.currentPVFItemCatalog()`(`dnfpvf` 目录),`ResolveItem(itemID)` 得到 `definition.Name`。
|
||||
|
||||
---
|
||||
|
||||
## 六、常见坑
|
||||
|
||||
1. **`go 1.26.5` 版本声明**:装 ≥1.26 即可,`GOTOOLCHAIN=auto` 会自动下载所需 toolchain。
|
||||
2. **GM 接口路由**:`gm_tool_compat.go` 里 `gmToolPost` 包装 POST 接口,GET 接口用 `HandleFunc` + `writeGMJSON`。
|
||||
3. **服务端 `Register` 的校验**:`buyout_price` 必须等于 `unit_price`(或"不设一口价"),`start_price` 在 `[0, unit_price]` 或"不设竞拍",时长只能是 24/48。
|
||||
4. **对外发布**:不要附带本机测试用的深渊掉落修改;远程换装工具不要公开 Admin 端口。
|
||||
Reference in new issue
Block a user