audit(movement.keyboard.motion): close Paralysis/Faint/Sleep gate as false positive

Investigated the prior round's flagged gap ("CanAct()'s Paralysis/Faint/Sleep
movement doors not wired into net_play.gd") via exhaustive reachability
tracing of the 40250 reference source. All three resolve to non-gaps:

- Paralysis: its only setter (__Shaman_SetParalysis, InstanceBaseEffect.cpp:821)
  has zero callers anywhere in the reachable source -- dead code, never true
  in live gameplay.
- Sleep: the AFFECT_SLEEP case in SetAffect() is commented out; the only live
  setter is the AFFECT_STUN case (SetSleep(), InstanceBaseEffect.cpp:932-933),
  i.e. IsSleep() is just an alias for AFFECT_STUN -- already covered by the
  existing `stunned` field and _can_process_network_state()'s dead/stunned/
  knock_down gate.
- Faint: only setter is chrFaintTest(), a debug-only Python binding operating
  on GetSelectedInstancePtr(); unverifiable from the available 40250 checkout
  (no root/ UI scripts) -- evidence-blocked, left unresolved per the skill's
  reachability rule.

Byproduct finding while tracing IsStun(): main-character GC_STUN routes to
Die() in the reference (RecvStunPacket), not Stun(); verified this causes no
movement.keyboard.motion discrepancy since CanAct() blocks movement equally
on IsDead()/IsStun() and the current client already blocks movement equally
on dead/stunned. The Die()-vs-Stun() distinction only affects death-system
semantics, already tracked under combat.affect-status.md (Remaining #2, rows
63/67) -- not duplicated here.

No implementation code changed. Removed the resolved item from manifest.json
and movement.keyboard-motion.md's Remaining list; documented full evidence
in the contract doc and remediation roadmap. Contract stays PARTIAL
(AccumulationMovement root-motion, OnMove/OnMoving event-source parity,
sync/hit/death/warp gate stacking, and real-model bone-flip test remain open
for future rounds).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-22 16:20:49 +09:00
co-authored by Claude Sonnet 5
parent 7df97e01f9
commit a4ac48db24
5 changed files with 95 additions and 14 deletions
+62 -10
View File
@@ -86,7 +86,7 @@
| 项目 | 状态 | 备注 |
|---|---|---|
| Preconditions | PARTIAL | active/失焦/按键残留、锁门、私店、情绪和锁门 held WASD 已覆盖;同步、受击、死亡和传送门未全覆盖 |
| Branch structure | PARTIAL | W/S、A/D、八方向、释放顺序和 `m_isCmrRot` 相机跟随已覆盖;Paralysis/Faint/Sleep 未接入移动门 |
| Branch structure | PARTIAL | W/S、A/D、八方向、释放顺序和 `m_isCmrRot` 相机跟随已覆盖;Paralysis/Faint/Sleep 已调查——Paralysis 死代码、Sleep 已被 `stunned` 门覆盖、Faint 证据不足,均非差异 |
| Algorithms/formulas | PARTIAL | 方向优先级、归一化和相机跟随的 fold-to-±90/rate 公式一致(`movement.keyboard.motion/camera-auto-follow-rotation`);固定 300 像素 Dst 与根运动仍被连续速度替代 |
| State transition order | PARTIAL | 输入状态、锁门/移动技能恢复、PlayerController、NetPlay 入口已定位;资源事件与网络事件的完整顺序未证实 |
| Constants/units | PARTIAL | 角度/旋转阈值和 0/300/100ms 节流已记录;米/厘米、移动速度和目标距离不同 |
@@ -101,15 +101,11 @@
- 补充死亡/传送/受击以及私店、情绪、移动技能和普通锁定的多门叠加顺序;各单门 held WASD 恢复已覆盖基础路径。
- 继续对照 `.msa` 的 motion event、`AccumulationMovement` 根运动和当前 `get_move_motion_speeds` fallback,决定是否需要根运动适配层。
- 真实角色模型在 W/S 快切时根节点/骨骼姿态不翻转的模型级测试仍缺。
- **新增(2026-09-22 发现,未修复)**`CPythonPlayer::__CanMove()` 还委托
`CActorInstance::CanAct()`,后者在 `IsParalysis()/IsFaint()/IsSleep()` 时也拒绝移动
`ActorInstanceBattle.cpp` `CanAct`/`CanMove`)。当前端 `net_play.gd::_can_process_network_state()`
只挡 `dead`/`stunned`/`knock_down`,没有 Paralysis/Faint/Sleep 三个状态的移动门。
三者的触发源分别是萨满技能 `__Shaman_SetParalysis``InstanceBaseEffect.cpp:821`)、
Python 侧 `SetFaint``PythonCharacterModule.cpp:1094/1101`)和效果可见性绑定的
`SetSleep``InstanceBaseEffect.cpp:933`),当前端尚未定位这些触发源对应的服务端
affect/状态位,需要先把三者映射到现有 affect 管线再决定移动门怎么接,属于比本轮
camera 分支更大的独立子项,留给后续轮次。
- ~~`CanAct()` 的 Paralysis/Faint/Sleep 移动门~~ **已调查关闭(2026-09-22
`movement.keyboard.motion/canact-paralysis-faint-sleep-gate`,见下方调查轮次)**
Paralysis 是可达代码中的死代码(唯一 setter 零调用点);Sleep 在实际运行中是
AFFECT_STUN 的别名,已被当前端 `stunned` 移动门覆盖;Faint 的可达性在现有 40250
检出材料下无法证明或证伪,evidence-blocked。三者均非需要修复的移动等价差异。
### Audit round 2026-09-21
@@ -222,3 +218,59 @@
改为已闭合(见上表),`Remaining` 移除相机副作用项、新增 Paralysis/Faint/Sleep 移动门
子项(见上)。合同整体仍为 `PARTIAL`——根运动/资源事件/同步受击门/Paralysis-Faint-Sleep
仍未闭合。
### 调查轮次 2026-09-22T — `movement.keyboard.motion/canact-paralysis-faint-sleep-gate`
本轮对上条 Remaining 中「`CanAct()` 的 Paralysis/Faint/Sleep 三个移动门未接入」逐一做
可达性追踪(`grep -arn`,因大量 `UserInterface/*.cpp` 注释含 CP949 字节,需要 `-a` 才能
正确匹配),结论:三者均不构成需要修复的移动等价差异,逐项证据如下。
- **`IsParalysis()`/`m_isParalysis`**:全部可达源码中唯一的写入点是
`CInstanceBase::__Shaman_SetParalysis``InstanceBaseEffect.cpp:821-823`,直接调用
`m_GraphicThingInstance.SetParalysis(...)``m_GraphicThingInstance` 声明为
`CActorInstance``InstanceBase.h:999`,确认改的是 `CanAct()` 读的同一个标志位)。
`__Shaman_SetParalysis`/`SetParalysis`/`Paralysis` 在整个 `40250/ClientVS22/source`
树(`UserInterface`+`GameLib`+其余全部模块)做穷举 grep,**零调用点**——没有任何脚本、
网络包处理器或技能逻辑调用它。结论:Paralysis 分支在可达代码中是死代码,40250
Windows 客户端实际运行中 `IsParalysis()` 恒为 false。当前端不接入此门是正确等价
(无对应行为可对照),不是差异。
- **`IsSleep()`/`m_isSleep`**`InstanceBaseEffect.cpp``SetAffect()` 分支表中
`AFFECT_SLEEP` 分支已被注释掉(死代码);唯一存活的写入路径是
`AFFECT_STUN: m_GraphicThingInstance.SetSleep(isVisible);`(约行 932-933)。即
`IsSleep()` 在实际运行中就是 AFFECT_STUN 的别名,不是独立信号。当前端
`entity_store.h:119``stunned` 字段本就注释为对应 `AFFECT_STUN`
`InstanceBaseEffect.cpp:932`),`net_play.gd::_can_process_network_state()` 已经在
`dead`/`stunned`/`knock_down` 上阻止移动——即 Sleep 对应的移动门已经被现有 `stunned`
门覆盖,不需要新增单独的 Sleep 门。
- **`IsFaint()`/`m_isFaint`**:唯一写入点是 `chrFaintTest()`
`PythonCharacterModule.cpp:1087-1105`),Python 绑定名 `"FaintTest"`
(同文件 1305 行),与另一个调试方法 `chrtestRestoreRenderMode`/`"RestoreRenderMode"`
(1072/1319 行)并列在同一张绑定表里,且操作对象是 `GetSelectedInstancePtr()`
(更像选人/预览界面而非在线主控角色)。当前可用的 40250 检出只有 8 个 `.py`
文件、没有 root/ 下的任何 UI/任务脚本,因此无法从现有材料证明或证伪是否有在线
脚本会调用 `char.FaintTest()`。结论:证据不足,按 SKILL.md 的「不要在没有可达性证据
时把未验证代码当作产品行为来追」原则,本项目前不作为移动门差异处理,标记为
evidence-blocked。
**过程中新发现(已移交,非本合同范围)**:追踪 `IsStun()` 时发现
`CActorInstance::Stun()` 唯一调用者 `CInstanceBase::Stun()`
`InstanceBaseBattle.cpp:683-688`)本身只被 `RecvStunPacket()`
`PythonNetworkStreamPhaseGame.cpp:1556-1578`)调用,且该函数显式区分:
`if (GetMainInstancePtr()==pkInstSel) pkInstSel->Die(); else pkInstSel->Stun();`——
主角收到 `GC_STUN` 走的是 `Die()``InstanceBaseBattle.cpp:691``ActorInstance.cpp:365`
`m_isRealDead=TRUE`+播放 NAME_DEAD 动作),只有非主角远端 actor 才真正调用 `Stun()`/
置位 `IsStun()`。当前端 `entity_store.cpp``GC_STUN` 处理器对任意 vid(含主角)统一
`stunned=true`,没有这个主角分流。**但这不是 movement.keyboard.motion 的差异**
`CanAct()``IsDead()`/`IsStun()` 的移动阻断效果相同,而 `net_play.gd`
`_can_process_network_state()` 已经对 `dead`/`stunned` 同等阻止移动——即无论主角
`GC_STUN` 走死亡路径还是眩晕路径,当前端的**移动阻断结果**已经正确等价。
`Die()`/`Stun()` 分流只影响死亡系统本身(`m_isRealDead`、EXP 掉落、尸体/复活流程等),
这是 `combat.death.exp_drop`(或 `combat.affect_status`,见其合同 Remaining #2
分支矩阵第 63/67 行已经记录 `native GC_STUN` 目前只置 `stunned=true` 缺解除路径)的
范围,不在此重复登记。
**结论**:本轮不改动实现代码——三个候选门中两个(Paralysis/Sleep)是死代码/已被现有
`stunned` 门覆盖,第三个(Faint)证据不足;衍生发现的 GC_STUN 主角分流差异经核实对
移动阻断结果无影响,已移交 `combat.affect_status` 既有条目,不重复登记。`Remaining`
中对应条目标记为已调查关闭。合同状态维持 `PARTIAL`(根运动/资源事件/同步受击门等
仍未闭合),Branch structure 行的 Paralysis/Faint/Sleep 备注移除。
+1
View File
@@ -481,3 +481,4 @@
{"time":"2026-09-21T17:01:24Z","event":"implementation_fix_round","result":"PARTIAL","ids":["network.login.phase_flow/auth-game-phase-matrix/phase-dead-noop-time-sync"],"files":["extension/src/net/classic/classic_session.cpp","extension/tests/net_classic_session_test.cpp","audit/contracts/network.login.phase-flow.md","audit/remediation-roadmap.md","audit/manifest.json","audit/history.jsonl","audit/reports/coverage.md"],"tests":["40250 reference source inspection (PASS: PythonNetworkStream.cpp RecvPhasePacket(PHASE_DEAD) has an explicit empty branch and returns true without a phase setter)","pre-fix build-debug/extension/net_classic_session_test (FAIL: PHASE_DEAD enabled time-sync and changed the next handshake response; 1 check)","post-fix build-debug/extension/net_classic_session_test (PASS)","cmake --build build-debug -j4 (PASS)","ctest --test-dir build-debug --output-on-failure -j2 (PASS; 23/23)","ASAN_OPTIONS=detect_leaks=0:halt_on_error=1 build-asan/extension/net_classic_session_test (PASS)","MT_PROTOCOL=classic godot --headless --path project --script app_flow_lifecycle_test.gd (PASS)","MT_PROTOCOL=classic godot --headless --path project --script p10_test.gd (PASS)","MT_PROTOCOL=classic godot --headless --path project --script netplay_test.gd (PASS)","MT_PROTOCOL=classic godot --headless --path project --script disconnect_world_reset_test.gd (PASS)","audit_phase_dispatch.py (PASS; no payload differences)","audit_packet_registry.py (PARTIAL; only approved HDR_GC_SYMBOL_DATA adaptation)","audit_sequence_table.py (PASS; 32768 entries identical)","audit_source_coverage.py (PASS; 0 unowned behavior candidates)","audit_ledger.py refresh --write/report --write/validate (PASS; 45 contracts)","manifest/history JSON validation (PASS)","git diff --check (PASS)"],"reason":"按 40250 RecvPhasePacket(PHASE_DEAD) 的空操作语义,移除当前端错误打开 time-sync mode 的副作用,保持重复握手使用普通 HANDSHAKE 回包。未修改飞行物、技能、战斗、角色名编码或 UI;真实 Windows/服务器主动 PHASE_CLOSE、空角色 live、完整跨阶段 retry、GC_WARP 失败回退和可选安全 runtime仍未闭合,合同保持 PARTIAL。"}
{"time":"2026-09-21T17:08:09Z","event":"implementation_fix_round","result":"PARTIAL","ids":["network.login.phase_flow/auth-game-phase-matrix/phase-dead-noop-time-sync"],"files":["extension/src/net/classic/classic_session.cpp","extension/tests/net_classic_session_test.cpp","audit/contracts/network.login.phase-flow.md","audit/remediation-roadmap.md","audit/manifest.json","audit/history.jsonl","audit/reports/coverage.md"],"tests":["40250 reference source inspection (PASS: PythonNetworkStream.cpp RecvPhasePacket(PHASE_DEAD) is an explicit empty branch; only real phase setters clear the close owner)","pre-fix build-debug/extension/net_classic_session_test (FAIL: PHASE_DEAD cleared the explicit PHASE_CLOSE owner marker; 1 check)","post-fix build-debug/extension/net_classic_session_test (PASS: plain HANDSHAKE response and PHASE_CLOSE marker are both preserved)","cmake --build build-debug -j4 (PASS)","ctest --test-dir build-debug --output-on-failure -j2 (PASS; 23/23)","ASAN_OPTIONS=detect_leaks=0:halt_on_error=1 build-asan/extension/net_classic_session_test (PASS)","MT_PROTOCOL=classic godot --headless --path project --script app_flow_lifecycle_test.gd (PASS)","MT_PROTOCOL=classic godot --headless --path project --script p10_test.gd (PASS)","MT_PROTOCOL=classic godot --headless --path project --script netplay_test.gd (PASS)","MT_PROTOCOL=classic godot --headless --path project --script disconnect_world_reset_test.gd (PASS)","audit_phase_dispatch.py (PASS; no payload differences)","audit_packet_registry.py (PARTIAL; only approved HDR_GC_SYMBOL_DATA adaptation)","audit_sequence_table.py (PASS; 32768 entries identical)","audit_source_coverage.py (PASS; 0 unowned behavior candidates)","audit_ledger.py refresh --write/report --write/validate (PASS; 45 contracts)","manifest/history JSON validation (PASS)","git diff --check (PASS)"],"reason":"在同一分支的进一步等价审计中补齐 PHASE_DEAD 的第二个无副作用约束:通用 phase != PHASE_CLOSE 条件会清除显式关闭 owner,修复为仅真实 phase setter 清除 m_phase_closed。此为同一稳定分支的实质性实现补充,不是重复修复。未修改飞行物、技能、战斗、角色名编码或 UI;真实 Windows/服务器主动 PHASE_CLOSE、空角色 live、完整跨阶段 retry、GC_WARP 失败回退和可选安全 runtime仍未闭合,合同保持 PARTIAL。"}
{"time": "2026-09-22T00:00:00Z", "event": "implementation_fix_round", "result": "PARTIAL", "ids": ["movement.keyboard.motion/camera-auto-follow-rotation"], "files": ["project/player_controller.gd", "project/keyboard_motion_timeline_test.gd", "audit/contracts/movement.keyboard-motion.md", "audit/remediation-roadmap.md", "audit/manifest.json", "audit/history.jsonl"], "tests": ["40250 reference source inspection (PASS: PythonPlayerInput.cpp:445-491 CPythonPlayer::NEW_MoveToDirection m_isCmrRot branch folds fDirRot to fSigDirRot/fRotRat and applies CCamera::Roll(-m_fCmrRotSpd*fElapsedTime*fRotRat/90) every frame a direction key is held and the movement gates pass)", "pre-fix keyboard_motion_timeline_test.gd (FAIL by construction: camera.yaw never written by _keyboard_wish/_process, new directional assertions would fail)", "post-fix godot --headless --path project --script keyboard_motion_timeline_test.gd (PASS)", "godot --headless --path project --script test_wasd_steering_parity.gd (PASS)", "godot --headless --path project --script test_no_auto_move_regression.gd (PASS)", "godot --headless --path project --script game_camera_test.gd (PASS)", "godot --headless --path project --script movement_parity_test.gd (PASS)", "godot --headless --path project --script player_move_test.gd (PASS)", "godot --headless --path project --script mouse_controller_test.gd (PASS)", "godot --headless --path project --script test_alignment_parity.gd (PASS)", "git diff --check (PASS)", "audit_ledger.py refresh --write/report --write/validate (PASS)", "cmake --build build-debug -j4 / ctest (BLOCKED: pre-existing stale CMakeCache.txt from a different checkout path, unrelated to this GDScript-only change; not run)", "godot --headless --path project --script player_motion_test.gd (BLOCKED: pre-existing parse error, GDScriptNativeClass.clear_texture_cache() not found in this Godot 4.7.1 build; file untouched this round, last changed in a prior commit)"], "reason": "按 40250 CPythonPlayer::NEW_MoveToDirection 的 m_isCmrRot 分支,在 player_controller.gd 新增 _camera_auto_rotate():按住方向键且通过 frozen/locked/private_shop_open/processing_emotion 门后,每帧按方向相对相机的方位角折算 fRotRat(正前/正后 0,正左右 ±90 最大速率,对角线减半),以 m_fCmrRotSpd=20.0 的速率持续把 camera.yaw 转向新朝向。D3D CCamera::Roll 与 Godot yaw 的转向手性相反,增量对 fRotRat 取正号(参考实现取负号),作为显式记录的平台适配,用『按住方向时相机朝该方向收敛、正前后不转、从不越界』的不变式核验,而非逐位对照 D3D 数值。未修改飞行物、技能、战斗、UI 或根运动/资源事件路径;Paralysis/Faint/Sleep 未接入移动门(新发现,记录于 Remaining,未修复)、根运动/AccumulationMovement、同步/受击门恢复顺序、真实模型姿态测试仍未闭合,合同保持 PARTIAL。"}
{"time": "2026-09-22T01:30:00Z", "event": "audit_round", "result": "PARTIAL", "ids": ["movement.keyboard.motion/canact-paralysis-faint-sleep-gate"], "files": ["audit/contracts/movement.keyboard-motion.md", "audit/manifest.json", "audit/history.jsonl"], "tests": ["40250 reference source reachability audit (exhaustive grep -arn across UserInterface/ + GameLib/ for SetParalysis/Paralysis, AFFECT_SLEEP/SetSleep, SetFaint/FaintTest, Stun/RecvStunPacket)"], "reason": "Investigated the movement.keyboard-motion.md Remaining item \"CActorInstance::CanAct() Paralysis/Faint/Sleep movement gates not wired into net_play.gd\" (flagged unfixed by the prior round). Findings: (1) IsParalysis()/m_isParalysis has exactly one setter in the whole reachable 40250 source, CInstanceBase::__Shaman_SetParalysis (InstanceBaseEffect.cpp:821-823), which itself has zero callers anywhere in the tree -- Paralysis is dead code, never true in live gameplay, so not wiring it is correct parity, not a gap. (2) IsSleep()/m_isSleep: the AFFECT_SLEEP case in SetAffect()s switch is commented out (dead); the only live setter is the AFFECT_STUN case (m_GraphicThingInstance.SetSleep(isVisible), ~InstanceBaseEffect.cpp:932-933) -- IsSleep() is functionally just an alias for AFFECT_STUN in practice, which the current clients Entity.stunned field already represents (entity_store.h:119) and net_play.gd::_can_process_network_state() already gates movement on (dead/stunned/knock_down) -- already covered, not a gap. (3) IsFaint()/m_isFaint: only setter is chrFaintTest() (PythonCharacterModule.cpp:1087-1105), a Python-bound debug helper (\"FaintTest\", registered alongside chrtestRestoreRenderMode) operating on GetSelectedInstancePtr(); the available 40250 checkout has no root/ Python UI/quest scripts to prove or disprove live-gameplay reachability -- evidence-blocked, left unresolved per SKILL.md do-not-audit-unreachable-code-as-product-behavior rule, no fix attempted. Byproduct finding while tracing IsStun(): CActorInstance::Stun() is only reachable via RecvStunPacket() (PythonNetworkStreamPhaseGame.cpp:1556-1578), which explicitly routes GC_STUN to Die() for the main character and to Stun() only for remote actors; verified this does NOT create a movement.keyboard.motion discrepancy because CanAct() blocks movement identically on IsDead() and IsStun(), and net_play.gd::_can_process_network_state() already blocks movement identically on dead and stunned -- the movement-blocking outcome is already correct parity regardless of which reference path fires. The Die()-vs-Stun() distinction only affects death-system semantics (m_isRealDead, EXP drop, corpse/respawn flow), which is combat.death.exp_drop/combat.affect_status territory and is already tracked there (combat.affect-status.md Remaining #2, equivalence rows 63/67: native GC_STUN only sets stunned=true with no dedicated clear path) -- not re-logged as a new finding to avoid duplicate tracking. No code changed this round; removed the resolved Remaining bullet from manifest.json and movement.keyboard-motion.md, replaced with investigation evidence and citations. Contract stays PARTIAL (root-motion/resource-event-source/sync-hit-death-gate-stacking/real-model-bone-flip items remain open)."}
+1 -2
View File
@@ -279,8 +279,7 @@
"OnMove/OnMoving/OnWaiting/OnStop 与当前每帧 signal、NetPlay 节流的事件源逐项证明",
"同步、私店、受击、死亡、传送、情绪以及多种锁门叠加顺序;移动技能/普通锁定/私店/情绪的 held WASD 恢复已覆盖基础路径",
"真实角色模型/骨骼在快速反向输入时不翻转的模型级测试",
"输入事件到移动请求、停止请求和网络包的完整时间线",
"CActorInstance::CanAct() 的 Paralysis/Faint/Sleep 三个移动门未接入当前端 net_play.gd 的移动状态判定(新发现,2026-09-22,未修复,见合同 Remaining"
"输入事件到移动请求、停止请求和网络包的完整时间线"
],
"fingerprints": {
"reference": "20c74407914aba18dde8f76f7b86917372a56cc7333b2ca8bfeee76c39e357e4",
+29
View File
@@ -273,6 +273,35 @@ Paralysis/Faint/Sleep 移动门和真实模型姿态测试仍未闭合,下一
继续(`AccumulationMovement` 根运动差异或 Paralysis/Faint/Sleep 移动门是其中两个具体、
无需真实服务端/客户端证据即可推进的候选)。
### 2026-09-22T01:30Z`movement.keyboard.motion/canact-paralysis-faint-sleep-gate` 调查关闭(无需修复)
对上一轮标记的两个候选之一(`Paralysis/Faint/Sleep` 移动门)做完整可达性追踪后确认它是
**假阳性**,不是代码可执行的差异,本轮未改动 GDScript/C++ 实现:
- Paralysis:唯一 setter `__Shaman_SetParalysis``InstanceBaseEffect.cpp:821`)在整个可达
40250 源码树中零调用点,是死代码,40250 客户端实际运行时 `IsParalysis()` 恒为 false。
- Sleep`SetAffect()``AFFECT_SLEEP` 分支已被注释掉,唯一存活路径是
`AFFECT_STUN → SetSleep()``InstanceBaseEffect.cpp:932`),即 `IsSleep()` 实际上是
AFFECT_STUN 的别名,已被当前端 `entity_store.h:119``stunned` 字段和
`net_play.gd::_can_process_network_state()``dead/stunned/knock_down` 门覆盖。
- Faint:唯一 setter `chrFaintTest()``PythonCharacterModule.cpp:1087`)是与
`chrtestRestoreRenderMode` 并列的调试用 Python 绑定,作用于 `GetSelectedInstancePtr()`
现有 40250 检出没有 root/ Python 脚本可证明/证伪在线可达性,evidence-blocked,未修复。
调查过程中发现的衍生项(`IsStun()``RecvStunPacket()` 对主角 `GC_STUN``Die()`
而非 `Stun()`)核实后确认**不是** `movement.keyboard.motion` 差异——`CanAct()`
`IsDead()`/`IsStun()` 的移动阻断效果相同,当前端 `_can_process_network_state()` 也已对
`dead`/`stunned` 同等阻断,移动结果已正确等价;`Die()`/`Stun()` 分流只影响死亡系统本身
EXP 掉落/尸体/复活),已在 `combat.affect-status.md` Remaining #2、分支矩阵第
63/67 行登记,不在本合同重复记录。详见
`audit/contracts/movement.keyboard-motion.md` 的「调查轮次 2026-09-22T」小节。
**该分支已关闭,不要在没有新 live 证据的情况下重新调查 Paralysis/Faint/Sleep 或
GC_STUN 主角分流。**
`movement.keyboard.motion` 下一轮候选收窄为:`AccumulationMovement` 固定 300cm
根运动/连续速度替代差异、`OnMove/OnMoving/OnWaiting/OnStop` 资源事件源、同步/受击/
死亡/传送门叠加顺序、真实模型骨骼快切不翻转的模型级测试——四者均仍未闭合。
`network.login.phase_flow` 的下一主候选仍保持文件前面记录的
`network.login.phase_flow/auth-game-phase-matrix/live-package-order/server-initiated-phase-close`
本地结构和 owner 分支均已覆盖,只等新的真实服务端/Windows fixture;在没有新 fixture 前不要
+2 -2
View File
@@ -24,8 +24,8 @@ Total contracts: 45
## Pending branch items
- Total: 225
- P0: 94
- Total: 224
- P0: 93
- P1: 118
- P2: 13
- P3: 0