服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
4.8 KiB
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。
实现逻辑
pvf.Open打开归档。- 读
stackable/stackable.lst(堆叠道具)和equipment/equipment.lst(装备),用ParseListText解析出item_id → 路径。 - 对每个 item,用
resolveItemFile找文件(路径候选策略复刻服务端readDungeonDropText):- 直接用 lst 里的路径
- 尝试
root/前缀(stackable//equipment/) - 尝试
(r)修订文件名(90 版本特有)
ScriptMetadata读[name]段得到名称。
四、与 GM 工具的集成(离线名称显示)
名称表存放
名称表 dnf_items.json 与 pvfexport.exe 放在服务端根目录下的 GM配置/ 目录:
90server/
└── GM配置/
├── pvfexport.exe
└── dnf_items.json
自动重导出
GM 工具(launcher)启动时,db.py 的 _load_item_names() 会:
- 定位
GM配置/dnf_items.json和Client/Script.pvf。 - 对比两者修改时间:
- 名称表已是最新 → 秒加载(约 0.09 秒)。
- PVF 被更新(或名称表缺失)→ 自动调
pvfexport.exe -compact重导出(约 5 秒),再加载。
- 加
_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)与主项目解耦。