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

4.8 KiB
Raw Permalink Blame History

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)

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。

编译

cd pvf
go build -o pvfexport.exe ./cmd/pvfexport/

用法

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)与主项目解耦。