- item_tooltip_view.gd: 新增 avoid_rect 属性,tooltip 与装备窗口重叠时自动推到左侧 - inventory_ui.gd: 悬停装备时传入窗口矩形作为避让区域 - 包含其他累积的功能开发和测试文件
163 lines
15 KiB
Markdown
163 lines
15 KiB
Markdown
# 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 地图碰撞和真实服务器时序尚未达到同一实现证据。
|