- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
13 KiB
13 KiB
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。
- 对 pack 内数据提供有边界的
UserInterface/PythonPackModule.cpppack.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/、Godotassets.zip/PCK 和asset_index.txt是主运行时资源入口。
- loose
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 主链调用。
- 实现当前 fork 自定义 XChaCha20 + Zstd
extension/src/proto/proto.cpp/proto_node.cpp- 读取 MIPX/MIPT/MMPT、CLZO 和 proto key,建立 item/mob 索引。
project/locale.gd- 从
locale/locale/<lang>直接用 GodotFileAccess读取 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 固定字段;itemvalues[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 单文件格式,40250CEterPack使用其原有 pack header/index/压缩/加密/策略链;两者不能互读。它是明确的平台/格式替换,不是已证明等价的适配。 - 40250 的
SEARCH_FILE_FIRST/pack-first、根包与目录包、CRC 字典、静态缓存、文件覆盖优先级和 pack 内失败语义没有在统一运行时入口中闭环;AssetResolver的固定目录 rank 不能直接证明与所有 40250 pack 注册顺序一致。 EterLocale按 code page/locale 变体转换文本;当前locale.gd直接按 UTF-8FileAccess读取。中文 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.gd16/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-8FileAccess为主,没有这些非 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/资源侧使用 GodotImageTexture/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 编码、完整失败分支和统一虚拟读取入口尚未达到一致实现。