- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
128 lines
13 KiB
Markdown
128 lines
13 KiB
Markdown
# resource.pack-locale-proto
|
||
|
||
## Scope
|
||
|
||
比较 40250 的 EterPack 文件查找/覆盖、路径归一化、缓存、locale 编码、Python `pack.Get/Exist`、item/mob proto、MSM/MSA 和当前端资源入口、失败回退及测试证据。
|
||
|
||
## Reference call chain
|
||
|
||
- `EterPack/CEterPackManager::Get`
|
||
- 按 `SEARCH_FILE_FIRST` / pack-first 选择 loose file 或 pack。
|
||
- `ConvertFileName` 统一小写、反斜杠为 `/`,以 `CEterFileDict` 的 CRC/name 映射 pack。
|
||
- `GetFromPack` 支持根包、目录包、静态缓存和 pack overlay。
|
||
- `EterPack/CEterPackCursor`
|
||
- 对 pack 内数据提供有边界的 `Read/Seek/Size`。
|
||
- `UserInterface/PythonPackModule.cpp`
|
||
- `pack.Exist` 查询虚拟路径。
|
||
- `pack.Get` 只对 `.py/.pyc/.txt` 返回原始字节,其余失败。
|
||
- `EterLocale/StringCodec.cpp` 及 locale 变体
|
||
- 按 locale code page 处理非 UTF-8 文本,再交给 Python/UI。
|
||
- `GameLib/ItemManager.cpp::LoadItemTable/LoadItemList/LoadItemDesc`
|
||
- 从 EterPack 获取 MIPT/MIPX item proto、文本 item list 和 itemdesc;检查 fourcc、version、stride、数据尺寸及记录合法性。
|
||
- `UserInterface/PythonNonPlayer.cpp::LoadNonPlayerData`
|
||
- 从 EterPack 获取 MMPT mob proto,检查 fourcc、解压尺寸和记录。
|
||
- `GameLib/RaceDataFile.cpp`、`UserInterface/PythonSkill.cpp`
|
||
- 通过资源管理器加载 MSM/MSA、动作列表、技能和模型依赖。
|
||
|
||
## Current call chain
|
||
|
||
- `project/asset_root.gd` / `project/asset_pack.gd`
|
||
- loose `assets/`、Godot `assets.zip`/PCK 和 `asset_index.txt` 是主运行时资源入口。
|
||
- `formats/asset_resolver.cpp`
|
||
- 扫描目录或装载 `MTIDX1` 索引;按固定一级目录优先级和 `ymir work/` 后缀选择路径。
|
||
- `extension/src/pack/eterpack.cpp` / `pack_mount.cpp` / `asset_source.cpp`
|
||
- 实现当前 fork 自定义 XChaCha20 + Zstd `.epk`,并提供 loose/pack 读取和缓存抽取;目前没有被 GameScene/AssetRoot 主链调用。
|
||
- `extension/src/proto/proto.cpp` / `proto_node.cpp`
|
||
- 读取 MIPX/MIPT/MMPT、CLZO 和 proto key,建立 item/mob 索引。
|
||
- `project/locale.gd`
|
||
- 从 `locale/locale/<lang>` 直接用 Godot `FileAccess` 读取 4 个文本表及 item/mob names;缺失表静默保留空字典。
|
||
- `formats/msm.cpp`、`formats/msa.cpp`、`formats/textscript.cpp`、`formats/combo_table.cpp`
|
||
- 解析动作、模型和脚本格式,测试覆盖合成输入及部分真实资源。
|
||
|
||
## Audit findings (2026-09-20)
|
||
|
||
### 已对齐的部分
|
||
|
||
- `proto.cpp` 已支持 40250 实际使用的 MIPX/MIPT/MMPT fourcc、版本/stride/element 边界、CLZO 解压和 item/mob 固定字段;item `values[3]`、specular、mob type/rank/level 等调用方字段已有测试。
|
||
- `AssetResolver` 和 `PackMount` 都处理大小写/斜杠、`d:/ymir work/` 后缀和后挂 pack 覆盖;MSM/MSA、文本脚本和连击表已有格式级回归。
|
||
- locale、proto、MSA/MSM 资源都已经从当前 assets 目录进入游戏场景的不同子系统,缺少资源时多数调用方有明确 fallback。
|
||
|
||
### 材料差异
|
||
|
||
- 40250 的主资源权威是 `CEterPackManager`,而当前主运行时是 loose/`assets.zip`/Godot PCK + `AssetResolver`。`AssetSource`/`PackMount` 目前没有被 `AssetRoot`、`Metin2World`、模型、音频和 UI 统一调用;因此“实现了 pack reader”不等于“游戏实际按 40250 pack 优先级加载”。
|
||
- 当前 `EterPack` 使用 fork 自定义的 XChaCha20 + Zstd 单文件格式,40250 `CEterPack` 使用其原有 pack header/index/压缩/加密/策略链;两者不能互读。它是明确的平台/格式替换,不是已证明等价的适配。
|
||
- 40250 的 `SEARCH_FILE_FIRST`/pack-first、根包与目录包、CRC 字典、静态缓存、文件覆盖优先级和 pack 内失败语义没有在统一运行时入口中闭环;`AssetResolver` 的固定目录 rank 不能直接证明与所有 40250 pack 注册顺序一致。
|
||
- `EterLocale` 按 code page/locale 变体转换文本;当前 `locale.gd` 直接按 UTF-8 `FileAccess` 读取。中文 assets 目前可用,但 CP125x、Vietnamese、Arabic、Japanese 等路径和异常字节行为没有等价证明。
|
||
- `locale.gd` 只加载 `locale_interface.txt`、`locale_game.txt`、`itemdesc.txt`、`skilldesc.txt` 及两个 names 表;40250 的 Python/locale 系统会按功能继续读取 jobdesc、empire、insult、guild、地图和 UI 路径等资源。各调用方自行拼路径,缺表时容易静默退化为 key/空值。
|
||
- proto 当前单独由 GameScene 直接拼 `assets_root/locale/...` 文件路径加载;当资源只存在于当前自定义 pack 或 Godot PCK 内且没有解出到真实文件系统时,`Metin2Proto`/`SkillTable`/`Locale` 不共享同一虚拟读取器,可能出现 proto/文本/模型“一部分能读、一部分找不到”。
|
||
- 40250 `CItemManager::LoadItemTable`、`CPythonNonPlayer::LoadNonPlayerData` 的具体错误分支和记录尺寸校验较完整;当前 `proto_node` 失败后只写 `last_error` 并由上层继续使用空表,ItemManager 的“阻止未知 vnum/缺记录”语义还没有全部接到物品/模型入口。
|
||
- MSA/MSM 格式解析已有较强测试,但资源失败时当前部分视图使用动作别名、目录文件 fallback 或胶囊/占位模型;这些是可见行为替换,不能仅凭解析测试宣称与 40250 的 `CResourceManager` 失败/保留当前动作语义一致。
|
||
|
||
## Equivalence matrix
|
||
|
||
| 项目 | 状态 | 备注 |
|
||
|---|---|---|
|
||
| Preconditions | PARTIAL | 文件/pack/proto 存在性和尺寸有检查;统一虚拟资源入口及编码前置未闭合 |
|
||
| Branch structure | PARTIAL | loose/pack、proto fourcc、MSA/MSM 主分支已定位;搜索模式、locale 变体、失败回退未全对齐 |
|
||
| Algorithms/formulas | PARTIAL | proto 字段/CLZO 和路径归一化已对齐;pack 格式、CRC/cache 和目录优先级不等价 |
|
||
| State transition order | PARTIAL | GameScene 资源初始化顺序已定位;pack mount→proto→UI/model 的统一顺序无证据 |
|
||
| Constants/units | PARTIAL | proto stride/offset 和 pack name field 有测试;code page、压缩/加密参数属于不同格式 |
|
||
| Timing/event sources | PARTIAL | 当前同步加载与 `AssetResolver` 索引存在;40250 的 pack cache/lazy mapped read 未闭环 |
|
||
| Resource/data sources | PARTIAL | MIPX/MMPT/MSA/MSM 可读取;主链仍可能绕过 PackMount,locale 编码和完整资源集合不足 |
|
||
| Protocol side effects | MAPPED | 该合同主要是本地资源,不产生网络协议副作用;proto 失败对物品/实体的上层影响仍属 remaining |
|
||
| Interruption/failure/cleanup | PARTIAL | 单个读失败有错误返回;切换 locale、pack 重载、cache 失效和资源部分失败清理未完整证明 |
|
||
|
||
## Regression evidence
|
||
|
||
- `extension/tests/pack_roundtrip_test.cpp`:当前自定义 `.epk` 的加密/压缩、路径归一化、ymir suffix 和 patch 覆盖。
|
||
- `extension/tests/proto_test.cpp`、`extension/tests/proto_item_layout_test.cpp`:真实/合成 item_proto、mob_proto、stride 和字段偏移。
|
||
- `formats/tests/msm_test.cpp`、`formats/tests/msa_test.cpp`、`formats/tests/combo_table_test.cpp`、`formats/tests/map_formats_test.cpp`:格式解析与真实资源样本。
|
||
- `project/test_chinese_locale.gd`、`project/skill_test.gd`:locale 和技能文本 smoke test。
|
||
|
||
`project/package_render_test.gd` 未计入本轮通过证据:它要求导出包的 `MT_TEST_MODE=render` 入口,直接用 `godot --headless --script` 不会复现该生命周期。
|
||
|
||
## Deep audit round 2026-09-21
|
||
|
||
### Fresh regression results
|
||
|
||
- `pack_roundtrip_test`、`proto_test`、`proto_item_layout_test`、`formats_map_test`、`formats_msa_test`、`formats_msm_test`、`formats_combo_table_test` 均退出码 0;proto live 文件缺失时按测试设计跳过,formats live A1 在未设置 `M2_ASSETS` 时跳过。
|
||
- `test_chinese_locale.gd` 16/16、`skill_test.gd` 通过;中文角色、怪物、物品和技能文本可读取。
|
||
- `package_render_test.gd` 在直接 `godot --headless --script` 入口未能结束,和合同中要求的导出包 `MT_TEST_MODE=render` 生命周期不一致,不能计为通过。
|
||
|
||
### Static parity conclusion
|
||
|
||
- 当前自定义 `.epk`、proto 和路径归一化测试通过,但主运行时仍以 loose/assets.zip/PCK + AssetResolver 为主,PackMount/AssetSource 没有统一接入模型、音频、UI、proto 和 locale 主链。
|
||
- 自定义 XChaCha20+Zstd `.epk` 不能互读 40250 原生 EterPack;pack-first/search order、CRC 字典、cache overlay 和失败回退仍不是同一实现。
|
||
- 中文 UTF-8 路径可用,但 EterLocale code page/异常字节/完整功能文本表和 proto/模型共享虚拟读取器仍缺等价证据;缺资源时部分入口继续用空表/占位/动作别名。
|
||
|
||
本轮仍为 `PARTIAL`,完成资源、pack、locale、proto 第二轮证据登记,未修改实现。
|
||
|
||
### Active EterLib resource-core review
|
||
|
||
- `EterLib/Resource.cpp` 的 `CResource` 通过 `CEterPackManager` 加载、重载、清理和错误回退;`ResourceManager.cpp` 维护资源缓存、CRC、后台加载、资源 map、延迟删除和 `Update` 生命周期;`FileLoaderThread.cpp` 将读取请求、取数和处理拆成线程阶段。
|
||
- `TextFileLoader.cpp`/`parser.cpp` 提供缓存模式、token/group 解析和带类型的 token 读取,和当前仅按 `FileAccess` 读文本的路径不是同一失败/缓存/异步语义。当前已有格式和 `.epk` 单元测试,但没有证明主运行时的模型、音频、UI、proto、locale 都经过同一个资源管理器。
|
||
- 因此本轮将上述 EterLib 活跃文件归入资源合同的静态源码证据;缓存命中、CRC 失败、后台加载取消、延迟删除和 typed-token 错误分支仍未在当前端形成等价实现,合同继续为 `PARTIAL`。
|
||
|
||
### Active EterPack/EterLocale/GameLib resource review
|
||
|
||
- `EterPack/EterPack.h`/`EterPackManager.h`/`EterPackCursor.h` 明确了 EPKD v2 header/index、filename CRC 字典、root/dir pack、`SEARCH_FILE_FIRST`/`SEARCH_PACK_FIRST`、静态 cache、mapped read、读写/删除/Extract 和 pack policy;`EterPackPolicy_CSHybridCrypt.h` 还定义 Camellia/Twofish/XTEA、每文件 key、SDB 和密钥流生命周期。当前自定义 XChaCha20+Zstd `.epk` 不能互读这套格式。
|
||
- `EterLocale/CodePageId.h` 覆盖 CP874/932/936/949/950/1250-1258/65001;`StringCodec.h`、Vietnamese/Arabic/Japanese 头文件分别暴露 code-page 转换、越南语编解码、阿拉伯语 shaping 和 Shift-JIS lead/trail/比较规则。当前 locale 以 UTF-8 `FileAccess` 为主,没有这些非 UTF-8/RTL/Shift-JIS 分支。
|
||
- `GameLib/Property*`、`ItemManager.h`、`RaceManager.h`、`RaceMotionData.h` 连接 property CRC、物品/种族/动作资源到 pack;当前 proto/MSM/MSA 格式能读不等于完整 `CPropertyManager`/`CRaceManager`/`CItemManager` 资源注册链已一致。
|
||
- 本轮完成 EterPack、EterLocale 和 GameLib 资源声明/生命周期静态核对,合同保持 `PARTIAL`。
|
||
|
||
### Active EterLib image/font resource review
|
||
|
||
- `EterLib/Util.cpp` 的 `LoadTextData`/`LoadMultipleTextData` 直接通过 `CEterPackManager` 读文本;同一文件还依据 code page 选择 charset/font face。`TargaResource.cpp`、`JpegFile.cpp`、`GrpImage.cpp`/`GrpImageTexture.cpp`/`GrpSubImage.cpp` 负责从资源字节到图像、纹理、子图和 GPU 纹理的 load/clear 生命周期。
|
||
- `GrpDIB.cpp`/`DibBar.cpp`/`BlockTexture.cpp` 是 Windows DIB 到分块纹理的路径,涉及 clip、invalidate、锁定/解锁和最大纹理尺寸;当前 UI/资源侧使用 Godot `ImageTexture`/atlas,缺少 DIB/分块上传、纹理丢失重建及 pack 失败分支的等价证据。
|
||
- 本轮完成 EterLib 文本/图像资源活跃文件静态核对;当前格式解析通过不等于 `CResourceManager`→图像纹理→UI 的同一资源和清理链,合同继续为 `PARTIAL`。
|
||
|
||
### Active UserInterface locale/font bridge review
|
||
|
||
- `UserInterface/Locale.cpp`/`.h` 与 `Locale_inc_*` 通过编译期区域选择 code page、字体、语言、地图/货币/日期和功能开关;`GameType.cpp` 的 `DefaultFont_Startup/Cleanup/ReloadDefaultFonts` 从 `CResourceManager` 重载默认/斜体字体,`SetGuildSymbolPath` 参与会徽资源入口。
|
||
- 当前 `locale.gd` 以运行时 lang/UTF-8 文本表为主,中文路径可用,但没有编译期 locale 变体、code-page 字体选择、默认字体资源重载和 locale-specific feature flags 的同一来源。
|
||
- 本轮完成 UserInterface locale/font bridge 静态核对;资源合同继续为 `PARTIAL`。
|
||
|
||
## Status
|
||
|
||
`PARTIAL`。proto 和多种格式读取已经有较好基础,但当前运行时资源主链、40250 EterPack 格式/优先级、locale 编码、完整失败分支和统一虚拟读取入口尚未达到一致实现。
|