Files
90CNServer/docs/PVF解析开发文档.md
hanfuit 645b64fe21 DNF90 服务端与启动器完整源码(含 GM 工具功能增强)
服务端(Go):
- GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面
- GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新
- 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新)
- 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍
- 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0)
- 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算

启动器(Python/JS):
- 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭
- GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐)
- 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
2026-11-04 14:31:55 +08:00

130 lines
4.8 KiB
Markdown
Raw Permalink 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.
# 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)与主项目解耦。