Files
mtgodot-poc/audit/contracts/item.refine-socket-costume.md
T
shenleiandClaude Opus 5 a989b8f19b chore: delete 106 unreachable *_system.gd and their parity tests
These files were loaded only by their own test_*_parity.gd and never by
the runtime (transitively checked, incl. client_phase_integration_system).
Removes 76 manifest implementation/evidence references across 18
contracts; contract docs get a note that conclusions based on them are
void. Live systems kept: chest, consumable, item_attr, metin_socket,
whisper_chat.

Full headless suite: no new failures (7 failing + 5 headless-hanging
render tests are identical before the deletion).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 17:51:52 +09:00

127 lines
17 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.
# item.refine_socket_costume
## Scope
审计强化窗口、强化请求与回包、普通/魔石/首饰镶嵌、属性添加与洗炼、时装栏位与外观更新,以及当前端额外的幻化/衣柜逻辑。判断标准不是本地字典能否算出相同结果,而是是否复现 40250 的物品原型校验、请求协议、服务器权威结果、`GC_ITEM_*` 状态更新、角色部件刷新、特效/资源和失败清理链。
## Reference call chain
- `UserInterface/PythonNetworkStreamPhaseGame.cpp` / `PythonNetworkStreamModule.cpp`
- `SendRefinePacket` 发送 `CG_REFINE(pos,type)`;40250 取消强化使用 `(255,255)`,写包后进入序列发送。
- `RecvRefineInformationPacket` 解析旧版 `GC_REFINE_INFORMATION(95)`;`RecvRefineInformationPacketNew` 解析新版 `GC_REFINE_INFORMATION_NEW(119)`,两者都把服务端给出的源物品、结果 vnum、费用、概率和材料交给 `OpenRefineDialog`。
- 强化信息由服务器提供,客户端不拥有强化成功率、结果 vnum、费用或材料公式。
- `UserInterface/Packet.h` / `GameLib/ItemData.cpp` / `PythonItemModule.cpp`
- 旧、新强化包的 wire layout 不同;`ITEM_USE` 子类型包括 `USE_TUNING`、`USE_CLEAN_SOCKET`、`USE_CHANGE_ATTRIBUTE`、`USE_ADD_ATTRIBUTE`、`USE_ADD_ATTRIBUTE2`、`USE_ADD_ACCESSORY_SOCKET`、`USE_PUT_INTO_ACCESSORY_SOCKET` 等。
- ItemData 提供 `GetSocket`、`GetSocketCount`、`GetRefine`、`GetRefinedVnum`、`GetValue`、flags/anti-flags、装备类型和外观资源;客户端用这些信息做 UI/目标校验,不替代服务端物品变更。
- `UserInterface/PythonPlayer.cpp` / `PythonPlayerModule.cpp` / `PythonNetworkStreamPhaseGameItem.cpp`
- `SetItemData` 对非零 vnum 先验证 ItemManager;`SetItemCount`、socket、attribute 按服务器回包写入本地物品,并刷新窗口。
- `CanRefine`、`CanAttachMetin` 等只依据 ItemManager 的真实 type/subtype/flag/socket 数据判断允许与否,然后由物品使用协议请求服务端执行。
- 物品更新顺序是服务器回包驱动的 count、socket、attribute 与窗口刷新,不是 UI 或脚本直接扣除材料。
- `UserInterface/InstanceBase.cpp` / `PythonNetworkStreamPhaseGameActor.cpp`
- 角色的 armor、weapon、hair 部件来自角色同步包;`SetArmor` 通过 ItemData 映射模型、specular 和强化特效,`SetHair`/weapon attachment 刷新实际角色模型。
- `__GetRefinedEffect` 按 ItemData 的 refine/socket/type/subtype 选择并挂接/清理强化特效。
- `Client/Eternexus/root/uirefine.py` / `uiinventory.py`
- 强化窗口只负责显示服务器给出的 refine table、材料库存和警告;确认/取消均发送网络请求。属性、清孔、首饰孔和物品使用入口同样依赖 `item_proto` 的 use type 与服务器 `GC_ITEM_*` 回包。
- 服务器权威位于 `Server/metin2/src/server/game/src/char_item.cpp`、`item.cpp`、`item_attribute.cpp` 及 `item_proto`。这些文件决定属性池、概率、socket/强化限制、消耗和最终 item data;客户端源码没有对应的独立权威算法。
## Current call chain
- `project/ui/refine_ui.gd` 已接收 `M2Client.refine_ask`,显示材料/概率/费用并调用 `M2Client.refine`;`extension/src/net/classic/classic_session.cpp` 已能构造 `CGRefine`,`EntityStore` 已有 `RefineCue`。
- `extension/src/net/m2_client.cpp::refine` 只允许 `0 <= pos < INVENTORY_MAX_NUM`,所以 `RefineUI.cancel_refine()` 发出的 40250 取消哨兵 `255,255` 在 native 层直接返回 false,未到达服务器。
- `extension/src/net/classic/classic_parser.cpp` 已分别解析旧版 `GCRefineInfoOld`(95,59 字节,无 type)和新版 `GCRefineInfo`(119,60 字节,有 type),并将两者送入 `EntityStore::mut_refine`;旧版 wire layout 本身已补齐,但旧版真实服务器回包和类型默认值仍缺端到端 fixture。
- `project/item_attr_system.gd`、`item_enchant_system.gd` 直接在传入 `Dictionary/Array` 上生成/重置普通和稀有属性并扣本地卷轴;没有调用 `M2Client::use_item_to_item`、等待 `GC_ITEM_UPDATE` 或从 ItemManager 读取真实 use subtype/attribute set。
- `project/jewelry_socket_system.gd` 直接用硬编码 vnum、首饰 vnum 前缀、成功率、寿命和 bonus tier 修改 sockets/gem_times;没有通过 40250 的 `ITEM_USE`/`USE_ADD_ACCESSORY_SOCKET`/`USE_PUT_INTO_ACCESSORY_SOCKET` 请求和服务器更新闭环。
- `project/costume_system.gd` 维护独立的 `equipped_costumes`,复制传入时装、默认本地 7 天计时并计算 bonus/model_id;未发现它接入 `M2Client`、EntityStore 的 equipment 回包或 `project/ui/equip_model.gd` 的角色部件更新。
- `project/costume_attr_transfer_system.gd` 和 `project/wardrobe_transmog_system.gd` 直接修改库存、金币、属性和 `transmog_*` 字段。现有 40250 客户端源码中没有与 `wardrobe_transmog_system.gd` 对应的幻化协议/角色字段,因此它是当前端自定义扩展,不应标记为 40250 已实现。
- `project/refine_effect_system.gd` 有本地强化光效规格和粒子测试,但尚未证明由权威装备回包触发,且当前模型/角色路径与 40250 的 `InstanceBase` 部件、ItemData 和 effect attach 链未形成端到端测试。
## Branch matrix
| 场景 | 40250 | 当前端 | 结论 |
|---|---|---|---|
| 打开强化窗口 | 服务器发送旧/新版 refine info;客户端按 packet layout 交给 UI | native parser 已分别接受 95/119 并生成 cue | PARTIAL:旧版真实回包与 UI 行为缺 fixture |
| 强化确认 | UI 发 `CG_REFINE(pos,type)`,结果和消耗由服务器回包决定 | 正常 pos/type 可发 `CGRefine` | PARTIAL:缺完整成功/失败后的 item/point/effect 顺序证明 |
| 强化取消/超距/ESC | 发送 `CG_REFINE(255,255)` | `RefineUI` 调用 `refine(255,255)`,native 范围校验先拒绝 | GAP |
| 强化数据来源 | `src_vnum/result_vnum/cost/prob/materials` 全部服务端提供 | `RefineCue` 能保存字段,窗口能显示 | MAPPED + tests:旧包与真实回包覆盖不足 |
| 普通/魔石/首饰镶嵌 | ItemManager use subtype/flag/socket 校验后发物品使用请求,服务器改 item data | 本地系统按字典直接改 socket/attrs | GAP |
| 属性添加/洗炼/第五属性 | 客户端显示 ItemData/回包,成功率和属性生成在服务器 `char_item.cpp/item_attribute.cpp` | 本地硬编码池/概率并直接写 attrs | GAP:算法复制不等于客户端协议实现 |
| 稀有属性 6/7 条 | 在当前 40250 客户端引用中未找到与当前 71151/71152 本地系统相同的完整客户端链 | `ItemEnchantSystem` 本地新增稀有池并直接扣卷轴 | CUSTOM/PARTIAL,不能宣称 40250 1:1 |
| 时装穿戴/卸下 | 角色同步包给 armor/weapon/hair 部件,ItemData/InstanceBase 刷新模型与强化效果 | `CostumeSystem` 只更新本地字典和信号;未接 actor/model 回包 | GAP |
| 时装属性/限时 | 服务器 item data/affect/point 回包决定状态 | 本地 `bonuses` 与 `remaining_time` 逐帧扣减 | PARTIAL:缺权威计时、point/affect 和过期回包 |
| 幻化/衣柜 | 40250 客户端源码未找到相同协议或 `transmog_*` 角色部件链 | 本地直接扣金币/卷轴/皮肤并改字段 | CUSTOM,不属于已验证的 40250 parity |
| 强化外观效果 | `InstanceBase::__GetRefinedEffect` 依 ItemData 的 refine/socket/type/subtype attach/clear effect | 本地 `RefineEffectSystem` 可计算规格并创建粒子 | PARTIAL:资源、部件更新和清理未闭环 |
## Implementation-equivalence matrix
| 项目 | 状态 | 证据/限制 |
|---|---|---|
| Preconditions | PARTIAL | refine 基础 pos 校验存在;取消哨兵被错误拒绝;属性/socket/costume helper 不统一使用 ItemManager 的 type/subtype/flag/anti-flag/equipment slot 校验 |
| Branch structure | PARTIAL | 强化 UI 分支和 native 请求存在;本地属性、镶嵌、时装操作绕过 `CG_ITEM_USE`/`GC_ITEM_UPDATE`,并增加了参考端不存在的幻化分支 |
| Algorithms/formulas | PARTIAL | 40250 的强化、属性、socket 结果由服务器权威计算;当前本地池/概率/寿命只通过孤立测试,不能证明与运行时服务端一致 |
| State transition order | GAP | 参考是请求→服务器判定→`GC_ITEM_*`/point/affect/actor update→刷新;当前多个 helper 是本地立即写入和发 signal |
| Constants/units | PARTIAL | refine packet header/字段和部分 vnum 已定位;socket 前缀、gem tier、时装 7 天、幻化 1000 万等硬编码未由当前 40250 运行时数据链证明 |
| Timing/event sources | PARTIAL | RefineCue/signal/UI 事件存在;旧包、取消包、真实 item update/actor update 顺序和过期竞态未覆盖 |
| Resource/data sources | PARTIAL | 当前可读部分 item proto/模型数据;属性池、socket 规则和 costume model_id 多来自脚本常量,未接 40250 ItemData/资源选择链 |
| Protocol side effects | PARTIAL | `CGRefine` 和基础 refine cue 存在;取消 sentinel、旧 refine header、item-use/socket/costume request、SendSequence/失败反馈未完整闭合 |
| Interruption/failure/cleanup | GAP | 本地操作成功前就能改变库存/金币/attrs/sockets;网络拒绝、重复回包、断线、换图、时装过期和 actor model 清理没有同一权威回滚路径 |
## Regression evidence
- `project/test_refine_parity.gd`:通过,覆盖窗口显示、材料颜色、确认/取消 UI 规则和超距行为;使用 fake client,未证明 native `255,255` 取消包真实发出,也未覆盖旧版 95 包。
- `project/test_refine_effect_parity.gd`:25/25 通过,覆盖本地强化等级和粒子规格;未覆盖 40250 `InstanceBase` 真实 ItemData 部件更新、attach/clear 和资源加载失败。
- `project/test_item_attr_parity.gd`:28/28 通过;`project/test_item_enchant_manager_parity.gd`:36/36 通过。两者都直接对 `Dictionary` 做本地生成/扣除,未启动服务器或消费 `GC_ITEM_UPDATE`。
- `project/test_jewelry_socket_parity.gd`:23/23 通过;覆盖本地打孔/镶嵌/寿命计算,但没有 40250 物品使用协议、ItemData socket count、服务器拒绝或回包。
- `project/test_costume_system_parity.gd`:23/23 通过;`project/test_costume_attr_transfer_parity.gd`:23/23 通过;`project/test_wardrobe_transmog_parity.gd`:22/22 通过。它们验证本地 helper 的字典和 signal 行为,不能证明角色模型、服务端库存/金币或 40250 客户端协议等价。
- native/refine 现有测试覆盖 `CGRefine`/cue 基础布局,但未覆盖 `GC_REFINE_INFORMATION(95)` 与 `(119)` 双版本解析、`255,255` 取消发送和 refine 后 item/point/affect/actor 更新顺序。
## Deep audit round 2026-09-21
### Fresh regression results
- `godot --headless --path project --script test_refine_parity.gd`:退出码 0;强化窗口/材料/确认/取消 UI 规则通过,但 fake client 不能证明 native 取消包真正发出。
- `godot --headless --path project --script test_refine_effect_parity.gd`:退出码 0,25/25 通过;只证明本地强化等级到粒子规格的映射。
- `godot --headless --path project --script test_item_attr_parity.gd`:退出码 0,28/28 通过;只证明本地属性字典算法。
- `godot --headless --path project --script test_item_enchant_manager_parity.gd`:退出码 0,36/36 通过;只证明本地普通/稀有属性管理器的直接修改路径。
- `godot --headless --path project --script test_jewelry_socket_parity.gd`:退出码 0,23/23 通过;只证明本地首饰打孔、镶嵌和寿命计算。
- `godot --headless --path project --script test_costume_system_parity.gd`、`test_costume_attr_transfer_parity.gd`、`test_wardrobe_transmog_parity.gd`:均退出码 0,分别 23/23、23/23、22/22 通过;验证的是本地时装/属性转移/幻化字典状态,不是服务器权威链路。
- `godot --headless --path project --script guild_refine_test.gd`:退出码 0,确认 UI 取消会调用 `client.refine(255,255)`。
- `./build/extension/net_classic_session_test`、`./build/extension/net_entity_test`:均退出码 0;native 基础 refine/DS refine 包和新版 cue 通过。源码复核确认 parser 已分别支持旧版 95(59 字节)与新版 119(60 字节)。
### Static parity findings
- 当前明确的协议缺陷仍是 `M2Client::refine` 要求 `0 <= pos < INVENTORY_MAX_NUM`;因此 `RefineUI.cancel_refine()` 发出的 40250 取消哨兵 `(255,255)` 在 native 层被拒绝,`ClassicSession::send_refine` 虽可编码该字节,但 UI 入口到不了它。
- 旧/新版 refine parser 已经分别存在,先前“旧版 95 包被统一结构丢弃”的判断已纠正;尚缺旧版实际字节 fixture、旧包 `type=0` 对 UI 分支的行为和成功/失败后的 item/point/actor 更新时序。
- 40250 的属性、socket、装备外观和时装穿戴依赖 ItemManager/ItemData + `CG_ITEM_USE`/`CG_ITEM_USE_TO_ITEM` + 服务器 `GC_ITEM_*`/affect/point/character parts;当前 `ItemAttrSystem`、`ItemEnchantSystem`、`JewelrySocketSystem`、`CostumeSystem` 仍能直接修改传入字典、金币、socket、attributes 或本地计时。
- 当前 `CostumeSystem` 的 bonus/model/7 天过期以及 `wardrobe_transmog_system.gd` 的金币/卷轴扣除都没有连接 EntityStore equipment/point/affect 回包;幻化分支在 40250 源码中没有对应协议,应继续标记为 CUSTOM,而不是 parity 已实现。
- `EquipModel` 存在由 equipment 状态刷新强化光效的路径,但本轮没有证明 refine 后 `GC_ITEM_UPDATE` 与 character part/模型资源更新按 40250 顺序触发,资源失败和清理也未闭合。
### Round conclusion
本轮仍为 `PARTIAL`:旧/新版 refine 解析已确认存在,强化/DS 基础包和局部 UI 测试通过;但取消 sentinel 是明确失败点,本地属性/镶嵌/时装/幻化旁路仍绕过服务器权威结果,真实 item/point/affect/actor 更新和失败清理尚未与 40250 统一。本轮未修改实现代码。
## Next audit round 2026-09-20T18:19Z
本轮对强化/镶嵌/时装链进行一次入口级复核,重点验证 UI 认为“已发出”的请求是否真的能穿过 native 层:
- `RefineUI.cancel_refine()` 按 40250 发送 `client.refine(255, 255)`,但 `M2Client::refine`(`extension/src/net/m2_client.cpp:2351-2356`)要求 `pos >= 0 && pos < INVENTORY_MAX_NUM`,因此取消请求在 native 入口直接返回 `false`;`ClassicSession::send_refine` 的字节编码能力无法弥补这个前置门。现有 `test_refine_parity.gd` 使用 fake client,不能捕获该失败。
- `RecvRefineInformationPacket`(95)和 `RecvRefineInformationPacketNew`(119)均已在 parser 中存在,本轮没有把此前的“旧包完全缺失”继续列为事实;但旧版真实字节 fixture、`type=0` 的 UI 分支和强化后 `GC_ITEM_UPDATE`/point/affect/actor 更新顺序仍没有证据。
- `ItemAttrSystem`、`ItemEnchantSystem`、`JewelrySocketSystem`、`CostumeSystem` 仍直接修改传入字典、属性、socket、金币或本地剩余时间;40250 客户端对应入口只做 ItemData 校验并发 `CG_ITEM_USE`/`CG_ITEM_USE_TO_ITEM`,结果由服务器回包驱动。局部脚本测试通过不能证明这两条链统一。
本轮没有修改实现代码。合同继续为 `PARTIAL`,其中取消 sentinel 是可直接复现的协议级差异,优先级高于继续增加本地属性公式测试。
## Status
`PARTIAL`。强化窗口已有可用的新版协议骨架和本地 UI 回归;属性、socket、时装和幻化的本地脚本测试通过,实际实现仍主要是本地字典状态机,尚未复现 40250 的 ItemManager→CG 请求→服务器权威结果→GC item/actor/model 更新完整链路。
## Implementation fix round 2026-09-21
- `mtnet::bounds::refine_request` 与 `M2Client::refine` 现在保留普通库存位置校验,同时按 40250 接受强化取消哨兵 `(255,255)`;`net_bounds_test` 覆盖普通位置、哨兵和非法混合值。
- `test_refine_parity.gd`/`guild_refine_test.gd` 仍证明 UI 会发出取消调用;native 回归现在证明该调用不会在入口被错误拦截。真实服务器回包、旧版 95 字节 fixture 和强化后权威 item/point/actor 顺序仍待补齐。
- `EntityStore` 的正 HP 点数更新会清除 `dead`,`M2Client` 会重置 `dead_seen`,保证复活后下一次死亡可以再次发出 `entity_dead`。
本合同继续为 `PARTIAL`:本轮只修复了可直接验证的协议边界和死亡状态边沿,没有把本地属性/镶嵌/时装旁路误标为 40250 等价实现。
### 2026-09-22 死代码删除
以下文件只被自身测试引用、运行时从不加载,已删除;本文中以它们为实现或证据的结论作废,相关行为需按 40250 源码单元重新移植:`costume_attr_transfer_system.gd`, `costume_system.gd`, `item_enchant_system.gd`, `jewelry_socket_system.gd`, `refine_effect_system.gd`, `test_costume_attr_transfer_parity.gd`, `test_costume_system_parity.gd`, `test_item_enchant_manager_parity.gd`, `test_jewelry_socket_parity.gd`, `test_refine_effect_parity.gd`, `test_wardrobe_transmog_parity.gd`, `wardrobe_transmog_system.gd`。