- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
25 KiB
combat.death-exp-drop
Scope
比较普通攻击/技能命中后的动作事件、GC_DAMAGE_INFO、GC_DEAD、主角死亡、POINT_EXP 增量、GC_CREATE_FLY(FLY_EXP)、地面掉落、归属校验、重复事件、复活和地图切换清理。
Reference call chain
UserInterface/PythonNetworkStreamPhaseGame.cpp::RecvDamageInfoPacket- 读取目标 VID、伤害值和
EDamageFlag,交给角色管理器显示伤害/受击表现;DAMAGE_FLYING 等特殊 flag 进入击飞状态。
- 读取目标 VID、伤害值和
UserInterface/PythonNetworkStreamPhaseGame.cpp::RecvDeadPacket- 找到
CInstanceBase后调用Die;若 VID 是主角,非决斗时通知OnGameOver,并调用CPythonPlayer::NotifyDeadMainCharacter清除自动攻击目标。
- 找到
UserInterface/InstanceBaseBattle.cpp::CInstanceBase::Die- 卸马鞍、清 AFFECT、取消选中/目标,再进入
CActorInstance::Die;底层m_isRealDead门保证死亡动作只启动一次。
- 卸马鞍、清 AFFECT、取消选中/目标,再进入
UserInterface/PythonNetworkStreamPhaseGame.cpp::RecvPointChange- 对所有点数先
ShowPointEffect(Type, VID);主角更新CPythonPlayer::SetStatus和 HUD,等级/属性/技能/金币走专门刷新;POINT_EXP使用TPacketGCPointChange.amount作为本次权威增量。
- 对所有点数先
UserInterface/PythonNetworkStreamPhaseGame.cpp::RecvCreateFlyPacket- 只按
bType/startVID/endVID创建客户端飞行表现;经验数值仍来自POINT_EXP,经验球不能替代点数包。
- 只按
UserInterface/PythonNetworkStreamPhaseGameItem.cpp::RecvItemGroundAddPacket/RecvItemGroundDelPacket- 创建/删除地面物品、记录 vnum/坐标/owner;拾取请求由客户端发送,归属和结果由服务端裁决。
UserInterface/PythonPlayer.cpp::NotifyCharacterDead- 目标死亡时清除当前 target,避免死亡实体继续被攻击/显示目标框。
GameLib/FlyingData.cpp/PythonEffectModule.cppFLY_EXP只负责经验球视觉的起点、终点和资源类型,不本地计算经验。
Current call chain
extension/src/net/classic/classic_parser.cpp- 解析
GC_DAMAGE_INFO、GC_DEAD、GC_POINT_CHANGE、GC_CREATE_FLY、GC_ITEM_GROUND_ADD/DEL,写入EntityStore。
- 解析
extension/src/net/entity_store.cpp::mut_set_point/mut_dead/mut_ground_add- 保存主角绝对点数和
POINT_EXP.amount,设置实体 HP/dead,维护地面掉落队列和飞行 cue;take_exp_gain()只消费一次累计增量。
- 保存主角绝对点数和
extension/src/net/m2_client.cpp::_process/ classic mirror- 将
points_changed、vitals_changed、去重后的entity_dead、ground_item_added/removed、fly_cue和damage发给 GDScript。
- 将
project/net_world.gd::_on_dead/_on_damage/_on_fly- 隐藏 HP/名字/目标效果,标记死亡并延迟渐隐尸体;显示受击表现;对
FLY_EXP生成三枚追踪经验球并指向本地主角。
- 隐藏 HP/名字/目标效果,标记死亡并延迟渐隐尸体;显示受击表现;对
project/net_play.gd::_on_points/_trigger_exp_gain/_clear_target- 优先使用
exp_gain,其次从绝对经验差计算反馈;HUD 更新经验/等级;目标死亡时清目标并记录经验球起点。
- 优先使用
project/ui/ground_items.gd- 贴地生成掉落物、显示 owner,并按 40250
GetCloseItem/SendClickItemPacket实现DISTANCE_APPROX300cm、VID tie-break、party/anti-flag 归属检查和pickup_item请求。
- 贴地生成掉落物、显示 owner,并按 40250
project/ui/death_ui.gd/project/ui/quickbar.gd- 主角死亡显示复活面板并禁用快捷栏;复活后的正 HP/vitals 解除面板。
Audit findings (2026-09-20)
已对齐的部分
GC_POINT_CHANGE的POINT_EXP.amount已在 classic 和 m2dev 两条解析路径保留并累计;表现层不再只比较绝对 EXP,因此跨等级 EXP 回零仍能显示增量。GC_DEAD、HP<=0、GC_DAMAGE_INFO、目标清除、死亡实体不可选/不可阻挡、地面物品添加/删除和 owner 拒绝路径已有实现。GC_CREATE_FLY的经验球只负责视觉;当前_trigger_exp_gain不再额外合成第二批经验球,避免一个服务端飞行包造成双倍表现。- 掉落物坐标转换、地面贴合、owner 独立文本、普通/骑马拾取距离以及服务端拾取请求已有回归证据。
材料差异
- 40250 主角收到
GC_DEAD时会执行OnGameOver、清除自动攻击并进入死亡 Actor 状态;当前entity_dead主要被 DeathUI/Quickbar 消费,NetPlay没有统一把主角死亡传给PlayerController.stop/动作锁和自动攻击清理,主角节点若正在点地/WASD 可能继续响应移动。这是和用户此前“死亡后仍有动作/控制”风险直接相关的 P0 差异。 - 40250
CInstanceBase::Die/CActorInstance::Die有m_isRealDead一次性门、清 affect、卸马鞍、OnUnselected/OnUntargeted;当前_on_dead依赖dead_seen和meta的外围去重,死亡视觉、NetWorld、PlayerController、技能/钓鱼/自动攻击状态没有一个共同的 death transition,重复GC_DEAD/HP<=0/重建时序仍缺端到端矩阵。 - 40250 的
RecvPointChange对每个点类型先显示 point effect,并按 POINT_LEVEL/属性/技能/金币走分支刷新;当前_on_points只完整处理经验、等级、HUD 数值和少量音效,普通 POINT_* 的上浮数字/point effect、金币 amount 专门反馈和属性/技能窗口刷新没有完全等价。 - 40250
RecvCreateFlyPacket要求起点和终点 actor 均存在,否则只丢弃视觉;当前为支持主角未在_by_vid的结构做了_local_node特判,这属于合理平台适配,但远端起点/终点缺失、目标坐标 fallback、地图切换时飞行队列清理仍需要协议级对照。 - 40250
CInstanceBase::Die并不负责本地计算经验;经验只来自服务端POINT_EXP.amount。当前离线/事件路径仍存在spawn_exp_fly和_trigger_exp_gain两套可调用入口,需要继续审计是否有任何非权威路径把本地掉落/经验反馈当成服务端结果。 - 当前掉落归属检查使用名称匹配,参考端由服务端 owner/name 裁决且
CPythonItem的创建、删除和拾取响应受网络包序约束;名字为空、重名账号、先拾取后 DEL、重连 catch-up 和 owner 更新的结果还没有包级记录。 - 当前死亡尸体使用固定 2.5 秒后 fade,参考端由
CInstanceBase/CharacterManager 的删除与 fade 生命周期驱动;服务端提前GC_CHARACTER_DEL、重新GC_CHARACTER_ADD、死亡后地图切换时,旧 tween、目标效果、经验球和动作状态的统一清理还未完全证明。
Equivalence matrix
| 项目 | 状态 | 备注 |
|---|---|---|
| Preconditions | PARTIAL | VID、目标、owner、点数索引和服务端权威增量已有检查;主角死亡门、起止 actor 和重连包序不完整 |
| Branch structure | PARTIAL | DAMAGE/DEAD/POINT_EXP/FLY_EXP/ground add-del 主分支已映射;POINT_* 专门刷新、主角 GameOver、重复死亡和归属边界未闭合 |
| Algorithms/formulas | PARTIAL | EXP amount 优先、绝对值 fallback、经验球追踪和拾取距离已对齐;死亡门、point effect 和 owner 裁决仍不同 |
| State transition order | PARTIAL | 网络包→EntityStore→signals→NetWorld/HUD 主顺序已对齐;主角死亡→停止输入/清自动攻击→复活恢复未统一 |
| Constants/units | PARTIAL | POINT_EXP.amount、经验球数量/速度和拾取范围已有实现;死亡 fade、动画持续时间和完整 point effect 常量未闭合 |
| Timing/event sources | PARTIAL | GC_CREATE_FLY 与 POINT_EXP 分离、points dirty 单次消费已测;真实攻击命中窗→死亡→经验→掉落包序仍缺记录 |
| Resource/data sources | PARTIAL | 伤害/死亡/经验球/掉落基础资源可用;完整死亡动作、point effect、掉落模型和 item/mob 失败回退未全等价 |
| Protocol side effects | PARTIAL | dead/point/fly/ground packets 已解析;主角 GameOver、自动攻击清除、point effect 和拾取结果副作用未全闭合 |
| Interruption/failure/cleanup | PARTIAL | 基本尸体 fade、删除和地图 reset 已有;重复死亡、MISS/无效目标、重连、复活、跨图和待飞行物清理仍需矩阵 |
Implementation fix round 2026-09-21
NetPlay::_on_entity_dead现在区分目标死亡和主角死亡;主角死亡会清理自动攻击、预约动作、技能/钓鱼状态,锁住PlayerController的点地与 WASD 输入,并阻止网络状态处理。PlayerController.set_dead增加死亡/复活状态门;正 HPvitals_changed会恢复主角控制。EntityStore与M2Client.dead_seen同时修复正 HP 清除死亡边沿,避免复活后下一次死亡信号丢失。test_combat_death_pickup_entry_parity.gd、netplay_test.gd、net_entity_test.cpp覆盖死亡、移动清理、复活恢复和再次死亡所需的状态边沿;本轮未宣称 POINT_* 特效、真实服务端包序和跨图清理全部等价。
本合同继续为 PARTIAL:经验/掉落权威链、完整死亡动画/复活 UI、真实服务器包序和异常清理仍需后续审计。
Regression evidence
extension/tests/net_state_queue_test:POINT_EXP.amount累计和一次性消费、死亡状态和网络状态队列。project/mob_death_drop_test.gd:怪物死亡目标/HP/名字/影子清理、掉落坐标贴地、owner/拾取/删除。project/combat_parity_test.gd:防御球、击退阻挡、攻击锁、伤害表现和死亡抗击退相关分支。project/playable_combat_test.gd:无目标、超距、死亡目标/自身、冷却和断线时不发送错误战斗请求。project/gamescene_test.gd:地图 reset 时待发攻击/飞行和 NetPlay 状态清理。
这些测试证明当前端的若干主链可工作,但没有真实 40250 服务器的同一场战斗包序、主角死亡后输入门、POINT_* effect 和重复/乱序事件记录,因此合同保持 PARTIAL。
Status
PARTIAL。经验增量丢失、掉落归属 fixture 和双重经验球等已确认问题已有修复;死亡/经验/掉落主链可运行,但主角死亡状态门、完整 point effect、服务端包序、重复/乱序/复活清理仍未达到 40250 的统一实现。
Audit round 2026-09-21T03:23Z — ground-pickup ownership and distance
本轮继续沿 40250 CPythonPlayer::PickCloseItem / SendClickItemPacket /
__OnPressItem、CPythonItem::GetCloseItem/SetOwnership 与当前
GroundItems::try_pickup/try_pickup_vid、NetPlay::pick_ground_item、
EntityStore::mut_ground_owner 对照,确认已有经验/死亡主链没有回归,但掉落拾取仍有三处实现差异:
- 40250 的所有权预检不是“只允许同名”。
SendClickItemPacket对非本人 owner 还会检查IsPartyMemberByName以及物品ITEM_ANTIFLAG_DROP | ITEM_ANTIFLAG_GIVE; 当前GroundItems只比较 owner 字符串和本地角色名,队伍成员掉落与 anti-flag 分支没有实现,因此会把 40250 允许请求的队伍物品提前拦截在客户端。 - 40250
GetCloseItem使用DISTANCE_APPROX的厘米距离,并在该版本函数体内固定以 300cm 判定;当前try_pickup使用 Godot 欧氏米制距离,且骑马时放宽到 5m。 两者在对角线边界、骑马拾取和同距离物品选择上可能选出不同 VID。 - 40250
__OnPressItem在点击路径先用GetGroundItemPosition和NEW_IsClickableDistanceDestPixelPosition判断,超距才建立预约;当前NetPlay::pick_ground_item有同类主路径,但公开的try_pickup_vid自身不再检查 距离,后续若被其它输入入口直接调用会绕过这个边界。
对照结果:CPythonItem::SetOwnership 和当前 mut_ground_owner 对未知 VID 都只忽略,
GC_ITEM_GROUND_ADD/DEL 的创建/删除主顺序一致;本轮 test_combat_death_pickup_entry_parity.gd、
mob_death_drop_test.gd、combat_parity_test.gd、playable_combat_test.gd、
gamescene_test.gd 与 net_state_queue_test 全部通过,但现有 fixture 只覆盖本人掉落,
没有覆盖队伍 owner、anti-flag、厘米近似距离、同距 tie-break、先拾取后 DEL 和真实服务端
攻击→死亡→经验→掉落包序。因此合同继续为 PARTIAL,下一轮修复候选是补齐 party/anti-flag
上下文并把拾取距离/选择规则收敛到 40250 的实际函数语义。
Implementation fix round 2026-09-21T04:10Z — ground-pickup parity
本轮先在 project/test_ground_pickup_parity.gd 中加入回归断言,修复前实际复现
6 项失败:队伍成员物品被错误拒绝、对角线 DISTANCE_APPROX 边界被欧氏距离拒绝、
同距物品没有按最高 VID 选择,以及 try_pickup_vid 可绕过点击距离门。随后按 40250
实际调用链完成修复:
GroundItems::_on_added保存掉落包或 item proto 的anti_flags;_can_pick_owned_item与CPythonPlayer::SendClickItemPacket一致,自己的 owner 直接允许,队伍成员仅在没有ITEM_ANTIFLAG_DROP | ITEM_ANTIFLAG_GIVE时允许, 其他 owner 仍触发cannot_pick_item。GroundItems::try_pickup改为厘米整数DISTANCE_APPROX,固定使用CPythonItem::GetCloseItem实际函数体的 300cm 边界;按升序 VID 遍历并用<=复刻同距时后者(最高 VID)覆盖前者。骑马传入的 500cm 参数不再错误放宽实际GetCloseItem的 300cm 硬边界。GroundItems::try_pickup_vid增加NEW_IsClickableDistanceDestPixelPosition对应的 150cm 自身距离门;NetPlay::CLICK_ITEM_PICKUP_CM和预约到达边界同步为 150cm,并在边界使用<=。
修复后通过:test_ground_pickup_parity.gd、netplay_test.gd、
mob_death_drop_test.gd、test_combat_death_pickup_entry_parity.gd。本轮仍未宣称
真实服务端攻击→死亡→经验→掉落包序、空 owner/重名、拾取竞争和重连 catch-up 已完全
等价,合同继续为 PARTIAL。
深审轮次(2026-09-20)
本轮重新沿 GC_DEAD -> entity_dead -> NetWorld/NetPlay/PlayerController 和
GC_PLAYER_POINT_CHANGE -> POINT_EXP.amount -> _trigger_exp_gain 跟踪正式连接:
EntityStore已把POINT_EXP.amount累加为一次性exp_gain,NetPlay._on_points优先消费该增量;因此“服务端确实下发 POINT_EXP 但跨等级回零后没有经验反馈”的旧问题已有防回归证据。GC_CREATE_FLY(FLY_EXP)仍只触发NetWorld._on_fly的视觉球,_trigger_exp_gain不再生成第二批球,权威经验和表现来源没有混用。- 当前
client.entity_dead在NetWorld中只消费_by_vid的实体;本地主角由GameScene以_local_node单独持有,NetPlay的entity_dead回调只在死亡目标等于当前目标时清目标,未统一调用PlayerController.stop、清自动攻击/预约动作、死亡 UI/GameOver 和复活恢复。该缺口仍是正式链路差异,不是测试 fixture 问题。 RecvPointChange的参考端对所有POINT_*先执行ShowPointEffect,再按等级、属性、技能、能量和金币分支刷新;当前_on_points主要更新 HUD/经验/等级,完整 point effect 与金币 amount 的专门副作用仍未闭合。
本轮执行 test_combat_death_pickup_entry_parity.gd、mob_death_drop_test.gd、
combat_parity_test.gd、playable_combat_test.gd、gamescene_test.gd 和
build/extension/net_state_queue_test 均通过;这些测试证明已有局部链路可工作,但没有把主角 GC_DEAD 门禁、
真实服务端包序和完整 POINT_* 分支纳入断言,因此合同继续保持 PARTIAL。
Deep audit round 2026-09-20
本轮重新从 40250 RecvDeadPacket、CInstanceBase::Die、RecvPointChange 和 RecvCreateFlyPacket 对到当前 parser/store/UI 消费链:
- 40250 主角
GC_DEAD会先触发非决斗OnGameOver、NotifyDeadMainCharacter清自动攻击,再进入一次性Die;当前entity_dead主要由NetWorld处理远端节点,主角由GameScene.player单独持有,尚未有统一入口把死亡同时传给PlayerController.stop、NetPlay 自动攻击/预约清理、死亡 UI 和复活恢复。 - 当前
POINT_EXP.amount的累计与一次性消费仍然正确,GC_CREATE_FLY(FLY_EXP)只做视觉,不会再造第二份经验;但 40250 对所有POINT_*先ShowPointEffect,并对等级/属性/技能/能量/金币有专门刷新,当前_on_points对普通 point effect、金币 amount 和属性/技能窗口刷新仍不完整。 - 当前地面掉落和 owner/拾取主路径通过了本轮回归,但名字匹配、重名/空 owner、先拾取后 DEL、重连 catch-up、重复死亡/重新 Add、复活和跨图时序仍没有真实包级证据。
- 本轮
test_combat_death_pickup_entry_parity.gd、mob_death_drop_test.gd、combat_parity_test.gd、playable_combat_test.gd、gamescene_test.gd和net_state_queue_test均退出码 0;通过项覆盖局部战斗门、死亡怪物、掉落、经验增量和 map reset,不覆盖主角GC_DEAD门禁、完整POINT_*副作用及服务端攻击→死亡→经验→掉落包序。
结论:经验增量和基础掉落/死亡表现已可回归,但主角死亡状态门、完整 point effect、重复/乱序/复活清理和真实服务器包序仍与 40250 不同,合同保持 PARTIAL。
Audit round 2026-09-20T19:03Z
本轮重新核对 RecvDeadPacket、CInstanceBase::Die、RecvPointChange、
RecvCreateFlyPacket 与当前 parser/store/bridge 的完整边界,并重跑死亡、经验和掉落回归:
- 上一轮补的主角死亡门现在已形成可验证闭环:
NetPlay::_on_entity_dead清除自动攻击、 预约动作、技能/钓鱼状态并调用PlayerController.set_dead(true);正 HP 的vitals_changed和GC_MAIN_CHARACTER清除死亡状态。相关测试全部通过,原先“死亡后仍继续 WASD/点地” 的差异不再列为当前未修复项。 POINT_EXP.amount仍是唯一经验增量来源,GC_CREATE_FLY(FLY_EXP)只创建视觉飞行物;_trigger_exp_gain不再重复生成经验球,经验增量消费测试通过。- 当前端仍没有 40250
ShowPointEffect(type, vid)的 typed event。所有点数最终合并进points_changed,没有按POINT_STAT_RESET_COUNT、四维属性、技能/坐骑技能、能量、金币 等分支保留“先显示点效果、再刷新对应窗口”的事件来源;POINT_GOLD.amount也没有 等价的OnPickMoney(amount)信号。 fly_cue的主角 end VID 适配和缺失 actor 丢弃已有明确分支,但当前NetWorld仍把 服务端 FLY_EXP 组合成固定三颗 Godot 光球,真实.fly/索引飞行资源、地图切换中的飞行 队列清理以及 start/end actor 包序没有 live 服务端证据。- 地面物品、owner 和拾取距离测试通过,但空 owner/重名、先 PICKUP 后 DEL、重复 DEAD/ADD、 重连 catch-up 和跨图清理仍未由同一套包序 fixture 证明;这些仍属于服务端权威边界, 当前本地 helper 不能替代协议证据。
本轮执行 test_combat_death_pickup_entry_parity.gd、mob_death_drop_test.gd、
combat_parity_test.gd、playable_combat_test.gd、gamescene_test.gd、
net_state_queue_test 和 net_entity_test,均退出码 0;合同继续为 PARTIAL。
Implementation fix round 2026-09-20T19:17Z
本轮按 40250 RecvPointChange → CPythonCharacterManager::ShowPointEffect →
CPythonPlayer::SetStatus / 窗口刷新分支修复点数事件链:
EntityStore::mut_set_point统一 classic 与 m2dev 的单点变更入口,先保留{vid, type, value, amount}typed event,再更新主角点表、实体生命/等级和经验增量;POINT_ENERGY == 0同步清除POINT_ENERGY_END_TIME。M2Client新增point_changed,两种协议镜像都在vitals_changed/points_changed前发出,保留 40250 的“先 point effect、后状态刷新”顺序;新增POINT_LEVEL_STEP、POINT_STAT_RESET_COUNT及四维属性常量。NetWorld对POINT_LEVEL/POINT_LEVEL_STEP按 40250 注册的level_up.mse/skillup_1.mse资源在对应 VID 上播放,并在远端等级变更后刷新LevelTag;NetPlay对金币正 amount 发出money_picked/ HUDon_pick_money, 并发出属性、技能窗口刷新请求;SkillUI监听点数事件。net_entity_test.cpp、netplay_test.gd覆盖 typed event 顺序、EXP/GOLD amount、 零能量清理、金币反馈和属性/技能刷新;同时修复 NetWorld 在未显式分离本地节点的 headless/adaptor 场景错误跳过主实体的问题。
本轮仍不宣称完全等价:LevelUp/SkillUp 的真实 C++ effect manager 生命周期、
金币浮字的完整 UI 资源、状态窗口所有旁路调用,以及真实服务器攻击→死亡→经验→掉落
包序和跨图异常清理仍需继续审计。
本合同继续为 PARTIAL。
Audit round 2026-09-20T21:20Z
本轮对上一轮修复后的死亡/经验/掉落链做回归复核,并重新对照
RecvDeadPacket -> CInstanceBase::Die -> NotifyCharacterDead/NotifyDeadMainCharacter、
RecvPointChange -> POINT_EXP.amount、RecvCreateFlyPacket 和地面物品包:
- 主角死亡的
PlayerController输入门、自动攻击清理、复活正 HP 边沿、远端死亡目标清理和经验增量一次性消费均有测试证据;本轮未发现上一轮已修复分支回归。 POINT_EXP.amount仍是经验的权威增量,FLY_EXP只负责视觉;死亡、掉落坐标/贴地、owner 文本/拒绝、拾取距离和删除主路径回归通过。- 仍不等价的分支保持明确:真实服务端攻击→伤害→死亡→
POINT_EXP→CREATE_FLY→地面掉落包序未 fixture 化;重复/乱序死亡、无效 VID、重连 catch-up、重名/空 owner 和拾取竞争未闭合。 - 40250 的
CInstanceBase::Die会清 affect、卸马鞍、取消选中/目标并由底层一次性死亡门驱动动作;当前这些动作分散在 NetWorld/NetPlay/UI,完整死亡动画、特效管理器生命周期和跨图旧队列清理仍没有同一入口证明等价。
结论:本轮是回归审计,没有新增实现修改;已有死亡和经验主链保持可用,但合同继续为 PARTIAL,下一轮优先补协议级死亡包序和重复/乱序/复活清理 fixture。
Implementation fix round 2026-09-21T04:00Z — local main fly target fallback
沿 40250 RecvCreateFlyPacket 的起点/终点实例解析继续核对发现:参考端从
CPythonCharacterManager 取得 CInstanceBase,本地主角虽然不在远端 actor 容器中,
仍由 GetMainInstancePtr() 提供位置。当前端 GameScene 将本地主角单独放在
NetWorld._local_node,但 _entity_pos() 原先只查 EntityStore 与 _by_vid;在阶段切换
的短窗口内若本地主角 EntityStore 行尚未镜像,_on_fly(FLY_EXP, mob_vid, main_vid) 会因
终点为 Vector3.INF 直接丢弃经验球。
修复前先让 test_exp_fly_parity.gd 删除本地 VID 的 EntityStore fixture,专项实际复现
“经验球数量为 0”的失败。现在 _entity_pos() 对本地 VID 优先回退到 _local_node,
_on_fly() 对本地主角作为起点也使用同一实例来源;因此 EntityStore 行暂缺时仍可按
40250 的主角实例语义创建和追踪飞行物。
修复后通过:test_exp_fly_parity.gd、fly_reset_test.gd、netbridge_test.gd、
test_combat_death_pickup_entry_parity.gd、mob_death_drop_test.gd、
gamescene_test.gd。测试中 test_exp_fly_parity.gd 仍有 3 个 ObjectDB leak warning,
不影响退出码和本轮断言结果,需单独做生命周期清理审计。
合同继续为 PARTIAL:真实服务器攻击→死亡→经验→掉落包序、重复/乱序/重连/拾取竞争、
完整死亡资源生命周期和跨图清理仍未由 live packet fixture 证明。
Implementation fix round 2026-09-21T04:02Z — indexed fly actor-instance gate
继续核对 40250 RecvCreateFlyPacket:参考端在 CreateIndexedFly 前必须同时取得
GetInstancePtr(startVID) 和 GetInstancePtr(endVID),任一实例不存在就直接返回,
不会用网络层残留坐标制造一个脱离角色实例的飞行物。
修复前回归新增两项失败:远端终点节点不存在但 EntityStore 有坐标时错误生成飞行物;
远端起点节点不存在但 EntityStore 有坐标时同样错误生成飞行物。当前
NetWorld._on_fly() 对起点和非零终点增加实例存在门;本地主角保留上一轮的
_local_node 适配,因此只在本地主角实例真实存在时允许创建。
修复后 test_exp_fly_parity.gd、fly_reset_test.gd、netbridge_test.gd、
test_combat_death_pickup_entry_parity.gd、mob_death_drop_test.gd、
gamescene_test.gd 和 combat_fx_test.gd 全部通过。合同仍为 PARTIAL,真实服务端
攻击→死亡→经验→掉落包序、重复/乱序/重连/拾取竞争和跨图清理仍未闭合。