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

106 lines
12 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.drop_pickup
## Scope
审计地面掉落物从 `GC_ITEM_GROUND_ADD`、所有权更新、模型/特效/名条创建,到鼠标点选、走近、500ms 拾取节流、权限拒绝、`CG_ITEM_PICKUP``GC_ITEM_GROUND_DEL` 和拾取后物品回包的完整链路。
## Reference call chain
- `PythonNetworkStreamPhaseGameItem.cpp`
- `RecvItemGroundAddPacket` 读取坐标、VID、vnum,转换全局/本地坐标后调用 `CPythonItem::CreateItem`
- `RecvItemOwnership` 调用 `CPythonItem::SetOwnership``RecvItemGroundDelPacket` 调用 `CPythonItem::DeleteItem`
- `PythonItem.cpp` / `ItemData.cpp` / `ItemManager.cpp`
- `CreateItem` 先通过 `CItemManager::GetItemDataPointer` 验证 vnum,使用 `CItemData::GetDropModelThing` 创建实际掉落模型,按地形高度/法线处理位置,并挂接 drop effect、掉落声音和掉落动画状态;名条使用 item data 名称。
- `SetOwnership` 只更新已有 VID 的 ground instance 和名条;未知 VID 不创建对象。
- `DeleteItem` 同时删除模型实例和 `CPythonTextTail` 名条;`DeleteAllItems` 用于清理整张地图。
- `PythonPlayer.cpp` / `PythonPlayerInputMouse.cpp`
- `SendClickItemPacket` 在观战模式直接拒绝,500ms 节流;读取地面物品 owner、vnum 和 ItemManager anti-flag,非本人且不满足组队/可掉落可赠送条件时触发 `OnCannotPickItem`,否则发送 `SendItemPickUpPacket`
- `__OnPressItem` 清理旧预约和自动攻击;距离足够时发送 WAIT 状态和拾取请求,否则建立 `MODE_CLICK_ITEM` 预约并持续走近。
- `__ProcessClickItem` 到达约 20 像素内时发送 WAIT、`SendClickItemPacket`、停止并清理预约;物品消失则清理预约。
- `PythonTextTail.cpp` / `CPythonItem::__Pick`
- 鼠标拾取先测试实际掉落模型的相交,再回退到文本尾标拾取;owner 名条独立显示并参加尾标排布。
## Current call chain
- `extension/src/net/classic/classic_parser.cpp` / `EntityStore`
- 解析 `GC_ITEM_GROUND_ADD``GC_ITEM_GROUND_DEL``GC_ITEM_OWNERSHIP`,写入 `GroundItem{vid,vnum,x,y,z,owner}` 并通过 `M2Client` 信号同步。
- 当前 parser/store 不查询 ItemManager 以拒绝未知 vnum;未知掉落仍可进入当前状态。
- `project/ui/ground_items.gd`
- `_on_added` 生成通用 `item_bag.gr2` 或 BoxMesh,占位模型;按 MapCoord 和地形采样设置位置,创建名字/owner Label3D、稀有光柱和名条防重叠。
- `_on_removed`/`clear_for_map_change` 删除 Godot 节点;`_process` 旋转占位网格、按距离改名字颜色并重新排布尾标。
- `try_pickup`/`try_pickup_vid` 有 3m 步行、5m 骑马、最近优先、500ms 节流和 owner 名字拦截;然后直接调用 `client.pickup_item(vid)`
- `pick_at` 用投影后的模型点、名字标签和 owner 标签的 64px 最近点选择,不是参考端的实际模型相交 + TextTail Pick。
- `project/net_play.gd` / `player_controller.gd`
- `pick_ground_item` 在约 20m?(代码使用 `CLICK_ITEM_PICKUP_CM` 换算)内立即发 WAIT 并尝试拾取,否则建立走近预约;有观战拦截和自动攻击清理。
- `extension/src/net/m2_client.cpp` / `classic_session.cpp`
- `pickup_item` 只检查 `is_in_game`、正 VID 和 u32 范围,再发送 `CG_ITEM_PICKUP`;没有参考端的 observer、item proto、owner/party、anti-flag 或 action gate。
- `ground_item_added`/`ground_item_removed`/`item_picked_up` 信号存在,但未证明 `GC_ITEM_GROUND_DEL` 后 inventory item set/update 的严格反馈顺序。
## Branch matrix
| 场景 | 40250 | 当前端 | 结论 |
|---|---|---|---|
| 掉落创建 | ItemManager 验证 vnum,按 item proto 的 drop model、地形/法线、drop effect、声音和动画创建 | parser/store 建立记录;UI 通用 bag/BoxMesh,无实际 item drop model、effect、sound、drop animation | GAP |
| 坐标/地形 | 全局坐标转换后按 Background height/normal 设置模型位置 | MapCoord 转换并 `sample_height + 0.1`,没有法线、模型包围盒和掉落动画落点 | PARTIAL |
| 所有权更新 | 只更新已存在 VIDowner 进入独立 TextTail owner 实例 | `mut_ground_owner` 更新并重新发 ground addedUI 重建/更新 owner tag | MAPPED + tests |
| 名称与颜色 | 名称取 `CItemData::GetName`,颜色/尾标来自 item data 和 TextTail | 名称优先 proto/item_listgrade/color 有适配,但 `_process` 近距离颜色会覆盖原品阶色 | PARTIAL |
| 名条点击 | 实际模型 `Intersect`,再 TextTail `Pick` | 64px 投影点近邻,可能选中不可见/非相交对象或错误标签 | PARTIAL |
| 近距离拾取 | 距离约 20 像素内发送 WAIT+pickup;远距离建立 MODE_CLICK_ITEM 走近 | `pick_ground_item` 有走近预约和 WAIT`GroundItems` 又有 3m/5m 阈值,两个距离层未统一证明 | PARTIAL |
| 最近拾取/按键 | `GetCloseItem`/预留动作按参考距离和对象选择 | 最近优先、3m/5m 和 500ms 已有本地测试 | MAPPED + tests |
| 所有权拒绝 | owner 不同且非 party/anti-flag 允许时本地拒绝并回调 | 只比较 owner 字符串与本地名字;没有 ItemManager anti-flag 和 party member 分支 | PARTIAL |
| 观战/行动门 | `IsObserverMode` 和主角行动状态阻止拾取 | NetPlay 有 observernative `pickup_item` 未有统一行动门 | PARTIAL |
| 拾取协议 | `SendItemPickUpPacket` 写包并 `SendSequence` | ClassicSession 构造 VID 包,但没有序列/副作用等价保证 | PARTIAL |
| 拾取成功/失败清理 | server GC ground del 删除模型和名条;后续 item get/set/update 刷新背包 | ground del 删除 Nodeitem event/物品信号存在;失败、超时、重复回包和预约清理缺少包级测试 | PARTIAL |
| 换图/断线 | `DeleteAllItems` 和阶段清理释放全部 ground instance/text tail | `clear_for_map_change` queue_free;延迟释放/generation/断线顺序未完全证明 | PARTIAL |
## Implementation-equivalence matrix
| 项目 | 状态 | 证据/限制 |
|---|---|---|
| Preconditions | PARTIAL | stage/VID、距离、owner 名和 observer 有部分检查;ItemManager vnum/anti-flag/party/action gate 缺失 |
| Branch structure | PARTIAL | add/owner/del、最近拾取、走近预约和移动端入口存在;实际模型拾取、权限分支和 drop animation 分支不等价 |
| Algorithms/formulas | PARTIAL | 坐标转换、距离、500ms、尾标布局已覆盖;模型包围盒/法线/近似拾取和颜色更新不同 |
| State transition order | PARTIAL | reference 是 add→model/text tail→ownership→pickup→server del→inventory feedback;当前 signal→Node 与延迟 queue_free/双距离层顺序未端到端证明 |
| Constants/units | PARTIAL | 3m/5m/500ms 有测试;参考 20 像素、真实 item model 尺寸和服务端 cm/m 换算还需统一 fixture |
| Timing/event sources | PARTIAL | 当前依赖 Godot `_process`、signals、预约;参考由网络收包、输入状态机、Item/Actor/TextTail 事件驱动 |
| Resource/data sources | GAP | 当前 ground model 使用通用 item bag/BoxMesh,没有使用 `CItemData::GetDropModelThing`、drop effect、drop sound、法线等资源链 |
| Protocol side effects | PARTIAL | add/del/ownership/pickup packet 基础结构已解析;SendSequence、OnCannotPickItem 全分支和拾取后回包顺序未闭合 |
| Interruption/failure/cleanup | PARTIAL | map change 清理有实现;server 拒绝、重复 ownership、延迟删除、断线和旧 VID generation 尚未完整覆盖 |
## Regression evidence
- `project/test_ground_pickup_parity.gd`:拾取距离、骑马距离、最近优先、500ms、owner 名拦截、稀有光柱和基础名条通过。
- `project/mob_death_drop_test.gd`:掉落坐标/地形高度、Yang 名称、拾取调用和死亡清理通过。
- `project/p2b_test.gd`:掉落节点、proto 名称、多条名条去重、owner 独立名条和颜色通过,但退出仍有 2 个 ObjectDB 泄漏。
- `project/text_tail_arrange_test.gd`:尾标 AABB 和 owner 排布通过。
- `extension/tests/net_entity_test.cpp``extension/tests/net_classic_session_test.cpp`ground add/del/owner 与经典物品包基础布局通过。
- 未覆盖:未知 vnum、真实 drop model/effect/sound、模型相交拾取、party + anti-flag 权限矩阵、observer/native action gate、CG_ITEM_PICKUP 后 GC_DEL→ITEM_GET/ITEM_SET 顺序、失败/重复/断线/换图竞态。
## Deep audit round 2026-09-20
### Fresh regression results
- `godot --headless --path project --script test_ground_pickup_parity.gd`:退出码 0;距离、骑马距离、最近优先、500ms、owner 拦截和基础名条通过。
- `godot --headless --path project --script mob_death_drop_test.gd`:退出码 0;死亡脱离、地面坐标/高度、Yang 命名和拾取调用通过。
- `godot --headless --path project --script p2b_test.gd`:退出码 0;地面掉落、名称、名条去重、owner 名条和颜色通过,但退出仍有 2 个 ObjectDB 实例泄漏。
- `godot --headless --path project --script text_tail_arrange_test.gd`:退出码 0;尾标 AABB 与 owner 排布通过。
- `godot --headless --path project --script test_drop_and_cursor_parity.gd`:退出码 0MapCoord heading、cursor 注册、占位模型创建和点选通过。
- `./build/extension/net_entity_test``./build/extension/net_classic_session_test`:均退出码 0;只证明 ground add/del/owner 和 pickup 包的基础 native 处理。
### Static parity findings
- 40250 `CPythonItem::CreateItem` 先用 `ItemManager` 校验 vnum,再加载 `GetDropModelThing`,创建 drop effect、drop sound、drop animation,并按地形法线调整实例;当前 parser/store 接受未知 vnum`GroundItems` 使用通用 `item_bag.gr2` 或 BoxMesh,未接 item-specific model/effect/sound/animation。
- 40250 的 `SetOwnership` 只更新已存在 VID`DeleteItem` 同时回收 ground instance 与 TextTail;当前 owner 更新通过 `ground_item_added` 信号重建/更新 Godot 节点,仍没有未知 vnum/未知 VID 的严格资源语义和 generation 保护。
- 40250 鼠标拾取优先测试真实掉落模型相交,再回退到 TextTail;当前 `pick_at` 只比较模型/名条/owner 标签的屏幕投影点距离 64px,可能选中不可见或非实际相交对象。
- 40250 `SendClickItemPacket` 有 observer、owner、party/anti-flag 和 action gate;当前 `GroundItems` 只比较 owner 字符串,`M2Client::pickup_item` 只检查游戏阶段、VID 和整数范围,缺少 ItemManager anti-flag、party 分支、native action gate 与 SendSequence 等价语义。
- 当前 `NetPlay` 的 20m 走近/等待路径与 `GroundItems` 的 3m/5m 拾取路径同时存在;它们都能发 `CG_ITEM_PICKUP`,但距离、预约清理、拒绝和重复/延迟回包尚未由同一状态机统一证明。
- 现有 `ground_item_removed``item_picked_up` 信号存在,但没有包级 fixture 证明 `CG_ITEM_PICKUP → GC_ITEM_GROUND_DEL → GC_ITEM_GET/SET/UPDATE` 的成功、失败、重复、断线和换图时序。
### Round conclusion
本轮仍为 `PARTIAL`:基础 ground add/del/owner/pickup 协议和局部 UI 回归通过,但真实掉落资源链、模型相交点选、权限矩阵、native action gate 与拾取后的权威回包链仍未与 40250 统一;本轮未修改实现代码。
## Status
`PARTIAL`。当前端已具备掉落状态、坐标、owner 名条、最近拾取和基础协议骨架,但通用占位模型替代了 40250 的 item proto drop model/effect/sound/animation,拾取权限和模型点选分支也不完整;现有本地回归不能标记为完整 1:1,本轮未修改实现代码。