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

12 KiB
Raw Blame History

item.drop_pickup

Scope

审计地面掉落物从 GC_ITEM_GROUND_ADD、所有权更新、模型/特效/名条创建,到鼠标点选、走近、500ms 拾取节流、权限拒绝、CG_ITEM_PICKUPGC_ITEM_GROUND_DEL 和拾取后物品回包的完整链路。

Reference call chain

  • PythonNetworkStreamPhaseGameItem.cpp
    • RecvItemGroundAddPacket 读取坐标、VID、vnum,转换全局/本地坐标后调用 CPythonItem::CreateItem
    • RecvItemOwnership 调用 CPythonItem::SetOwnershipRecvItemGroundDelPacket 调用 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_ADDGC_ITEM_GROUND_DELGC_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 有走近预约和 WAITGroundItems 又有 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.cppextension/tests/net_classic_session_test.cppground 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 接受未知 vnumGroundItems 使用通用 item_bag.gr2 或 BoxMesh,未接 item-specific model/effect/sound/animation。
  • 40250 的 SetOwnership 只更新已存在 VIDDeleteItem 同时回收 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_removeditem_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,本轮未修改实现代码。