- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
20 KiB
world.entity_spawn_state_sync
Scope
审计 Actor 的 Add、AdditionalInfo、Add2、Update、Move、Delete、主角色切换、VID 重用、可见性和实体节点/网络数据同步。目标不是比较函数名,而是确认 40250 的字段、前置条件、分支、调用顺序、资源副作用和清理行为在当前客户端中保持相同;Godot 的节点/信号只是平台适配,不能掩盖网络实体状态的缺失。
Reference implementation and evidence
../40250/Server Client TMP4/ClientVS22/source/UserInterface/NetworkActorManager.cppAppendActor,UpdateActor,MoveActor,RemoveActor,SetMainActorVID,__AppendCharacterManagerActor,__RemoveCharacterManagerActor,__RemoveDynamicActors。- Add 时保存完整
SNetworkActorData;创建失败时删除数据行;主角色使用立即删除,远端 Delete 使用DeleteInstanceByFade。 UpdateActor的副作用顺序为 armor、weapon、hair、guild、affect、move speed、attack speed、alignment、PK、state flags。- 同 VID 重新创建时,只有 mount 状态发生切换才保留旧实例位置;否则使用新 Actor 的当前坐标。
../40250/Server Client TMP4/ClientVS22/source/UserInterface/PythonNetworkStreamPhaseGameActor.cppRecvCharacterAppendPacket:旧 Add 的两包链;PC/NPC 暂存,其他种族直接完成 Add。RecvCharacterAdditionalInfo:合并暂存 Actor;VID 不匹配时记录错误,不新建 Actor。RecvCharacterAppendPacketNew:Add2 完整填充字段,并在IsInvisibleRace时丢弃。RecvCharacterUpdatePacket:传递 state、affect、装备、速度、guild、alignment、PK、mount。RecvCharacterDeletePacket:删除 Actor 并清理相关附属对象。
../40250/Server Client TMP4/ClientVS22/source/UserInterface/PythonCharacterManager.cppDeleteInstance立即移除;DeleteInstanceByFade进入 dead instance list 后延迟销毁。
../40250/Server Client TMP4/ClientVS22/source/UserInterface/InstanceBase.cppSCreateData承接 race、parts、移动/攻击速度、guild、state、affect、mount 等创建字段。
Current implementation and evidence
extension/src/net/classic/classic_parser.cpp- Add 的 PC/NPC 使用
m_pending_actor等待 AdditionalInfo;非 PC/NPC 直接mut_spawn。 - Add2 现在先执行与 40250 相同的隐身种族过滤,再一次性构造完整
Entity并调用mut_spawn_full;旧 Add 的非 PC/NPC 也保留state_flag、affect_flag。 - Update 现在把 state、affect、装备、速度、guild、alignment、PK、mount 一次传入
mut_char_update,不再拆出独立 affect mutation。 - pending 现在与 40250 一样只有一个静态暂存记录;后一个旧 Add 覆盖前一个,AdditionalInfo 只能完成最后暂存 VID。Delete 后迟到 AdditionalInfo、map reset 后迟到包仍需继续核对。
- Add 的 PC/NPC 使用
extension/src/net/entity_store.cppapply(GC_CHARACTER_ADD/ADD2/UPDATE)现在保留 state、affect;Add2 还完整保存 empire、guild、alignment、PK、mount,并过滤三种隐身 race。apply(GC_CHAR_ADD_INFO)现在必须命中已有 Entity,未知 VID 只记录忽略,不再通过touch创建部分 Entity。mut_spawn_main/ genericGC_MAIN_CHARACTER现在统一产生一次MainSet边沿;首次主角为Spawn -> MainSet,重复主角包仅为一次MainSet,不再依赖重复事件或场景轮询。
project/net_world.gd_on_spawn会替换旧节点;已按 40250 改为仅在 mount 状态切换时保留旧 global position,普通重建使用新包坐标,并始终使用新包朝向。_fading只跟踪旧节点的 blend-out;收到同 VID 新 Spawn 时会立即解除门控并重建 live 节点,旧 tween 独立完成。_on_info只更新已有节点,不会修复 generic AdditionalInfo 误创建的部分节点。_on_main_set/set_local_vid没有完全复刻SetMainActorVID对远端 Actor 字典和动态实例的清理范围。
extension/src/net/m2_client.cpp- classic Add2 现在由
mut_spawn_full产生一条完整 Spawn,再由pump_classic统一向 Godot 发出;不会再拆成 Add2 Spawn + Info + affect 三条事件。
- classic Add2 现在由
Branch matrix
| 场景 | 40250 行为 | 当前端行为 | 结论 |
|---|---|---|---|
| 旧 Add 新 VID | 保存 Add 字段;PC/NPC 等待 AdditionalInfo,其他种族直接 AppendActor | PC/NPC 使用单个 static-equivalent pending;非 PC/NPC 现在完整保存 state/affect | PARTIAL |
| AdditionalInfo 命中 | 合并暂存记录后只创建完整 Actor | classic parser 命中 pending 后完整 Spawn;generic apply 命中已有 Entity 后发一次 Info | PARTIAL |
| AdditionalInfo 未命中 | 记录错误,不创建 Actor | classic parser 与 generic apply 均不创建 Entity | MAPPED |
| Add2 新 VID | 隐身种族直接丢弃;其他分支一次性填满字段后 AppendActor | 两条 parser/apply 路径均过滤隐身 race,并一次性填满字段后 mut_spawn_full |
PARTIAL |
| Update 已有 VID | 按固定顺序更新装备、guild、affect、速度、alignment、PK、state、mount | shared mutator 一次更新完整字段并发一次 Info;平台渲染副作用仍需核对 | PARTIAL |
| Update 未知 VID | 记录错误,不创建 | mutator 丢弃;generic update 丢弃 | MAPPED(字段完整性仍需补测) |
| Delete 远端 | DeleteInstanceByFade,并清理附属引用 |
Godot 远端 fade + queue_free;同 VID 新 Spawn 可立即建 live replacement | PARTIAL:dead-list 与 Godot 双节点短时并存仍是平台适配 |
| Delete 主角色 | 立即删除 | main/local 由 game scene 和 net world 分层清理 | PARTIAL |
| 同 VID 重用 | 非 mount 切换使用新坐标;mount 切换可保留旧位置,朝向使用新包 | _on_spawn 已按 mount 状态决定是否保留位置,朝向统一取新包;淡出期间允许立即替换 live 节点 |
PARTIAL:旧 dead-list/tween 的跨帧可见性仍是平台适配 |
| Main VID 切换 | 清空 Actor 字典、重置位置,并清理动态实例 | 只设置 main/local VID 并移除本地节点;通常依赖 map reset | PARTIAL |
| 可见性 | distance、wall、AFFECT_SHOW_ALWAYS、动态实例规则 | data/node 两层可见性和 fade 已有对应测试 | MAPPED(frame/淡出时序为平台适配) |
Equivalence matrix
| 项目 | 状态 | 证据/缺口 |
|---|---|---|
| Preconditions | MAPPED | unknown AdditionalInfo/update 不创建;Add/Add2 隐身 race 在 parser/apply 两条入口均提前丢弃 |
| Branch structure | PARTIAL | Add/AdditionalInfo/Add2/Update 的隐身、PC/NPC、主角色、未知 VID 和交错旧 Add 已覆盖主要分支;Delete/map reset 后迟到包仍缺 fixture |
| Algorithms/formulas | PARTIAL | 可见距离和 EntityStore tick 有对应实现;同 VID 的 mount 位置保留和淡出期间立即重建条件已对齐,旧 dead-list/tween 的跨帧并存仍是平台适配 |
| State transition order | PARTIAL | Add2 已变为单次完整 Spawn,Update 已合并为一次 shared mutator;渲染桥接的逐字段副作用顺序仍需验证 |
| Data completeness | MAPPED | Add/Add2/Update/AdditionalInfo 的协议字段已落入 EntityStore,包含 state/affect/empire/guild/alignment/PK/mount |
| Constants/units | PARTIAL | VID、位置、yaw、移动速度和 mount 标志要在 Add/Update/Move 组合包中逐字段验证 |
| Timing/event sources | PARTIAL | 40250 manager update/dead list 与 Godot frame、signal、queue_free 的交错未完成证明 |
| Resource/data sources | PARTIAL | 40250 SCreateData/CInstanceBase 与当前 model factory 的失败、替换、升级 ownership 未完全覆盖 |
| Protocol side effects | PARTIAL | Add/Info/Update/Delete 入口已映射;Add2/unknown AdditionalInfo 已对齐,渲染层删除/更新副作用仍需证明 |
| Interruption/failure/cleanup | PARTIAL | 模型创建失败、升级中 Delete、pending stale、Delete/map reset 后迟到包和主 VID 清理仍需自动测试 |
Required tests
- 旧 Add -> AdditionalInfo 的命中、未知 VID、交错两个 Add、Delete 后迟到 AdditionalInfo、map reset 后迟到 AdditionalInfo。
- Add/Add2 的完整字段快照:race/type/position/yaw/speeds/state/affect/empire/guild/alignment/PK/parts/mount/name。
- 隐身种族在旧 Add 和 Add2 中均不产生 Entity/node。
- Update 前后 state、affect、装备、guild、速度、alignment、PK、mount 的字段和事件顺序。
- Add -> Info -> Update -> Delete -> 同 VID Add,分别覆盖普通远端、mount 切换、非 mount 切换、淡出未完成和模型升级中;淡出未完成时新 Add 必须立即建 live replacement。
- 主 VID 切换时旧 Actor、HP、尾标、target、hover、模型和动态实例的清理顺序。
- 模型工厂返回失败、map reset 中断、重复 Delete、重复 Add 和跨帧 signal 的一致性。
Deep audit round 2026-09-20
本轮补做了参考实现的创建/重建/可见性时序核对,并运行实体层与 Godot 节点层回归:
- 40250
CNetworkActorManager::Update()在CPythonNetworkStream::OnProcess()中每次网络处理调用__UpdateMainActor(),再遍历 Actor 字典;当前NetWorld._process()已按每个 Godot frame 调用_update_visibility(),不再使用 100ms 节流。net_world_vis_test.gd已覆盖远端进入范围、单次建节点、离开范围淡出、数据层保留和重新进入,当前通过;旧版本的 4 项可见性失败已不再复现。 - 参考
__AppendCharacterManagerActor()对已有 VID 重建时读取旧坐标,但只有从骑乘到非骑乘或反向切换才覆盖新创建数据的坐标;当前_on_spawn()已改为只在 mounted 布尔状态切换时保留旧位置,普通同 VID Add/换装使用新坐标,朝向始终使用新包。 - 参考显式
RemoveActor()对普通远端 actor 走DeleteInstanceByFade,主 actor 走立即DeleteInstance();_on_despawn()现已按_main_vid区分这两个分支。_on_spawn()仍在同 VID 新 Spawn 时解除_fading并立即创建 live replacement,旧 tween 只负责旧节点。重复 Delete 和跨帧双节点生命周期仍需继续核对。 - 本轮已修复 Add/Add2/Update 的字段和分支差异:classic parser 的旧 Add 非 PC/NPC、Add2、Update 与 generic
EntityStore::apply均通过完整字段结构或 shared mutator 保留 state/affect 等字段;Add2 过滤 20025/20038/20039;未知 generic AdditionalInfo 不再touch()创建半实体。随后又将 classic pending 从 keyed map 收敛为 40250 的单个 static-equivalent 槽,并覆盖交错 Add;Delete/map reset 后迟到 AdditionalInfo 和渲染层副作用顺序仍需验证。 SetMainActorVID参考实现会清空 NetworkActor 字典、重置主坐标;当前NetWorld::_on_main_set()只改_main_vid并重刷名字色,旧远端节点、动态实例和 UI 引用的清理依赖外层 map reset,主 VID 切换的独立语义未证明。
Evidence run
./build/extension/net_entity_test:PASS,证明 EntityStore 的部分 Add/Info/Update/Delete、字段和状态队列夹具仍可运行。godot --headless --path project --script net_world_vis_test.gd:PASS,覆盖主角移动后可见实体创建、远端节点一次性构建、离开范围剔除、entity_removed一次性事件、强制可见实体和重新进入。project/gamescene_test.gd:PASS,但该测试验证的是场景装配/实体镜像/初始定位,不覆盖上述可见性节流和同 VID 淡出竞态。- 因此本合同继续
PARTIAL,可见性和字段回归已通过,但 Delete/map reset 后迟到包、主角色清理和渲染层副作用顺序仍未完全证明。
Active UserInterface actor-sync review
NetworkActorManager.h保存每个 actor 的服务器时间、目标位置/持续时间、当前/目标坐标、owner/victim、affect flags 和UpdateActor/MoveActor/Update;PythonCharacterManager负责将其映射到可渲染实例和删除列表。- 当前
EntityStore/NetWorld以 packet 快照和 Godot interpolation 驱动 remote view,基础移动同步可用,但没有参考端 actor manager 的 server-time movement、owner/victim 和统一删除/fade/update transform 入口;这也是远端动作事件、VID 复用和跨图旧包清理的剩余风险。 - 本轮完成 NetworkActorManager 与角色 manager 接口静态核对,实体同步合同继续为
PARTIAL。
Implementation fix round 2026-09-22T04:30Z
沿 40250 NetworkActorManager::__AppendCharacterManagerActor() 和
CPythonNetworkStream::RecvMainCharacter() 复核同 VID 重建及主实例注册边沿,补齐了两处已证实差异:
NetWorld._on_spawn()现在只在旧/新实例的 mounted 布尔状态发生变化时保留旧 global position;普通重生、换装或 VID 重用使用新 Add 数据的坐标。无论是否上下马,旋转都使用新包的angle_deg。EntityStore::mut_spawn_main()和EntityStore::apply(GC_MAIN_CHARACTER)现在在首次主角建立时按Spawn -> MainSet发出一次注册边沿,重复主角包只发一次MainSet,消除了旧实现的缺失/重复通知。
新增回归:
project/net_world_respawn_test.gd:普通同 VID 重建坐标/朝向、上下马重建保留旧位置但使用新朝向。extension/tests/net_entity_test.cpp:首次主角注册和重复主角包的MainSet次数。
修复前上述测试分别能够复现普通重建位置/朝向错误、上下马朝向错误以及 MainSet 缺失/重复;修复后
build/extension/net_entity_test 和 net_world_respawn_test.gd 通过。Add2 隐身种族、AdditionalInfo 未命中、旧 Add 字段完整性已在后续修复轮补齐;淡出期间同 VID 重用和主 VID 切换清理当时仍保持 PARTIAL。
Implementation fix round 2026-09-22T05:10Z
按 40250 PythonNetworkStreamPhaseGameActor::RecvCharacterAppendPacket*、
RecvCharacterAdditionalInfo、RecvCharacterUpdatePacket 和
NetworkActorManager::AppendActor/UpdateActor 的完整字段链复核并修复:
- classic 旧 Add 的即时实体现在以完整
Entity写入 state/affect;PC/NPC pending 合并继续一次完整 Spawn。 - classic Add2 和 generic Add2 都执行
IsInvisibleRace等价过滤,并一次性保存 name、parts、state、affect、empire、guild、alignment、PK、mount 等全部字段。 - classic/generic Update 统一经
mut_char_update更新 state、affect、装备、速度、guild、alignment、PK、mount,并在发生变化时只发一次 Info;mount 变化仍发 mount cue。 - generic AdditionalInfo 仅接受已存在的 actor;未知 VID 被忽略,不创建半实体,并补齐 empire 和字段变更检测。
新增/扩展回归:
extension/tests/net_entity_test.cpp:Add/Add2/Update 全字段、隐身 Add2、未知 AdditionalInfo、单次 Spawn/Info。extension/tests/net_classic_session_test.cpp:40250 Add2 全字段、三种隐身 race、未知 AdditionalInfo 和清理。
修复前新增回归可复现 state/affect 丢失、Add2 元数据丢失、隐身实体错误出现、unknown AdditionalInfo 半实体和事件计数差异;修复后两个 native 测试均通过。淡出期间同 VID 重用、pending 交错清理、主 VID 切换清理和 Godot 可见性时序仍保持 PARTIAL。
Implementation fix round 2026-09-22T05:35Z
按 40250 RemoveActor -> DeleteInstanceByFade 与后续 AppendActor -> __AppendCharacterManagerActor 的实例生命周期复核并修复:
_on_despawn()的旧节点继续渐隐;如果渐隐未结束就收到同 VID 的新 Spawn,_on_spawn()解除旧_fading门控并立即创建新的 live 节点,符合参考端 dead-list 不占用GetInstancePtr()的行为。- 旧 fade tween 只释放旧节点,不会删除新节点;新节点使用新的权威位置和旋转。
扩展 project/net_world_respawn_test.gd,修复前可复现淡出期间同 VID Spawn 被直接丢弃;修复后覆盖立即重建、位置以及旧 tween 完成后的新节点存活。
Implementation fix round 2026-09-22T05:55Z
按 40250 PythonNetworkStreamPhaseGameActor::RecvCharacterAppendPacket 的
static SNetworkActorData s_kNetActorData 和
RecvCharacterAdditionalInfo VID 匹配分支复核并修复:
ClassicParser::m_pending_actor从 keyed map 改为单个std::optional<Entity>,后一个 PC/NPC 旧 Add 覆盖前一个,避免当前端比 40250 多出可交错完成的 pending actor。- AdditionalInfo 只有在 VID 命中最后暂存记录时才调用一次
mut_spawn_full;被覆盖 VID 的迟到 AdditionalInfo 只记录未处理 header,不创建实体。
新增 net_classic_session_test.cpp 的交错旧 Add 回归,修复前可复现两个 pending 和第一个 VID 错误生成;修复后 net_classic_session_test 与 net_entity_test 通过。Delete 后迟到 AdditionalInfo、map reset 后迟到包、主 VID 清理和渲染副作用顺序仍保持 PARTIAL。
Evidence reconciliation 2026-09-22T06:15Z
重新以当前工作树运行可见性回归:net_world_vis_test.gd 通过。当前 _process() 已逐帧执行 _update_visibility(),与 40250 CNetworkActorManager::Update() 的检查频率一致;因此旧审计中关于 100ms 节流和 4 项可见性失败的描述已更新为历史发现,不再作为当前缺陷。Add2 的当前 pump 也已改为单次完整 Spawn,旧的“Spawn + Info + affect 拆分”描述同步更正。
Implementation fix round 2026-09-21T07:08Z
按 CNetworkActorManager::__RemoveCharacterManagerActor 的主/远端分支修正
NetWorld._on_despawn():_main_vid 现在走立即删除,普通远端 actor 继续走
DeleteInstanceByFade 等价的渐隐路径。新增 net_world_respawn_test.gd 断言主角
不会进入 _fading,同时保留普通远端 actor 的同 VID 重建和旧 tween 隔离验证。
net_world_respawn_test、net_world_reset_test、net_world_vis_test、
fly_target_lifecycle_test、netbridge_test 均通过。主 VID 切换、Delete/map reset
后的迟到包和完整渲染副作用顺序仍保持 PARTIAL。
Implementation fix round 2026-09-21T07:15Z
继续核对 RecvCharacterAdditionalInfo 的单槽暂存生命周期时发现:旧 Add
等待 AdditionalInfo 期间如果先收到 Delete,或随后进入 Loading/warp,参考端的
静态 SNetworkActorData 已失效;当前端原先仍保留 m_pending_actor,迟到的
AdditionalInfo 可以错误生成旧角色。现已在匹配 VID 的 Delete 中清槽,并新增
ClassicSession::reset_for_map_change(),将 parser 暂存和 EntityStore 一起清理。
新增回归覆盖 Delete 后 AdditionalInfo、map reset 后 AdditionalInfo;
net_classic_session_test、net_entity_test 和 mtgodot 构建通过。主 VID
切换、跨连接迟到 Update/Move 和渲染副作用顺序仍保持 PARTIAL。
Status
PARTIAL。本轮已完成 Add/Add2/Update/AdditionalInfo 的主要协议字段、前置条件、事件粒度、单槽 pending、逐帧可见性和淡出期间同 VID 立即重建;仍必须补齐 Delete/map reset 后迟到包、主角色清理和 Godot 渲染副作用顺序的独立证明,不能宣称与 40250 完全等价。
Implementation fix round 2026-09-21 — SetMainActorVID lifecycle boundary
本轮继续核对 40250 CNetworkActorManager::SetMainActorVID。参考实现收到新的主角色
VID 时会重置主坐标并清空 m_kNetActorDict,旧角色的 live instance、目标和临时 actor
状态不会带入新的主角色生命周期。当前端此前的 NetWorld._on_main_set() 只更新
_main_vid 并重算名字颜色;如果没有先经过完整 world_reset,旧远端节点、target/
hover、HP/击退同步、伤害队列和聊天尾标仍可能残留。
现在主 VID 发生实际变化时,_on_main_set() 会先解除目标特效,立即删除所有旧 live
actor,清空模型升级队列、sync-push/push、伤害队列、聊天尾标和渐隐索引,再注册新的
主 VID;相同 VID 的重复通知不触发清理。新增 net_world_main_switch_test.gd 覆盖旧
远端节点、target/hover 和临时表现状态在切换边沿全部清除,并与
net_world_reset_test.gd、net_world_respawn_test.gd、net_world_vis_test.gd 一起通过。
本轮只闭合了 Godot presentation 层对应 SetMainActorVID 的清理边沿;EntityStore 中
已有快照在切换后重新可见的生成时序、旧渐隐节点的最终销毁、跨连接迟到 Move/Update
以及完整渲染副作用顺序仍保持 PARTIAL。