nmarshall commited on
Commit
b095795
·
1 Parent(s): 47bc718

qvm: compose pre-movement client processing

Browse files
docs/FINDINGS.md CHANGED
@@ -1895,3 +1895,5 @@ RE-979: Rust matches120 original inactivity cases, including signed wrap, pause
1895
  RE-980: Ninety-six source setup cases recover movement-type precedence, pause-mask1 handling, finite gravity multiplication/truncation and unpaused speed220. A present controller is consumed only on unpaused paths. Packed words use raw shift/OR without byte masking, and the old event sequence is saved. Execution stops at154544 before scratch initialization and later callbacks; actual movement remains open. [Evidence](research/QVM_CLIENT_MOVEMENT_SETUP.md).
1896
 
1897
  RE-981: Rust matches96 original movement-setup cases, preserving type precedence, pause-mask1 behavior, f32 multiplication/truncation, controller consumption and unmasked packed words. Unsupported unpaused float/conversion domains reject atomically; the paused path ignores unused float inputs. Unit tests cover negative truncation, high-bit packing, controller retention and rollback. Actual movement callbacks remain open. [Evidence](research/RUST_CLIENT_MOVEMENT_SETUP.md).
 
 
 
1895
  RE-980: Ninety-six source setup cases recover movement-type precedence, pause-mask1 handling, finite gravity multiplication/truncation and unpaused speed220. A present controller is consumed only on unpaused paths. Packed words use raw shift/OR without byte masking, and the old event sequence is saved. Execution stops at154544 before scratch initialization and later callbacks; actual movement remains open. [Evidence](research/QVM_CLIENT_MOVEMENT_SETUP.md).
1896
 
1897
  RE-981: Rust matches96 original movement-setup cases, preserving type precedence, pause-mask1 behavior, f32 multiplication/truncation, controller consumption and unmasked packed words. Unsupported unpaused float/conversion domains reject atomically; the paused path ignores unused float inputs. Unit tests cover negative truncation, high-bit packing, controller retention and rollback. Actual movement callbacks remain open. [Evidence](research/RUST_CLIENT_MOVEMENT_SETUP.md).
1898
+
1899
+ RE-982: Forty-eight source154089 executions compose preparation, routing, rewritten inputs, actual inactivity and movement setup through154544. Blocking movement can turn an otherwise active command into a timeout; early exits retain preceding timestamp/command mutations. Inactivity receives the pre-round delta, including the allowed negative exception. Native routing/team/send/drop effects remain captured; scratch setup and actual movement are beyond the boundary. [Evidence](research/QVM_CLIENT_PRE_MOVEMENT.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1949,3 +1949,5 @@ RE-964–973 completed timer deadline/phase/recovery contracts, whole-body compo
1949
  - [x] RE-980 Original movement parameter selection and packed words ([evidence](research/QVM_CLIENT_MOVEMENT_SETUP.md)).
1950
 
1951
  - [x] RE-981 Rust finite movement parameters and one-shot controller handling ([evidence](research/RUST_CLIENT_MOVEMENT_SETUP.md)).
 
 
 
1949
  - [x] RE-980 Original movement parameter selection and packed words ([evidence](research/QVM_CLIENT_MOVEMENT_SETUP.md)).
1950
 
1951
  - [x] RE-981 Rust finite movement parameters and one-shot controller handling ([evidence](research/RUST_CLIENT_MOVEMENT_SETUP.md)).
1952
+
1953
+ - [x] RE-982 Original pre-movement client pipeline and early-exit state ([evidence](research/QVM_CLIENT_PRE_MOVEMENT.md)).
docs/RESEARCH_LEDGER.md CHANGED
@@ -2597,3 +2597,7 @@ Ninety-six source setup cases recover movement-type precedence, pause-mask1 hand
2597
  ## 2026-10-28 — RE-981 Rust finite movement parameters and one-shot controller handling
2598
 
2599
  Rust matches96 original movement-setup cases, preserving type precedence, pause-mask1 behavior, f32 multiplication/truncation, controller consumption and unmasked packed words. Unsupported unpaused float/conversion domains reject atomically; the paused path ignores unused float inputs. Unit tests cover negative truncation, high-bit packing, controller retention and rollback. Actual movement callbacks remain open. Focused `cargo xtask test-group rust-client-movement-setup` passes with deterministic report regeneration, relevant regressions and storage/documentation/whitespace checks. Full suite deferred to the tenth-commit boundary. Original assets unchanged; native IO effects remain captured.
 
 
 
 
 
2597
  ## 2026-10-28 — RE-981 Rust finite movement parameters and one-shot controller handling
2598
 
2599
  Rust matches96 original movement-setup cases, preserving type precedence, pause-mask1 behavior, f32 multiplication/truncation, controller consumption and unmasked packed words. Unsupported unpaused float/conversion domains reject atomically; the paused path ignores unused float inputs. Unit tests cover negative truncation, high-bit packing, controller retention and rollback. Actual movement callbacks remain open. Focused `cargo xtask test-group rust-client-movement-setup` passes with deterministic report regeneration, relevant regressions and storage/documentation/whitespace checks. Full suite deferred to the tenth-commit boundary. Original assets unchanged; native IO effects remain captured.
2600
+
2601
+ ## 2026-10-28 — RE-982 Original pre-movement client pipeline and early-exit state
2602
+
2603
+ Forty-eight source154089 executions compose preparation, routing, rewritten inputs, actual inactivity and movement setup through154544. Blocking movement can turn an otherwise active command into a timeout; early exits retain preceding timestamp/command mutations. Inactivity receives the pre-round delta, including the allowed negative exception. Native routing/team/send/drop effects remain captured; scratch setup and actual movement are beyond the boundary. Focused `cargo xtask test-group qvm-client-pre-movement` passes with deterministic report regeneration, relevant regressions and storage/documentation/whitespace checks. Full suite deferred to the tenth-commit boundary. Original assets unchanged; native IO effects remain captured.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -1986,3 +1986,5 @@ RE-964–973 recover the complete bounded timer body and its caller timing prefi
1986
  - [x] RE-980 Original movement parameter selection and packed words ([evidence](research/QVM_CLIENT_MOVEMENT_SETUP.md)).
1987
 
1988
  - [x] RE-981 Rust finite movement parameters and one-shot controller handling ([evidence](research/RUST_CLIENT_MOVEMENT_SETUP.md)).
 
 
 
1986
  - [x] RE-980 Original movement parameter selection and packed words ([evidence](research/QVM_CLIENT_MOVEMENT_SETUP.md)).
1987
 
1988
  - [x] RE-981 Rust finite movement parameters and one-shot controller handling ([evidence](research/RUST_CLIENT_MOVEMENT_SETUP.md)).
1989
+
1990
+ - [x] RE-982 Original pre-movement client pipeline and early-exit state ([evidence](research/QVM_CLIENT_PRE_MOVEMENT.md)).
docs/research/QVM_CLIENT_PRE_MOVEMENT.md ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # Original pre-movement client pipeline and early-exit state
2
+
3
+ RE-982. Focused validation: `cargo xtask test-group qvm-client-pre-movement`. [Machine report](qvm-client-pre-movement.json).
4
+
5
+ Forty-eight source154089 executions compose preparation, routing, rewritten inputs, actual inactivity and movement setup through154544. Blocking movement can turn an otherwise active command into a timeout; early exits retain preceding timestamp/command mutations. Inactivity receives the pre-round delta, including the allowed negative exception. Native routing/team/send/drop effects remain captured; scratch setup and actual movement are beyond the boundary.
docs/research/README.md CHANGED
@@ -1521,3 +1521,5 @@ The extension collector adds five focused tests for native tail calls, command-r
1521
  - [RE-980: Original movement parameter selection and packed words](QVM_CLIENT_MOVEMENT_SETUP.md).
1522
 
1523
  - [RE-981: Rust finite movement parameters and one-shot controller handling](RUST_CLIENT_MOVEMENT_SETUP.md).
 
 
 
1521
  - [RE-980: Original movement parameter selection and packed words](QVM_CLIENT_MOVEMENT_SETUP.md).
1522
 
1523
  - [RE-981: Rust finite movement parameters and one-shot controller handling](RUST_CLIENT_MOVEMENT_SETUP.md).
1524
+
1525
+ - [RE-982: Original pre-movement client pipeline and early-exit state](QVM_CLIENT_PRE_MOVEMENT.md).
docs/research/qvm-client-pre-movement.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_client_pre_movement.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Verified original client-processing source contract."""
2
+ from pathlib import Path
3
+ from analyze_qvm import write_json
4
+ from qvm_research_support import source
5
+ from qvm_client_processing_support import seed,pipeline,pipeline_fixtures
6
+
7
+ def main():
8
+ q,raw,h,ins=source();probes=[]
9
+ for a in pipeline_fixtures():
10
+ m,b=seed(raw,h,a);m,p,b=pipeline(ins,m,b,a);p['input']=a;probes.append(p)
11
+ write_json(Path('docs/research/qvm-client-pre-movement.json'),dict(source=q['source'],sha256=q['sha256'],probes=probes,limits='48 actual154089 executions through154544 or original early return155716 compose timestamp prep, early routing, command byte rewrites, actual150382 inactivity and movement setup. Rewritten inputs drive activity; earlier timestamp/command mutations persist on ordinary early exits. Routed callbacks/native effects captured; movement scratch initialization and later physics remain open.'))
12
+ if __name__=='__main__':main()
scripts/qvm_client_processing_oracle.py CHANGED
@@ -4,7 +4,7 @@ def signed(v):v&=0xffffffff;return v if v<2**31 else v-2**32
4
  def gate(a):
5
  if a['intermission']:return dict(events=[dict(op='intermission',slot=2)],proceed=False)
6
  if a['team']==3 or a['follow']:
7
- return dict(events=[] if a['spectator']==3 else [dict(op='spectator',slot=2,command_time=12345)],proceed=False)
8
  return dict(events=[],proceed=True)
9
 
10
  def command(a):
@@ -37,3 +37,13 @@ def movement(a):
37
  f32=lambda x:struct.unpack('<f',struct.pack('<f',x))[0]
38
  paused=bool(a['pause']&1);gravity=0 if paused else int(f32(f32(a['gravity'])*f32(a['factor']))) if a['controller'] else int(f32(a['gravity']))
39
  return dict(pm_type=1 if a['noclip'] else 3 if signed(a['health'])<=0 else 0,gravity_word=gravity&0xffffffff,speed=0 if paused else 220,controller=a['controller'] if paused else 0,packed=[(a['packs'][j]|(a['packs'][j+1]<<8))&0xffffffff for j in [0,2]],event_sequence=a['event_sequence'])
 
 
 
 
 
 
 
 
 
 
 
4
  def gate(a):
5
  if a['intermission']:return dict(events=[dict(op='intermission',slot=2)],proceed=False)
6
  if a['team']==3 or a['follow']:
7
+ return dict(events=[] if a['spectator']==3 else [dict(op='spectator',slot=2,command_time=a.get('command_time',12345))],proceed=False)
8
  return dict(events=[],proceed=True)
9
 
10
  def command(a):
 
37
  f32=lambda x:struct.unpack('<f',struct.pack('<f',x))[0]
38
  paused=bool(a['pause']&1);gravity=0 if paused else int(f32(f32(a['gravity'])*f32(a['factor']))) if a['controller'] else int(f32(a['gravity']))
39
  return dict(pm_type=1 if a['noclip'] else 3 if signed(a['health'])<=0 else 0,gravity_word=gravity&0xffffffff,speed=0 if paused else 220,controller=a['controller'] if paused else 0,packed=[(a['packs'][j]|(a['packs'][j+1]<<8))&0xffffffff for j in [0,2]],event_sequence=a['event_sequence'])
40
+
41
+ def pipeline(a):
42
+ from qvm_client_timer_oracle import prepare
43
+ pre=prepare(dict(a,exception=a['spectator']));cmd=dict(angles=list(a['angles']),saved=list(a['saved']),buttons=a['buttons'],move_word=42+sum(v<<(8*(j+1)) for j,v in enumerate(a['moves'])),command_time=pre['command_time']);idle_state=[a['deadline'],a['warned']];view=dict(pm_type=99,gravity_word=111,speed=222,controller=a['controller'],packed=[333,444],event_sequence=None);events=[];idle_result=None;idle_msec=None;done=False
44
+ if pre['proceed']:
45
+ routed=gate(dict(a,command_time=pre['command_time']));events.extend(routed['events'])
46
+ if routed['proceed']:
47
+ cmd=command(dict(a,**cmd,mode=a['game_mode']));moves=[(cmd['move_word']>>(8*(j+1)))&255 for j in range(3)];idle_msec=pre['msec'];idle_out=idle(dict(a,moves=moves,buttons=cmd['buttons'],mode=a['idle_mode'],msec=idle_msec));idle_result=idle_out['accepted'];events.extend(idle_out['events']);idle_state=[idle_out['deadline'],idle_out['warned']]
48
+ if idle_result:view=movement(a);done=True
49
+ return dict(proceed=done,events=events,msec=pre['msec'],idle_result=idle_result,idle_msec=idle_msec,timing=[pre['command_time'],pre['current_time'],pre['copied_tick'],pre['copied_command']],command=cmd,idle_state=idle_state,movement=view,health=a['health'])
scripts/qvm_client_processing_support.py CHANGED
@@ -82,3 +82,20 @@ def movement_view(m):return dict(pm_type=m[CLIENT+4]&0xffffffff,gravity_word=m[C
82
  def movement(ins,m,b,a):
83
  movement_seed(m,a);m,e,b=execute(ins,m,b,154405,154544)
84
  return m,dict(**movement_view(m),execution={k:e[k] for k in ['stop','trace_length','trace_sha256']}),b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  def movement(ins,m,b,a):
83
  movement_seed(m,a);m,e,b=execute(ins,m,b,154405,154544)
84
  return m,dict(**movement_view(m),execution={k:e[k] for k in ['stop','trace_length','trace_sha256']}),b
85
+
86
+ def pipeline_fixtures():
87
+ defaults=dict(connection=2,spectator=0,command_time=10050,current_time=10000,tick=10000,intermission=0,team=0,follow=0,pause=0,game_mode=0,flags=0,block=0,entity_flags=0,idle_seconds=60,pause_anchor=3000,health=1,exempt=0,idle_mode=0,noclip=0,controller=0,gravity=800.75,factor=0.5,deadline=20000,warned=0,moves=[0,0,0],buttons=0,angles=[11,22,33],saved=[44,55,66],packs=[0x1ff,0x102,0x80000000,0xff0001],event_sequence=0xdeadbeef)
88
+ profiles=[('normal',{}),('disconnected',dict(connection=1)),('stale',dict(command_time=10000)),('negative-exception',dict(command_time=8000,spectator=2,health=0,deadline=9000)),('intermission',dict(intermission=1)),('spectator',dict(team=3)),('spectator-return',dict(team=3,spectator=3)),('follow',dict(follow=1)),('pause-controller',dict(pause=1,controller=1)),('other-pause',dict(pause=2)),('mode10-lock',dict(game_mode=10,flags=16)),('mode10-normal',dict(game_mode=10)),('blocked-timeout',dict(block=1,moves=[1,0,0],deadline=9999)),('movement-saves',dict(moves=[1,0,0],deadline=9999)),('dead',dict(health=0,deadline=9999)),('warning',dict(deadline=19999)),('timeout',dict(deadline=9999)),('spectator-timeout',dict(idle_mode=1,deadline=9999)),('exempt',dict(exempt=1,deadline=0)),('protected',dict(entity_flags=8,deadline=0)),('disabled',dict(idle_seconds=0,warned=1)),('noclip',dict(noclip=1)),('controller',dict(controller=1,factor=-0.5)),('round-nine',dict(command_time=9999,current_time=9990))]
89
+ return [dict(defaults,**changes,name=name,quantize=q) for name,changes in profiles for q in [0,1]]
90
+
91
+ def pipeline(ins,m,b,a):
92
+ idle_seed(m,dict(a,mode=a['idle_mode']));command_seed(m,dict(a,mode=a['game_mode'],move_word=42+sum(v<<(8*(j+1)) for j,v in enumerate(a['moves']))));movement_seed(m,a)
93
+ m.update({34999596:a['intermission'],CLIENT+3096:a['team'],CLIENT+4324:a['follow'],CLIENT+3108:a['spectator'],CLIENT+468:a['connection'],CLIENT+472:a['command_time'],CLIENT:a['current_time'],CLIENT+712:a['quantize'],CLIENT+6172:17,CLIENT+6176:18})
94
+ events=[];idle_result=None;idle_msec=None
95
+ def actual_idle(i,m,args,bs):
96
+ nonlocal idle_result,idle_msec
97
+ assert args[8]==CLIENT
98
+ idle_msec=args[12]&0xffffffff;m,p,b=idle_run(i,m,dict(bs),args[12]);idle_result=p['accepted'];events.extend(p['events']);return m,int(idle_result),{'actual_source_child':150382}
99
+ handlers={**gate_handlers(events),150382:actual_idle};m,e,b=execute(ins,m,b,154090,155717,handlers,(154544,155716));done=e['stop']==154544
100
+ view=dict(pm_type=m[CLIENT+4]&0xffffffff,gravity_word=m[CLIENT+48]&0xffffffff,speed=m[CLIENT+52]&0xffffffff,controller=int(m[ENTITY+852]!=0),packed=[m[CLIENT+288]&0xffffffff,m[CLIENT+292]&0xffffffff],event_sequence=m[ROOT+332]&0xffffffff if done else None)
101
+ p=dict(proceed=done,events=events,msec=None if a['connection']!=2 else m[ROOT+328]&0xffffffff,idle_result=idle_result,idle_msec=idle_msec,timing=[m[CLIENT+o]&0xffffffff for o in [472,0,6172,6176]],command=command_view(m),idle_state=[m[CLIENT+3140]&0xffffffff,m[CLIENT+4144]&0xffffffff],movement=view,health=m[CLIENT+208]&0xffffffff,execution={k:e[k] for k in ['stop','trace_length','trace_sha256']});return m,p,b
scripts/test_qvm_client_pre_movement.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json,unittest
2
+ from pathlib import Path
3
+ from qvm_client_processing_oracle import pipeline
4
+ class OriginalTests(unittest.TestCase):
5
+ def test_original_contract(self):
6
+ rows=json.loads(Path('docs/research/qvm-client-pre-movement.json').read_text())['probes'];self.assertEqual(len(rows),48)
7
+ for p in rows:
8
+ for k,v in pipeline(p['input']).items():self.assertEqual(p[k],v)
9
+ def test_fixture_identity_and_trace(self):
10
+ r=json.loads(Path('docs/research/qvm-client-pre-movement.json').read_text());self.assertEqual(len(r['sha256']),64)
11
+ self.assertTrue(all(p['execution']['trace_length']>0 for p in r['probes']))
12
+ if __name__=='__main__':unittest.main()
xtask/checks.tsv CHANGED
@@ -3644,3 +3644,7 @@ edge crates/urt-gameplay/src/client_timer.rs scripts/analyze_rust_client_movemen
3644
  edge crates/urt-gameplay/src/bin/urt-client-timer.rs scripts/analyze_rust_client_movement_setup.py
3645
  edge crates/urt-gameplay/src/client_processing.rs scripts/analyze_rust_client_movement_setup.py
3646
  edge crates/urt-gameplay/src/bin/urt-client-processing.rs scripts/analyze_rust_client_movement_setup.py
 
 
 
 
 
3644
  edge crates/urt-gameplay/src/bin/urt-client-timer.rs scripts/analyze_rust_client_movement_setup.py
3645
  edge crates/urt-gameplay/src/client_processing.rs scripts/analyze_rust_client_movement_setup.py
3646
  edge crates/urt-gameplay/src/bin/urt-client-processing.rs scripts/analyze_rust_client_movement_setup.py
3647
+ report scripts/analyze_qvm_client_pre_movement.py docs/research/qvm-client-pre-movement.json
3648
+ edge docs/research/qvm-client-pre-movement.json scripts/test_qvm_client_pre_movement.py
3649
+ group qvm-client-pre-movement scripts/analyze_qvm_client_pre_movement.py
3650
+ edge scripts/analyze_qvm_client_pre_movement.py scripts/analyze_qvm_resumer_audit.py