research(motion): cdb live trace of retail walk-to-run transition

Live cdb trace of retail acclient.exe (v11.4186, PDB-matched) capturing
the exact function call sequence for a direct walk-to-run motion
transition where the user holds shift+W (walk) then releases SHIFT
while still holding W (transition to run).

Trace bps on:
- CPhysicsObj::DoInterpretedMotion (0x0050EA70)
- CPartArray::DoInterpretedMotion  (0x00518750)
- MotionTableManager::PerformMovement (0x0051C0B0)
- MotionTableManager::add_to_queue (0x0051BFE0)
- MotionTableManager::truncate_animation_list (0x0051BCA0)
- CMotionTable::DoObjectMotion (0x00523E90)
- CMotionTable::StopObjectMotion (0x00523EC0)

Captured trace at tools/cdb-scripts/walk_run_motion_trace.log shows
the precise walk-to-run sequence:

  [79] CPhysicsObj::DoInterpretedMotion: motion=45000005   walk start
  [82] CMotionTable::DoObjectMotion: motion=45000005
  [83] MotionTableManager::add_to_queue: arg1=45000005 arg2=00000001

  [89] CPhysicsObj::DoInterpretedMotion: motion=44000007   run start
  [92] CMotionTable::DoObjectMotion: motion=44000007
  [93] MotionTableManager::add_to_queue: arg1=44000007 arg2=00000001

  [104] CMotionTable::StopObjectMotion: motion=44000007    run end

Critical structural finding for #L.4-walk-run:

  Retail does NOT call truncate_animation_list during the walk→run
  transition. truncate_animation_list never fires in the entire 200-hit
  trace. Retail also does NOT call StopObjectMotion(WalkForward) before
  add_to_queue(RunForward). Retail just appends the new motion to the
  queue and lets MotionTableManager (and its CheckForCompletedMotions /
  remove_redundant_links per-tick cleanup, not yet traced) handle the
  natural progression.

  acdream's AnimationSequencer.SetCycle aggressively calls
  ClearCyclicTail() at line 430 BEFORE enqueuing the new cycle, which
  destroys the in-flight walk cycle's frames. The new run cycle is
  enqueued but _currNode is left in a state that doesn't smoothly
  continue — visible to the user as "it just blips forward walking,
  AS SOON as press another key like turning, its starts running"
  (the next motion event re-fires SetCycle which finally aligns state).

  Fix is a structural refactor of SetCycle to mirror retail's
  "additive queue with auto-cleanup" semantics. Out of scope for this
  research commit; filed as #L.4 in the next ISSUES.md entry.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-05-03 16:54:34 +02:00
parent a45c21ee51
commit b1d8e122ed
3 changed files with 191 additions and 0 deletions

View file

@ -0,0 +1,16 @@
$cdb = "C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\cdb.exe"
$script = "C:\Users\erikn\source\repos\acdream\tools\cdb-scripts\walk_run_motion_trace.cdb"
$log = "C:\Users\erikn\source\repos\acdream\tools\cdb-scripts\walk_run_motion_trace.log"
if (Test-Path $log) { Remove-Item $log }
Write-Host "Attaching cdb to acclient.exe..."
Write-Host "Once attached, do this in retail:"
Write-Host " 1. Stand still 2s"
Write-Host " 2. Hold shift+W (walk) 4s"
Write-Host " 3. Release SHIFT only, keep W, 4s (this is what we want to capture)"
Write-Host " 4. Release W"
Write-Host " 5. Wait for cdb to detach (or Ctrl+C this PS to detach manually)"
Write-Host ""
& $cdb -pn acclient.exe -cf $script *>&1 | Tee-Object -FilePath "$log.console"

View file

@ -0,0 +1,175 @@
Opened log file 'C:\Users\erikn\source\repos\acdream\tools\cdb-scripts\walk_run_motion_trace.log'
0:017> .sympath C:\Users\erikn\source\repos\acdream\refs
Symbol search path is: C:\Users\erikn\source\repos\acdream\refs
Expanded Symbol search path is: c:\users\erikn\source\repos\acdream\refs
************* Path validation summary **************
Response Time (ms) Location
OK C:\Users\erikn\source\repos\acdream\refs
0:017> .symopt+ 0x40
Symbol options are 0xB0367:
0x00000001 - SYMOPT_CASE_INSENSITIVE
0x00000002 - SYMOPT_UNDNAME
0x00000004 - SYMOPT_DEFERRED_LOADS
0x00000020 - SYMOPT_OMAP_FIND_NEAREST
0x00000040 - SYMOPT_LOAD_ANYTHING
0x00000100 - SYMOPT_NO_UNQUALIFIED_LOADS
0x00000200 - SYMOPT_FAIL_CRITICAL_ERRORS
0x00010000 - SYMOPT_AUTO_PUBLICS
0x00020000 - SYMOPT_NO_IMAGE_SEARCH
0x00080000 - SYMOPT_NO_PROMPTS
0:017> .reload /f acclient.exe
0:017>
0:017> r $t0 = 0
0:017>
0:017> bp acclient!CPhysicsObj::DoInterpretedMotion ".printf \"\\n[%d] CPhysicsObj::DoInterpretedMotion: motion=%08x speedBits=%08x\", @$t0, poi(esp+4), poi(esp+8); r $t0 = @$t0 + 1; .if (@$t0 >= 200) { .detach } .else { gc }"
0:017>
breakpoint 0 redefined
0:017> bp acclient!CPartArray::DoInterpretedMotion ".printf \"\\n[%d] CPartArray::DoInterpretedMotion: motion=%08x speedBits=%08x\", @$t0, poi(esp+4), poi(esp+8); r $t0 = @$t0 + 1; .if (@$t0 >= 200) { .detach } .else { gc }"
0:017>
breakpoint 1 redefined
0:017> bp acclient!MotionTableManager::PerformMovement ".printf \"\\n[%d] MotionTableManager::PerformMovement: motion=%08x speedBits=%08x holdkey=%08x\", @$t0, poi(esp+4), poi(esp+8), poi(esp+0xc); r $t0 = @$t0 + 1; .if (@$t0 >= 200) { .detach } .else { gc }"
0:017>
breakpoint 2 redefined
0:017> bp acclient!MotionTableManager::add_to_queue ".printf \"\\n[%d] MotionTableManager::add_to_queue: arg1=%08x arg2=%08x\", @$t0, poi(esp+4), poi(esp+8); r $t0 = @$t0 + 1; .if (@$t0 >= 200) { .detach } .else { gc }"
0:017>
breakpoint 3 redefined
0:017> bp acclient!MotionTableManager::truncate_animation_list ".printf \"\\n[%d] MotionTableManager::truncate_animation_list\", @$t0; r $t0 = @$t0 + 1; .if (@$t0 >= 200) { .detach } .else { gc }"
0:017>
breakpoint 4 redefined
0:017> bp acclient!CMotionTable::DoObjectMotion ".printf \"\\n[%d] CMotionTable::DoObjectMotion: motion=%08x\", @$t0, poi(esp+4); r $t0 = @$t0 + 1; .if (@$t0 >= 200) { .detach } .else { gc }"
0:017>
breakpoint 5 redefined
0:017> bp acclient!CMotionTable::StopObjectMotion ".printf \"\\n[%d] CMotionTable::StopObjectMotion: motion=%08x\", @$t0, poi(esp+4); r $t0 = @$t0 + 1; .if (@$t0 >= 200) { .detach } .else { gc }"
0:017>
breakpoint 6 redefined
0:017> g
[0] MotionTableManager::PerformMovement: motion=001afc60 speedBits=16f5e1f8 holdkey=16fa5c38
[1] CMotionTable::StopObjectMotion: motion=6500000d
[2] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[3] MotionTableManager::PerformMovement: motion=001afc2c speedBits=16f5e1f8 holdkey=16fa5c38
[4] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[5] MotionTableManager::PerformMovement: motion=001afbe4 speedBits=16f5e1f8 holdkey=16fa5c38
[6] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[7] CPhysicsObj::DoInterpretedMotion: motion=6500000d speedBits=001afcc0
[8] CPartArray::DoInterpretedMotion: motion=6500000d speedBits=001afcc0
[9] MotionTableManager::PerformMovement: motion=001afc14 speedBits=16f5e1f8 holdkey=16fa5c38
[10] CMotionTable::DoObjectMotion: motion=6500000d
[11] MotionTableManager::add_to_queue: arg1=6500000d arg2=00000000
[12] CPhysicsObj::DoInterpretedMotion: motion=8000003d speedBits=001afcb4
[13] CPartArray::DoInterpretedMotion: motion=8000003d speedBits=001afcb4
[14] MotionTableManager::PerformMovement: motion=001afc24 speedBits=16f5e4f8 holdkey=16fa5218
[15] CMotionTable::DoObjectMotion: motion=8000003d
[16] MotionTableManager::add_to_queue: arg1=8000003d arg2=00000000
[17] CPhysicsObj::DoInterpretedMotion: motion=10000054 speedBits=001afcb4
[18] CPartArray::DoInterpretedMotion: motion=10000054 speedBits=001afcb4
[19] MotionTableManager::PerformMovement: motion=001afc24 speedBits=16f5e4f8 holdkey=16fa5218
[20] CMotionTable::DoObjectMotion: motion=10000054
[21] MotionTableManager::add_to_queue: arg1=10000054 arg2=00000001
[22] MotionTableManager::PerformMovement: motion=001afc24 speedBits=16f5e4f8 holdkey=16fa5218
[23] CMotionTable::StopObjectMotion: motion=6500000f
[24] MotionTableManager::PerformMovement: motion=001afc40 speedBits=16f5e4f8 holdkey=16fa5218
[25] CMotionTable::StopObjectMotion: motion=6500000d
[26] CPhysicsObj::DoInterpretedMotion: motion=8000003d speedBits=001afcb4
[27] CPartArray::DoInterpretedMotion: motion=8000003d speedBits=001afcb4
[28] MotionTableManager::PerformMovement: motion=001afc24 speedBits=16f5e4f8 holdkey=16fa5218
[29] CMotionTable::DoObjectMotion: motion=8000003d
[30] MotionTableManager::add_to_queue: arg1=8000003d arg2=00000000
[31] CPhysicsObj::DoInterpretedMotion: motion=41000003 speedBits=001afcb4
[32] CPartArray::DoInterpretedMotion: motion=41000003 speedBits=001afcb4
[33] MotionTableManager::PerformMovement: motion=001afc24 speedBits=16f5e4f8 holdkey=16fa5218
[34] CMotionTable::DoObjectMotion: motion=41000003
[35] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[36] MotionTableManager::PerformMovement: motion=001afc24 speedBits=16f5e4f8 holdkey=16fa5218
[37] CMotionTable::StopObjectMotion: motion=6500000f
[38] MotionTableManager::PerformMovement: motion=001afc40 speedBits=16f5e4f8 holdkey=16fa5218
[39] CMotionTable::StopObjectMotion: motion=6500000d
[40] CPhysicsObj::DoInterpretedMotion: motion=8000003d speedBits=001af5e8
[41] CPartArray::DoInterpretedMotion: motion=8000003d speedBits=001af5e8
[42] MotionTableManager::PerformMovement: motion=001af558 speedBits=15f356a0 holdkey=13cf1420
[43] CMotionTable::DoObjectMotion: motion=8000003d
[44] MotionTableManager::add_to_queue: arg1=8000003d arg2=00000000
[45] CPhysicsObj::DoInterpretedMotion: motion=41000003 speedBits=001af5e8
[46] CPartArray::DoInterpretedMotion: motion=41000003 speedBits=001af5e8
[47] MotionTableManager::PerformMovement: motion=001af558 speedBits=15f356a0 holdkey=13cf1420
[48] CMotionTable::DoObjectMotion: motion=41000003
[49] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[50] MotionTableManager::PerformMovement: motion=001af558 speedBits=15f356a0 holdkey=13cf1420
[51] CMotionTable::StopObjectMotion: motion=6500000f
[52] MotionTableManager::PerformMovement: motion=001af574 speedBits=15f356a0 holdkey=13cf1420
[53] CMotionTable::StopObjectMotion: motion=6500000d
[54] MotionTableManager::PerformMovement: motion=001afc60 speedBits=16f5e1f8 holdkey=16fa5c38
[55] CMotionTable::StopObjectMotion: motion=6500000d
[56] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[57] MotionTableManager::PerformMovement: motion=001afc2c speedBits=16f5e1f8 holdkey=16fa5c38
[58] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[59] MotionTableManager::PerformMovement: motion=001afbe4 speedBits=16f5e1f8 holdkey=16fa5c38
[60] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[61] CPhysicsObj::DoInterpretedMotion: motion=6500000d speedBits=001afcc0
[62] CPartArray::DoInterpretedMotion: motion=6500000d speedBits=001afcc0
[63] MotionTableManager::PerformMovement: motion=001afc14 speedBits=16f5e1f8 holdkey=16fa5c38
[64] CMotionTable::DoObjectMotion: motion=6500000d
[65] MotionTableManager::add_to_queue: arg1=6500000d arg2=00000000
[66] MotionTableManager::PerformMovement: motion=001af408 speedBits=15f356a0 holdkey=13cf1420
[67] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[68] CPhysicsObj::DoInterpretedMotion: motion=41000003 speedBits=001af33c
[69] CPartArray::DoInterpretedMotion: motion=41000003 speedBits=001af33c
[70] MotionTableManager::PerformMovement: motion=001af2a0 speedBits=15f356a0 holdkey=13cf1420
[71] CMotionTable::DoObjectMotion: motion=41000003
[72] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[73] MotionTableManager::PerformMovement: motion=001af2a0 speedBits=15f356a0 holdkey=13cf1420
[74] CMotionTable::StopObjectMotion: motion=6500000f
[75] MotionTableManager::PerformMovement: motion=001af2a0 speedBits=15f356a0 holdkey=13cf1420
[76] CMotionTable::StopObjectMotion: motion=6500000d
[77] MotionTableManager::PerformMovement: motion=001af2a0 speedBits=15f356a0 holdkey=13cf1420
[78] CMotionTable::StopObjectMotion: motion=6500000f
[79] CPhysicsObj::DoInterpretedMotion: motion=45000005 speedBits=001af348
[80] CPartArray::DoInterpretedMotion: motion=45000005 speedBits=001af348
[81] MotionTableManager::PerformMovement: motion=001af2ac speedBits=15f356a0 holdkey=13cf1420
[82] CMotionTable::DoObjectMotion: motion=45000005
[83] MotionTableManager::add_to_queue: arg1=45000005 arg2=00000001
[84] CPhysicsObj::DoInterpretedMotion: motion=8000003d speedBits=001af5e0
[85] CPartArray::DoInterpretedMotion: motion=8000003d speedBits=001af5e0
[86] MotionTableManager::PerformMovement: motion=001af550 speedBits=15f356a0 holdkey=13cf1420
[87] CMotionTable::DoObjectMotion: motion=8000003d
[88] MotionTableManager::add_to_queue: arg1=8000003d arg2=00000000
[89] CPhysicsObj::DoInterpretedMotion: motion=44000007 speedBits=001af5e0
[90] CPartArray::DoInterpretedMotion: motion=44000007 speedBits=001af5e0
[91] MotionTableManager::PerformMovement: motion=001af550 speedBits=15f356a0 holdkey=13cf1420
[92] CMotionTable::DoObjectMotion: motion=44000007
[93] MotionTableManager::add_to_queue: arg1=44000007 arg2=00000001
[94] MotionTableManager::PerformMovement: motion=001af550 speedBits=15f356a0 holdkey=13cf1420
[95] CMotionTable::StopObjectMotion: motion=6500000f
[96] MotionTableManager::PerformMovement: motion=001af56c speedBits=15f356a0 holdkey=13cf1420
[97] CMotionTable::StopObjectMotion: motion=6500000d
[98] MotionTableManager::PerformMovement: motion=001afcfc speedBits=16f5e1f8 holdkey=16fa5c38
[99] CMotionTable::StopObjectMotion: motion=6500000d
[100] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[101] MotionTableManager::PerformMovement: motion=001afc84 speedBits=16f5e1f8 holdkey=16fa5c38
[102] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[103] MotionTableManager::PerformMovement: motion=001af2c0 speedBits=15f356a0 holdkey=13cf1420
[104] CMotionTable::StopObjectMotion: motion=44000007
[105] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000001
[106] CPhysicsObj::DoInterpretedMotion: motion=41000003 speedBits=001af4bc
[107] CPartArray::DoInterpretedMotion: motion=41000003 speedBits=001af4bc
[108] MotionTableManager::PerformMovement: motion=001af420 speedBits=15f356a0 holdkey=13cf1420
[109] CMotionTable::DoObjectMotion: motion=41000003
[110] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[111] MotionTableManager::PerformMovement: motion=001af420 speedBits=15f356a0 holdkey=13cf1420
[112] CMotionTable::StopObjectMotion: motion=6500000f
[113] MotionTableManager::PerformMovement: motion=001af420 speedBits=15f356a0 holdkey=13cf1420
[114] CMotionTable::StopObjectMotion: motion=6500000d
[115] MotionTableManager::PerformMovement: motion=001af420 speedBits=15f356a0 holdkey=13cf1420
[116] CMotionTable::StopObjectMotion: motion=6500000f
[117] CPhysicsObj::DoInterpretedMotion: motion=41000003 speedBits=001af47c
[118] CPartArray::DoInterpretedMotion: motion=41000003 speedBits=001af47c
[119] MotionTableManager::PerformMovement: motion=001af3e0 speedBits=15f356a0 holdkey=13cf1420
[120] CMotionTable::DoObjectMotion: motion=41000003
[121] MotionTableManager::add_to_queue: arg1=41000003 arg2=00000000
[122] MotionTableManager::PerformMovement: motion=001af3e0 speedBits=15f356a0 holdkey=13cf1420
[123] CMotionTable::StopObjectMotion: motion=6500000f
[124] MotionTableManager::PerformMovement: motion=001af3e0 speedBits=15f356a0 holdkey=13cf1420
[125] CMotionTable::StopObjectMotion: motion=6500000d
[126] MotionTableManager::PerformMovement: motion=001af3e0 speedBits=15f356a0 holdkey=13cf1420
[127] CMotionTable::StopObjectMotion: motion=6500000f