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

15 KiB
Raw Blame History

movement.remote-sync-state

Scope

比较远端角色 GC_MOVE/状态命令的接收、服务器时钟校正、TCP 状态队列释放、动作前移动、到达后的动作、速度/走跑模式、位置纠偏、死亡/击倒/表情锁、中断和换图清理。

Reference call chain

  • UserInterface/PythonNetworkStreamPhaseGameActor.cpp::RecvCharacterMovePacket
    • 解码 bRot * 5bFuncbArgdwTime、目标坐标和 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_WAITFUNC_MOVEFUNC_COMBOFUNC_ATTACKFUNC_MOB_SKILLFUNC_EMOTIONFUNC_SKILL 分支建立 Src/Dstm_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_queueserver_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++ 实体坐标;按 funcmovingwalk_modedead 更新远端动作。
  • 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 还检查 IsActEmotionm_bEnableTCPState;当前已新增 presentation-owned skill_active/skill_can_cancel/acting_emotion/tcp_state_enabled 门,并由远端一次性技能/表情的 motion_bound("wait") 尾事件释放。技能可取消性仍缺少 40250 动作数据的权威来源,未知资源和无模型节点保留回退路径。
  • 40250 的远端移动权威推进来自动作 .msaAccumulationMovement;当前有真实 WALK/RUN 根运动读取,但无模型时使用 dwDuration fallback,且坐骑直接 fallback,速度/到达时间不完全一致。
  • 40250 FUNC_COMBO/FUNC_SKILL 到达后使用 uArg 选择具体动作;当前队列保存了 mov_after_arg,且 net_world 已把 FUNC_ATTACK 映射为 NAME_NORMAL_ATTACK、把 FUNC_COMBOfunc_arg 原样传给 play_attack_motion,把 FUNC_SKILL 的循环/移动位传给 play_skill_motionFUNC_EMOTION 现在也按 uArg 尝试 set_motion_id,动作完成后释放 TCP 门;远端 combo/skill/emotion 资源缺失时仍按视图实现回退,怪物 motlist 的逐段资源覆盖和技能 grade 权威来源仍未完全闭合。
  • 40250 TEMP_PushGC_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::StateProcessTEMP_PushNetworkActorManager::MoveActor/SyncActor 的实际分支,并重跑当前端队列、实体和移动回归:

  • 40250 在释放队首命令前同时检查死亡、击倒、不可取消技能、表情和 m_bEnableTCPState;当前队列虽然有死亡/击倒门,且已补 FUNC_EMOTION 的距离/到达分支,但没有不可取消技能、表情活动和 TCP 状态开关的等价状态源,双人表情目标也无法从 TPacketGCMove 还原。
  • 40250 的 FUNC_COMBOFUNC_ATTACKFUNC_MOB_SKILLFUNC_SKILL 在到达后继续使用命令参数选择动作;当前 mov_after_arg 能保存参数,但远端视图侧没有完整的 arg→动作资源路由,当前回归只验证 func/队列状态,不验证具体 .msa 播放结果。
  • GC_SYNC_POSITION 在 40250 进入 TEMP_Push,当前端已补 100 段碰撞探测、BlendingPosition 偏移和大位移击倒动作门;真实模型动作、地图碰撞资源和无模型 fallback 仍不能证明完全一致。
  • 本轮 net_state_queue_testnet_entity_testnet_world_vis_testnetplay_testmovement_parity_testnet_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.gdproject/movement_parity_test.gd:上行状态节流和移动预测不回归。
  • project/net_world_remote_action_test.gd:远端 FUNC_ATTACK 使用普通攻击索引 13FUNC_COMBO 保留 bArg 连击段,并传递武器动作模式与攻速比例。
  • extension/tests/net_state_queue_test.cpp:不可取消技能、活动表情、TCP 状态禁用和恢复后的队列释放边界。
  • extension/tests/net_entity_test.cppGC_SYNC_POSITION 记录源点、单调 serial、150cm 大位移和技能抑制状态。
  • project/net_world_remote_action_test.gd:TEMP_Push 大位移动作、击倒网络门、旧位置起始偏移和动作尾释放。

Implementation fix round 2026-09-20

按 40250 InstanceBase::StateProcessRunNormalAttack / RunComboAttack 的动作选择 补齐远端攻击状态的参数桥:普通攻击使用 CRaceMotionData::NAME_NORMAL_ATTACK13),连击 使用 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_EMOTIONfunc_arg 接入 40250 动作 ID(单人表情使用 target_race=-1 双人 GC_MOTION 仍走原有目标 VID 路径),并让出生时已经带有 FUNC_* 的实体在字段刷新后 立即绑定初始动作,而不是等待下一次状态变化。

回归:net_state_queue_testnet_world_remote_action_testnet_world_vis_testnetplay_testplayer_motion_testtest_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::SyncActorCInstanceBase::NEW_SyncPixelPositionCActorInstance::TEMP_Push 补齐 GC_SYNC_POSITION 的表现层纠偏:EntityStore::mut_snap_position 现在保留同步前坐标、单调 correction serial、150cm 大位移判定及 IsUsingSkill 抑制位; M2Client::entity_dict 将这些字段交给 NetWorld。节点收到新 serial 后,NetWorldTEMP_Push 的 100 个采样点调用地图 is_blocked,无碰撞时用 PhysicsPush 的 1 秒 倒数偏移保持旧位置起步并回到权威目标;大位移且非技能时启动 DAMAGE_FLYING -> STAND_UP,并通过 set_entity_knockdown 复刻 IsKnockDown 网络状态门。 动作尾、节点剔除和换图会释放该门;没有可用模型/地形时保留权威坐标回退,不伪造资源动作。

回归:net_entity_testnet_world_remote_action_testnet_world_vis_testnetplay_testplayer_motion_testmovement_parity_test 和完整 ctest23/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 保留 movingmov_after_func 和根运动推进; 普通攻击、连击、技能、表情等到达动作继续走原有 finish_walk。新增 NPC/MOB 回归用例先证明修复前会失败,修复后 net_state_queue_testnet_entity_testnet_classic_session_testgit diff --check 均通过。

本合同仍为 PARTIAL:真实 WALK/RUN 根运动资源、技能可取消/grade 权威数据、双人 表情目标、TEMP_Push 地图碰撞和真实服务器时序尚未达到同一实现证据。