服务端(Go): - GM 塞背包/带属性装备发放:在线角色推送 op14 增量物品行,背包实时刷新,无需回角色选择界面 - GM 改金币/改等级:去掉在线拒绝,落库后推送 op14 钱包/op37 等级状态,在线实时刷新 - 等级端点统一切换到新版 GMSetCharacterLevel(自带在线刷新) - 无限/锁死疲劳模式 + 燃烧疲劳:每房间消耗 2 点、打怪与通关经验双倍 - 燃烧疲劳开关时给所有在线角色推送系统提示(op12 mode 0) - 深渊史诗掉率:GM 全局倍率 + 城镇增益药剂窗口倍率,真实接入掉落结算 启动器(Python/JS): - 标题栏自绘、窗口 1204x717、禁止拖拽改大小、仅最小化/关闭 - GM 工具:疲劳与燃烧、深渊史诗掉率、拍卖行上架(单一定价对齐) - 拍卖行上架前端对齐服务端单一价格语义,修复 400 报错
11 KiB
Mod 格式扩展
分层
internal/mod定义中立包模型、格式注册表和独立的Reader/Writer接口,不依赖 Wails、归档服务或版本库。Package保存名称和操作集合。replace携带明确编码的文件内容,merge-list/merge-strings携带键值条目,delete表达整文件删除。services.ExportService将选中文件、相对 HEAD 的工作区改动或提交前后状态适配为包模型;负责依赖补齐、预检、修订校验和目录发布。- 前端四类导出入口只指定来源,不包含格式专属的目录或编码规则。
添加格式
向注册表注册稳定 ID、显示名称、支持的操作以及读取器或写入器。读取和写入能力根据实际接口计算,不使用空实现冒充支持。
写入器先通过 Validate 检查包是否可表达,Files 描述实际输出路径及对应的包条目,再通过 Write 写入隔离的临时目录。Files 中没有 EntryPath 的文件为必需的生成元数据,不可移除。写入器不负责目录选择、覆盖确认或修改归档。
导入通过 Reader.Detect(fs.FS) 识别格式,Reader.Read(fs.FS) 返回同一 Package,再由服务层执行导入预览和归档暂存事务。外部文件路径不能直接成为写入路径;导入器需要校验包内路径和操作,并复用现有暂存、索引重建及版本变更记录流程。当前已注册 110USextend 和 115CN-mod,现有“导入”弹窗中的“Mod”选项提供入口。实现 ArchiveWriter 的格式直接发布单个 ZIP,其余格式保留目录发布。
110USextend
导出结果是父目录下的 <mod名称>-<版本号>/,包含 pack.json 和实际用到的 pvf/、merge/。版本号可填写,默认或留空为 1.0,不强制 semver;最多 128 字节,不允许控制字符和首尾空格。优先级 100、启用,types 记录整文件替换项的 PVF 类型。文本使用 UTF-8,无 BOM。
普通脚本和文本整份放入 pvf/<归档路径>;.lst、.str 使用 merge/<归档路径>。版本导出只包含新增、修改的表条目(新增表以空表为基线);选中文件、文件集或未启用版本控制的内存改动没有准确基线,跳过显式选择的整表,不全量导出。列表按编号匹配,文字表按第一个 > 分割并保留空值及文字中的空格;忽略有缩进的 // 注释和空行。
该格式不支持删除,预检会提示跳过的文件或表条目删除。只有删除或没有可应用条目的包不能导出。无法解析的文字表行跳过;无法解析或读取的依赖表、无法准确提取改动的合并表及未能补齐的依赖也跳过,记录文件路径、行号或原因到 [pvfine:export] 日志,并在确认弹窗汇总警告。非法目标路径、不能表达的输出内容仍阻止导出,不以改名绕过安全校验。
依赖补齐默认关闭。开启后,从同一来源状态补齐脚本引用的文字表条目、表号映射及配置列表中匹配的登记项;显式内容优先,重复项去重。历史版本使用该提交快照,不读取当前工作区内容。不补齐任意脚本、sprite、sounds、npk 资源。
直接导出保留既有行为:版本新增或修改输出后状态,删除输出前状态。导出不会应用编辑器草稿、创建版本或保存 PVF。
未启用版本控制的“当前内存改动”从归档 overlay 枚举所有现存修改和新增文件;mod 模式跳过无版本基线的 lst/str,直接导出仍保留整文件;已删除文件不在该范围中。
所有确认弹窗列出完整输出树,勾选文件可控制最终输出范围。排除作用于已冻结的预览,不重新计算依赖,也不会自动补回被排除的依赖文件。已有预览刷新或切换模式时保留相同输出路径的排除,新弹窗重置;选空时不可导出。mod 元数据根据最终保留条目重新生成。
未选择合并来源时,只改名称或版本号会复用同一有效内容快照,不重新解码文件、计算差异或扫描依赖。快照过期时间不延长,归档、工作区或版本会话变化后不可复用。日志分别记录锁等待、来源读取、差异提取、依赖补齐和总耗时;普通脚本不读取不必要的修改前内容,无文字表引用时不读取表号映射或文字表。
导出时合并已有 MOD
导出为 mod 时,可在依赖开关后选择一个相同格式的已有 MOD。110USextend 选择目录;115CN-mod 可选择目录、ZIP 或 .dnfmod。普通文件同路径冲突时使用本次内容;同类型的 lst/str 合并操作按键合并,同键使用本次值,保留已有 MOD 的其余文件及键。选择已有 MOD 后,自动读取包内名称及版本号;纯数字分段版本递增最后一段(如 1.0 → 1.1、1.2.3 → 1.2.4),保留末段前导零,其他版本保留原值并提示手动修改。名称和版本号仍可编辑,ID 使用本次输入。输出目录名为 <mod名称>-<版本号>/,ZIP 为 <mod名称>-<版本号>.zip,不覆盖已有 MOD。版本号留空时包名采用格式默认版本;名称与版本号组合必须是合法文件名。
已有内容经过读取、合并和格式校验后进入同一预览树,可以取消勾选。每次准备预览都会重新读取合并来源;执行时使用冻结内容,不再读取源 MOD。切换格式或重新打开弹窗会清除来源;取消文件选择保留原来源。115CN 的全部组件合并为基础内容;互斥组件或组件目标冲突会阻止合并。
当前仅合并 PVF 内容,不复制 NPK/BK2 等客户端资源、包级依赖/冲突和其他包元数据。
Mod 导入
工具栏“导入”中选择“Mod”和导入格式,再选择单个 mod 目录。格式选项由 ModImportService.Formats() 从注册表加载,只列出拥有 Reader 的格式;服务按显式传入的格式 ID 调用对应的 Detect / Read,目录与所选格式不匹配时拒绝预览,不回退到其他读取器。后续格式注册读取器后即可出现在导入选项中。110USextend 目录需包含 pack.json。当前支持读取本工具导出的 110USextend 包;pvf/ 中每个替换文件必须在 pack.json.types 中声明类型(1 为脚本、3 为文本),声明的文件必须存在。仅处理 pvf/ 和 merge/,不导入 sprite、sounds、npk 等游戏资源,不支持删除操作。
预览显示所有文件的新增/更新状态、整文件替换或表合并方式;展开表文件显示每个传入键的新增/覆盖状态、新值和原值。整文件替换使用包中声明的类型;.lst 按编号合并,.str 按第一个 > 拆分后按文字键合并,同键覆盖并保留无关条目。文字表保留空值、值中的空格和 >,以及当前表中未改动的行和注释。归档中不存在的合并表会新建;已有合并表类型必须匹配。
读取器拒绝非法路径、大小写路径冲突、文件/目录冲突、符号链接、非普通文件、敏感配置路径、无效编码、重复传入合并键及无法解析的合并行。包内文本必须是无 BOM 的 UTF-8;合并行忽略空行和有缩进的 // 注释。现有目标表无法准确解析时阻止导入,避免丢失条目。
ModImportService 在隔离归档上预检并冻结合并后的文件内容,确认时不重新读取目录。预览有效期为 10 分钟;归档、工作区修订或版本会话发生变化后必须重新预览。取消或重新选择会释放旧预览。确认导入复用文件导入暂存事务、索引更新及版本记录,预检或暂存失败不安装部分文件。启用版本控制时记录一组“导入 Mod”变更,可撤销;不会自动提交版本或保存 PVF。编辑器草稿仍由既有归档变更刷新流程处理。
115CN-mod
格式 ID 为 115CN-mod。配置使用 UTF-8 mod.json,要求 schema: 1、client: "115CN"、kind: "resource"。读取目录、.zip、.dnfmod,配置可在根目录或唯一的一层包目录内;也兼容旧 ZIP 的 GB18030 中文包目录。RAR、7z、JAR 不接受。不解压到游戏目录,不执行包内程序或脚本。
PVF 操作通过 component 关联组件,source 相对包根目录,path 为小写 PVF 路径:
add:仅新增,目标存在时整批预检失败。replace:整份替换,目标不存在时新增。merge:仅支持.lst按编号合并、.str按文字键合并。同键覆盖,其他条目保留。type:示例包使用的可选字段,1 为脚本、3 为文本。省略时.str/.nut推断为 3,其他为 1;合并表必须匹配对应类型。patch:提供的文档片段和示例包未包含标签补丁字段协议,当前明确拒绝,不猜测、忽略或降级成整文件替换。需补充登录器ModExamples/README.md中的完整协议后接入。
导入预览按 defaultSelected 初始化组件,勾选后从冻结的包内容重新预检。requires 必须同时选中,exclusiveGroup 同组最多选一项;未知组件、循环依赖、所选组件的重复目标及文件/目录冲突拒绝。全不选时禁止执行。切换不延长原预览的 10 分钟期限,源 ZIP 变化不影响已冻结的内容,归档修订变化后必须重新预览。现有 Unicode 文字表的尾部 NUL 终止符原样保留,其他无法解析的行仍阻止合并。
files 中的客户端资源仅校验声明的组件、路径、size 与 sha256 字段格式,并在预览警告中报告;不读取或校验资源正文摘要、不写入 PVF、不安装或备份 NPK/BK2。包级 requires / conflicts 提示用户在登录器核对,不模拟 MOD 管理器的安装、排序、关闭或恢复状态。资源安装、已有视频恢复和依赖管理仍由登录器负责。
导出复用现有来源、精确表差异和依赖补齐,生成 <名称>.zip,根目录为 mod.json 和 pvf/,单个默认组件 base。新增独立 MOD ID 输入框:初次生成唯一 ID,发布同一 MOD 的更新需填回原 ID;ID 只能为 1–64 个小写字母、数字、-、_。名称/版本/ID 更新复用有效内容快照。筛选文件后同步重建清单,不携带被排除的载荷。ZIP 写完后原子发布,拒绝覆盖已有同名包。MCP export_prepare 选择此格式时同样必须提供 modId。
ZIP 拒绝路径穿越、大小写路径冲突、符号链接、特殊文件和敏感配置路径;声明文件读取也检查父目录链接。限制 ZIP 最多 20,000 项、单项 64 MiB、总展开大小 512 MiB,清单 4 MiB。目录包同样限制 PVF 单文件和累计读取大小。不会读取未登记的 PVF 载荷。
验证
运行 go test ./...、前端 npm test 和 npm run build。真实归档的新增导出验证需要设置 PVF_TESTFILE;示例包和游戏目录不作为可写测试目标。
115CN 示例读取回归:设置 PVF_MOD_TESTFILE 为 ZIP 路径,运行 go test ./internal/mod -run '^TestCN115RealMOD$' -v。真实归档预检另外设置 PVF_MOD_ARCHIVE 为测试 PVF,运行 go test ./services -run '^TestCN115RealMODPreflight$' -v -timeout 15m。两者均只读源文件,不自动执行或保存 PVF。