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

163 lines
15 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.
# movement.remote-sync-state
## Scope
比较远端角色 `GC_MOVE`/状态命令的接收、服务器时钟校正、TCP 状态队列释放、动作前移动、到达后的动作、速度/走跑模式、位置纠偏、死亡/击倒/表情锁、中断和换图清理。
## Reference call chain
- `UserInterface/PythonNetworkStreamPhaseGameActor.cpp::RecvCharacterMovePacket`
- 解码 `bRot * 5``bFunc``bArg``dwTime`、目标坐标和 `dwDuration`
- `CNetworkActorManager::MoveActor` 查找已有 VID,同时更新 `SNetworkActorData` 的展示目标。
- `UserInterface/InstanceBase.cpp::PushTCPState`
-`ELTimer_GetServerFrameMSec() - dwCmdTime` 更新 70/30 EMA。
-`dwCmdTime + m_nAverageNetworkGap` 入队。
- `UserInterface/InstanceBase.cpp::StateProcess`
- 服务器帧未到时保留队首。
- `__CanProcessNetworkStatePacket` 拒绝死亡、击倒和不可取消技能。
- `__IsEnableTCPProcess` 在表情或 TCP 状态禁用期间暂停释放。
-`FUNC_WAIT``FUNC_MOVE``FUNC_COMBO``FUNC_ATTACK``FUNC_MOB_SKILL``FUNC_EMOTION``FUNC_SKILL` 分支建立 `Src/Dst``m_kMovAfterFunc` 和动作。
- `UserInterface/InstanceBase.cpp::MovementProcess`
-`CActorInstance::AccumulationMovement` 的动作根运动推进。
- 到达后按 `m_kMovAfterFunc` 执行 combo/attack/skill/emotion,远端过冲超过 100 像素时重新取 Src,并把普通移动降为等待。
- `UserInterface/PythonNetworkStreamPhaseGameActor.cpp::RecvSyncPositionPacket`
- 每个纠偏元素调用 `CNetworkActorManager::SyncActor`
- `CActorInstance::TEMP_Push` 做 100 段物理碰撞探测、BlendingPosition 和大位移受击动作。
- `UserInterface/PythonNetworkStreamPhaseHandShake.cpp`
- 握手 `dwTime + lDelta` 与本地毫秒时钟建立服务器帧时基。
## Current call chain
- `extension/src/net/classic/classic_parser.cpp::HDR_GC_MOVE`
- `extension/src/net/entity_store.cpp::mut_move`
- 远端 VID 入 `std::deque<StateCmd>`,用 `average_network_gap` 做 70/30 EMA。
- `drain_state_queue``server_frame_ms`、死亡/击倒门释放。
- `extension/src/net/entity_store.cpp::apply_state_cmd`
- 已覆盖 WAIT/MOVE/COMBO/ATTACK/MOB_SKILL/FUNC_EMOTION/技能位掩码,并保存 `mov_after_func`
- `extension/src/net/entity_store.cpp::tick`
- 先释放命令,再以 `.msa` 提取的 WALK/RUN 根运动速度推进;无资源时按 `dwDuration` 线性插值。
- `extension/src/net/entity_store.cpp::mut_snap_position`
- `GC_SYNC_POSITION` 直接把当前位置和 Src/Dst 设置为纠偏坐标并停止移动。
- `project/net_world.gd::_update_visibility`
- 节点位置直接读取 C++ 实体坐标;按 `func``moving``walk_mode``dead` 更新远端动作。
- `extension/src/net/classic/classic_session.cpp`
- 将 classic 握手推导出的 `server_frame_ms()` 每帧喂给 `EntityStore`
- `extension/tests/net_state_queue_test.cpp`
- 覆盖帧门、阈值、技能位、死亡/击倒挂起、顺序、时钟防呆、根运动和 duration fallback。
## Audit findings (2026-09-20)
已确认当前端的远端移动不再是收到 `GC_MOVE` 就直接改坐标,核心队列、70/30 网络延迟 EMA、服务器帧释放、1/50 像素阈值、到达后动作和根运动推进都已有对应实现;因此这一项不能继续保持“未审计的 MAPPED”。
仍有材料差异,状态定为 `PARTIAL`
- 40250 的 `FUNC_EMOTION` 在目标距离大于 100 像素时先走到目标再执行情绪;当前端已补齐 100 像素阈值、到达后的 `mov_after_func` 和远程表情不启用 skip-collision 的分支。由于 `TPacketGCMove` 本身不携带 `uTargetVID`,双人表情目标解析仍无法在网络层 1:1 复刻。
- 40250 `__CanProcessNetworkStatePacket` 还检查不可取消技能,`__IsEnableTCPProcess` 还检查 `IsActEmotion``m_bEnableTCPState`;当前已新增 presentation-owned `skill_active/skill_can_cancel/acting_emotion/tcp_state_enabled` 门,并由远端一次性技能/表情的 `motion_bound("wait")` 尾事件释放。技能可取消性仍缺少 40250 动作数据的权威来源,未知资源和无模型节点保留回退路径。
- 40250 的远端移动权威推进来自动作 `.msa``AccumulationMovement`;当前有真实 WALK/RUN 根运动读取,但无模型时使用 `dwDuration` fallback,且坐骑直接 fallback,速度/到达时间不完全一致。
- 40250 `FUNC_COMBO`/`FUNC_SKILL` 到达后使用 `uArg` 选择具体动作;当前队列保存了 `mov_after_arg`,且 `net_world` 已把 `FUNC_ATTACK` 映射为 `NAME_NORMAL_ATTACK`、把 `FUNC_COMBO``func_arg` 原样传给 `play_attack_motion`,把 `FUNC_SKILL` 的循环/移动位传给 `play_skill_motion``FUNC_EMOTION` 现在也按 `uArg` 尝试 `set_motion_id`,动作完成后释放 TCP 门;远端 combo/skill/emotion 资源缺失时仍按视图实现回退,怪物 motlist 的逐段资源覆盖和技能 grade 权威来源仍未完全闭合。
- 40250 `TEMP_Push``GC_SYNC_POSITION` 做 100 段物理碰撞探测、BlendingPosition,并在位移超过 150 像素时触发 DAMAGE_FLYING/STAND_UP;当前端已由 `mut_snap_position` 记录源点/序号/150cm 与技能判定,`NetWorld` 执行 100 段 `is_blocked` 探测、1 秒倒数偏移和击倒动作链。无模型/无地形 world 时仍保留可验证的权威坐标回退,因此资源和真实场景碰撞结果仍为 PARTIAL。
- 40250 对命令队列的未来时间不设置 1000ms 防呆阀;当前 `STATE_QUEUE_MAX_WAIT_MS` 超过阈值立即释放,是为未完成的真实服务器时基校准保留的有意偏离。
- 换图清理已清除实体和队列,但“远端动作正在播出时收到死亡、击倒、传送和重新 Add”的完整资源/动作事件顺序仍缺端到端证据。
## Deep audit round 2026-09-20
本轮重新对照 `InstanceBase::StateProcess``TEMP_Push``NetworkActorManager::MoveActor/SyncActor` 的实际分支,并重跑当前端队列、实体和移动回归:
- 40250 在释放队首命令前同时检查死亡、击倒、不可取消技能、表情和 `m_bEnableTCPState`;当前队列虽然有死亡/击倒门,且已补 `FUNC_EMOTION` 的距离/到达分支,但没有不可取消技能、表情活动和 TCP 状态开关的等价状态源,双人表情目标也无法从 `TPacketGCMove` 还原。
- 40250 的 `FUNC_COMBO``FUNC_ATTACK``FUNC_MOB_SKILL``FUNC_SKILL` 在到达后继续使用命令参数选择动作;当前 `mov_after_arg` 能保存参数,但远端视图侧没有完整的 arg→动作资源路由,当前回归只验证 func/队列状态,不验证具体 `.msa` 播放结果。
- `GC_SYNC_POSITION` 在 40250 进入 `TEMP_Push`,当前端已补 100 段碰撞探测、BlendingPosition 偏移和大位移击倒动作门;真实模型动作、地图碰撞资源和无模型 fallback 仍不能证明完全一致。
- 本轮 `net_state_queue_test``net_entity_test``net_world_vis_test``netplay_test``movement_parity_test``net_world_remote_action_test` 均退出码 0;这些测试证明队列门、EMA、根运动 fallback、可见性、上行移动和 TEMP_Push 纠偏事件/击倒门没有回归,但没有覆盖真实模型动作资源、表情锁、不可取消技能权威数据、实服乱序/跨图/死亡重建和地图碰撞资源。
本轮没有把已有 PASS 测试误升为等价完成;核心队列链保持可用,但上述状态门、动作参数和纠偏物理仍使合同保持 `PARTIAL`
## Equivalence matrix
| 项目 | 状态 | 备注 |
|---|---|---|
| Preconditions | PARTIAL | VID、主角过滤、服务器帧门、死亡/击倒、不可取消技能和表情/TCP 门已有实现;技能可取消性权威来源和双人表情目标未闭合 |
| Branch structure | PARTIAL | WAIT/MOVE/COMBO/ATTACK/MOB_SKILL/EMOTION/SKILL 距离分支已对齐;完整禁止门仍缺失 |
| Algorithms/formulas | PARTIAL | 70/30 EMA、1/50 阈值、100 像素过冲、100 点纠偏探测、150cm 击倒阈值和 1 秒 BlendingPosition 已对齐;无模型/无 world fallback 不等价 |
| State transition order | PARTIAL | 队列→动作前移动→到达动作→动作尾释放,以及 sync serial→碰撞→缓动→击倒/起身→释放门顺序已对齐;双人表情目标和真实资源尾时序未闭合 |
| Constants/units | PARTIAL | 角度、cm、阈值和 70/30 已记录;1000ms 防呆阀与坐骑/fallback 是偏离 |
| Timing/event sources | PARTIAL | classic server frame 已接入;真实握手时基、动作事件与状态动作的端到端联机证据不足 |
| Resource/data sources | PARTIAL | WALK/RUN `.msa` 根运动、PC combo `.msa` 模式目录、PC emotion action 和 damage_flying/standup 入口已接入;技能可取消/grade、怪物逐段 combo、emotion 目标、真实击倒资源覆盖仍不完整 |
| Protocol side effects | PARTIAL | GC_MOVE、GC_SYNC_POSITION、CHANGE_SPEED、WALK_MODE 已解析;纠偏动作已桥接,双人情绪目标和服务器时基副作用未等价 |
| Interruption/failure/cleanup | PARTIAL | 死亡/击倒挂队、换图清理已有单测;技能/表情/传送/重建完整矩阵未完成 |
## Regression evidence
- `extension/tests/net_state_queue_test.cpp`:服务器帧释放、WAIT 1.0、攻击/技能 50.0、FUNC_EMOTION 100.0、死亡/击倒挂队、FIFO、时钟防呆、根运动和 duration fallback。
- `extension/tests/net_entity_test.cpp`:远端移动命令的 near/far 行为、到达后动作和未知 VID 忽略。
- `project/netplay_test.gd``project/movement_parity_test.gd`:上行状态节流和移动预测不回归。
- `project/net_world_remote_action_test.gd`:远端 `FUNC_ATTACK` 使用普通攻击索引 13`FUNC_COMBO` 保留 `bArg` 连击段,并传递武器动作模式与攻速比例。
- `extension/tests/net_state_queue_test.cpp`:不可取消技能、活动表情、TCP 状态禁用和恢复后的队列释放边界。
- `extension/tests/net_entity_test.cpp``GC_SYNC_POSITION` 记录源点、单调 serial、150cm 大位移和技能抑制状态。
- `project/net_world_remote_action_test.gd`:TEMP_Push 大位移动作、击倒网络门、旧位置起始偏移和动作尾释放。
## Implementation fix round 2026-09-20
按 40250 `InstanceBase::StateProcess``RunNormalAttack` / `RunComboAttack` 的动作选择
补齐远端攻击状态的参数桥:普通攻击使用 `CRaceMotionData::NAME_NORMAL_ATTACK`13),连击
使用 `TPacketGCMove.bArg` 原值,二者都从远端视图读取当前武器动作模式,并把 `bAttackSpeed / 100`
传给动作播放层。此前两种状态都会降级成通用 `set_anim_state("attack")`,导致远端角色看不到
对应的 combo 段;资源解析失败时仍保留通用状态回退。
回归:`godot --headless --path project --script net_world_remote_action_test.gd` 通过。
死亡/击倒门、FUNC_EMOTION 目标、技能 grade、坐骑根运动和 `TEMP_Push` 纠偏物理仍保持
`PARTIAL`,本轮没有把动作索引测试误当成完整资源/联机等价。
## Implementation fix round 2026-09-20 — state gates and emotion action
按 40250 `__CanProcessNetworkStatePacket``__IsEnableTCPProcess` 增加 EntityStore 的
四个 presentation-owned 状态门:不可取消技能阻止后续状态出队,活动表情阻止 TCP 状态,
`m_bEnableTCPState=false` 时只允许当前 `FUNC_EMOTION`,死亡/击倒门保持原有优先级。NetWorld
在远端技能/表情动作开始时设置门,在 `motion_bound("wait")` 动作尾清除;节点剔除和换图也会
清除门,避免模型被释放后队列永久冻结。
同时把 `FUNC_EMOTION``func_arg` 接入 40250 动作 ID(单人表情使用 `target_race=-1`
双人 `GC_MOTION` 仍走原有目标 VID 路径),并让出生时已经带有 `FUNC_*` 的实体在字段刷新后
立即绑定初始动作,而不是等待下一次状态变化。
回归:`net_state_queue_test``net_world_remote_action_test``net_world_vis_test`
`netplay_test``player_motion_test``test_skill_state_arg_parity` 和完整 `ctest`23/23
均通过。技能可取消性的动作数据来源、真实远端模型动作尾时序、双人表情目标和 `TEMP_Push`
仍保持 `PARTIAL`
## Status
`PARTIAL`。远端 TCP 状态队列主链已经有较充分实现和单元测试;FUNC_EMOTION 的距离/到达顺序已补齐,但 40250 的不可取消技能门、双人表情目标、纠偏物理动作、远端动作参数和真实服务器时基仍未完全等价。
## Implementation fix round 2026-09-21 — TEMP_Push correction path
按 40250 `NetworkActorManager::SyncActor``CInstanceBase::NEW_SyncPixelPosition`
`CActorInstance::TEMP_Push` 补齐 `GC_SYNC_POSITION` 的表现层纠偏:`EntityStore::mut_snap_position`
现在保留同步前坐标、单调 correction serial、150cm 大位移判定及 `IsUsingSkill` 抑制位;
`M2Client::entity_dict` 将这些字段交给 `NetWorld`。节点收到新 serial 后,`NetWorld`
`TEMP_Push` 的 100 个采样点调用地图 `is_blocked`,无碰撞时用 `PhysicsPush` 的 1 秒
倒数偏移保持旧位置起步并回到权威目标;大位移且非技能时启动
`DAMAGE_FLYING -> STAND_UP`,并通过 `set_entity_knockdown` 复刻 `IsKnockDown` 网络状态门。
动作尾、节点剔除和换图会释放该门;没有可用模型/地形时保留权威坐标回退,不伪造资源动作。
回归:`net_entity_test``net_world_remote_action_test``net_world_vis_test`
`netplay_test``player_motion_test``movement_parity_test` 和完整 `ctest`23/23)通过。
真实 `IPhysicsWorld` 地形数据、动作资源覆盖、技能可取消权威来源、双人表情目标和真实服
乱序/跨图/死亡重建仍保持 `PARTIAL`
## Implementation fix round 2026-09-21 — unified remote FUNC_MOVE arrival branch
复核 40250 `CInstanceBase::MovementProcess` 的远端 actor 分支后确认,
`m_kMovAfterFunc == FUNC_MOVE` 穿过 `Dst` 时没有 PC/NPC/monster 的 `ch_type` 分叉:
`FUNC_MOVE` 的到达 switch case 继续沿当前根运动推进,只有后续状态命令或超过
`100cm` 的 overshoot 防呆分支才会改回 `WAIT`。当前端此前只让 PC 继续,NPC/MOB
在第一次越过 `Dst` 时提前停止并清除 `skip_collision`,导致远端怪物的连续 `GC_MOVE`
表现和 40250 不一致。
现已将 `EntityStore::advance_walk_by_motion` 的远端到达逻辑统一为参考分支:
死亡/击倒仍立即停止;`FUNC_MOVE` 保留 `moving``mov_after_func` 和根运动推进;
普通攻击、连击、技能、表情等到达动作继续走原有 `finish_walk`。新增 NPC/MOB
回归用例先证明修复前会失败,修复后 `net_state_queue_test``net_entity_test`
`net_classic_session_test``git diff --check` 均通过。
本合同仍为 `PARTIAL`:真实 WALK/RUN 根运动资源、技能可取消/grade 权威数据、双人
表情目标、TEMP_Push 地图碰撞和真实服务器时序尚未达到同一实现证据。