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:
co-authored by
Claude Sonnet 5
parent
7df97e01f9
commit
a4ac48db24
@@ -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 备注移除。
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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 前不要
|
||||
|
||||
@@ -24,8 +24,8 @@ Total contracts: 45
|
||||
|
||||
## Pending branch items
|
||||
|
||||
- Total: 225
|
||||
- P0: 94
|
||||
- Total: 224
|
||||
- P0: 93
|
||||
- P1: 118
|
||||
- P2: 13
|
||||
- P3: 0
|
||||
|
||||
Reference in New Issue
Block a user