Files
mtgodot-poc/audit/contracts/resource.pack-locale-proto.md
T
shenandshen c93894313a fix: 装备属性面板避让逻辑 + 多项功能更新
- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧
- inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域
- 包含其他累积的功能开发和测试文件
2026-09-21 16:38:59 -07:00

128 lines
13 KiB
Markdown
Raw 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.
# 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 编码、完整失败分支和统一虚拟读取入口尚未达到一致实现。