nmarshall commited on
Commit
9829445
·
1 Parent(s): 73a5d10

qvm: ordinary contact factor handoff

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -805,3 +805,6 @@ Research progress: RE-712 [report](research/QVM_CONTACT_DISPATCH.md); remaining
805
 
806
 
807
  RE-703–712 batch complete: [damage factor and contact dispatch](research/QVM_DAMAGE_FACTOR_BATCH.md). Complete289594 handles original-model contact factors and wound bookkeeping;211239 is still captured. RE-702 is partially advanced and RE-713 tracks contact/event/health composition, native parity and Rust/Bevy/Solari platform work. No playable-game milestone is claimed.
 
 
 
 
805
 
806
 
807
  RE-703–712 batch complete: [damage factor and contact dispatch](research/QVM_DAMAGE_FACTOR_BATCH.md). Complete289594 handles original-model contact factors and wound bookkeeping;211239 is still captured. RE-702 is partially advanced and RE-713 tracks contact/event/health composition, native parity and Rust/Bevy/Solari platform work. No playable-game milestone is claimed.
808
+
809
+
810
+ Research progress: RE-714 [report](research/QVM_CONTACT_FACTOR.md); remaining native/content/runtime/platform scope stays open.
docs/FINDINGS.md CHANGED
@@ -1151,3 +1151,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
1151
 
1152
 
1153
  RE-703–712 batch complete: [damage factor and contact dispatch](research/QVM_DAMAGE_FACTOR_BATCH.md). Complete289594 handles original-model contact factors and wound bookkeeping;211239 is still captured. RE-702 is partially advanced and RE-713 tracks contact/event/health composition, native parity and Rust/Bevy/Solari platform work. No playable-game milestone is claimed.
 
 
 
 
1151
 
1152
 
1153
  RE-703–712 batch complete: [damage factor and contact dispatch](research/QVM_DAMAGE_FACTOR_BATCH.md). Complete289594 handles original-model contact factors and wound bookkeeping;211239 is still captured. RE-702 is partially advanced and RE-713 tracks contact/event/health composition, native parity and Rust/Bevy/Solari platform work. No playable-game milestone is claimed.
1154
+
1155
+
1156
+ **RE-714 — Ordinary contact factor composition:** Ordinary292545 now calls complete289594, multiplies its returned float factor by100 and suppresses the scalar when the output location is zero. [Evidence](research/QVM_CONTACT_FACTOR.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1140,3 +1140,6 @@ Research progress: RE-712 [report](research/QVM_CONTACT_DISPATCH.md); remaining
1140
 
1141
 
1142
  RE-703–712 batch complete: [damage factor and contact dispatch](research/QVM_DAMAGE_FACTOR_BATCH.md). Complete289594 handles original-model contact factors and wound bookkeeping;211239 is still captured. RE-702 is partially advanced and RE-713 tracks contact/event/health composition, native parity and Rust/Bevy/Solari platform work. No playable-game milestone is claimed.
 
 
 
 
1140
 
1141
 
1142
  RE-703–712 batch complete: [damage factor and contact dispatch](research/QVM_DAMAGE_FACTOR_BATCH.md). Complete289594 handles original-model contact factors and wound bookkeeping;211239 is still captured. RE-702 is partially advanced and RE-713 tracks contact/event/health composition, native parity and Rust/Bevy/Solari platform work. No playable-game milestone is claimed.
1143
+
1144
+
1145
+ Research progress: RE-714 [report](research/QVM_CONTACT_FACTOR.md); remaining native/content/runtime/platform scope stays open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -1205,3 +1205,8 @@ Contact293373 recomputes global forward/right/up from attacker angles, calls2112
1205
 
1206
 
1207
  Batch RE-703–712 closes ten independently checked research tasks in ten separate commits, advancing the Python baseline1259→1300. All ten reports regenerate byte-identically below2MiB. See the [batch overview](research/QVM_DAMAGE_FACTOR_BATCH.md) for findings, native-model boundaries and remaining work. Original assets, Rust code and the shared VM evaluator are unchanged. RE-702 remains partially open; RE-713 records the next scope.
 
 
 
 
 
 
1205
 
1206
 
1207
  Batch RE-703–712 closes ten independently checked research tasks in ten separate commits, advancing the Python baseline1259→1300. All ten reports regenerate byte-identically below2MiB. See the [batch overview](research/QVM_DAMAGE_FACTOR_BATCH.md) for findings, native-model boundaries and remaining work. Original assets, Rust code and the shared VM evaluator are unchanged. RE-702 remains partially open; RE-713 records the next scope.
1208
+
1209
+
1210
+ ## 2026-10-21 — RE-714 Ordinary contact factor composition (separate task commit)
1211
+
1212
+ Ordinary292545 now calls complete289594, multiplies its returned float factor by100 and suppresses the scalar when the output location is zero. 96 weapon/location/equipment/clock/override fixtures verify actual pointer handoff, float32 scaling and zero-location suppression. Full suite: 1304 tests pass. Report regenerates byte-identically (311243 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -1183,3 +1183,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1183
 
1184
 
1185
  - [ ] RE-713 Compose complete contact292545 and health211239, continue shared/pellet callers, capture native parity, and integrate Rust/Bevy/Solari toward playable macOS/Linux builds. See the [next ten tasks](research/QVM_DAMAGE_FACTOR_BATCH.md#remaining-limits-and-next-ten-tasks).
 
 
 
 
1183
 
1184
 
1185
  - [ ] RE-713 Compose complete contact292545 and health211239, continue shared/pellet callers, capture native parity, and integrate Rust/Bevy/Solari toward playable macOS/Linux builds. See the [next ten tasks](research/QVM_DAMAGE_FACTOR_BATCH.md#remaining-limits-and-next-ten-tasks).
1186
+
1187
+
1188
+ - [x] RE-714 Ordinary contact factor composition ([report](research/QVM_CONTACT_FACTOR.md)).
docs/research/QVM_CONTACT_FACTOR.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Ordinary contact factor composition
2
+
3
+ RE-714. Reproduce with `python3 scripts/analyze_qvm_contact_factor.py`; check with `python3 scripts/test_qvm_contact_factor.py`. The [machine report](qvm-contact-factor.json) retains source identity and bounded fixtures.
4
+
5
+ Actual292675–292735 composes complete289594 using the live caller location pointer LOCAL60 and extra accumulator pointer. The fixture starts scalar/location at zero as the recovered contact prefix does. Both client-presence branches select integer100, converted to float32 for multiplication. A zero output location then forces scalar0, even when the factor child final override returns1. Child inventory executes recovered32155/32117; spawn-protection notifications are captured. Source caller/child call trees and external accumulator results are retained. Spatial buffers and full client records occupy separate ranges. No event or211239 health execution occurs.
6
+
7
+ No original executable/full QVM/native collision/physics/rendering executes. Native responses and legal input domains are supplied or unverified where stated; full runtime/macOS/Linux Rust/Bevy/Solari gameplay remains open.
docs/research/README.md CHANGED
@@ -741,3 +741,6 @@ The extension collector adds five focused tests for native tail calls, command-r
741
 
742
 
743
  [RE-703–712 batch overview and next tasks](QVM_DAMAGE_FACTOR_BATCH.md).
 
 
 
 
741
 
742
 
743
  [RE-703–712 batch overview and next tasks](QVM_DAMAGE_FACTOR_BATCH.md).
744
+
745
+
746
+ [RE-714: Ordinary contact factor composition](QVM_CONTACT_FACTOR.md). Reproduce with `python3 scripts/analyze_qvm_contact_factor.py`; check with `python3 scripts/test_qvm_contact_factor.py`.
docs/research/qvm-contact-factor.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_contact_factor.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Actual ordinary contact to complete damage-factor child and scalar publication."""
3
+ import itertools
4
+ from pathlib import Path
5
+ from qvm_research_support import source
6
+ from qvm_contact_support import seed,run,L
7
+ from analyze_qvm import write_json
8
+
9
+ def main():
10
+ q,raw,h,ins=source();rows=[]
11
+ for loc,w,armor,now,override in itertools.product([0,1,3,11],[3,4,24],[False,True],[3999,4000],[0,1]):
12
+ a=dict(location=loc,weapon=w,helmet_slot=15 if armor else -1,vest_slot=0 if armor else -1,now=now,override=override,special=False);m=seed(raw,h,**a);m[L+60]=0;m[L+56]=0;_,p=run(ins,m,292675,292833);p['input']=a;rows.append(p)
13
+ write_json(Path('docs/research/qvm-contact-factor.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Actual292675..292735 composed with complete289594 and actual inventory. No event/health execution. Explicit client/clock/location fixtures; native notification effects captured.'})
14
+ if __name__=='__main__':main()
scripts/qvm_contact_support.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Separated contact fixtures and explicit recovered children; no world runtime."""
2
+ import math
3
+ from qvm_research_support import constants
4
+ from qvm_trace_support import resume,captured
5
+ from analyze_qvm_acceleration import word,number
6
+ from analyze_qvm_contact_gates import LOCAL as L,ATTACKER,TARGET
7
+ from analyze_qvm_direction_codec import table,encode
8
+ from analyze_qvm_interaction_request import inventory_check
9
+ from analyze_qvm_victim_stats import mode
10
+ from analyze_qvm_temp_event import temp_event,record
11
+ from qvm_server_fire_support import PROJECTILE as EVENT
12
+ from qvm_damage_support import L as DAMAGE_LOCAL
13
+ A_CLIENT=L+0x10000
14
+ V_CLIENT=L+0x20000
15
+ TRACE=L+0x30000
16
+ CONTEXT=L+0x40000
17
+ EXTRA=L+0x50000
18
+ FEEDBACK_LOCAL=0x7b100000
19
+
20
+
21
+ def seed(raw,h,**a):
22
+ m=constants(raw,h,list(range(3704,3768,4))+list(range(4068,4068+432*34,4))+list(range(32796,32856,4)))
23
+ m.update(table(raw,h))
24
+ for p,size in [(L,512),(DAMAGE_LOCAL,128),(ATTACKER,880),(TARGET,880),(A_CLIENT,7000),(V_CLIENT,7000),(EVENT,880),(CONTEXT,308),(TRACE,56)]:
25
+ for o in range(0,size,4):m[p+o]=0
26
+ m.update({L+132:ATTACKER,L+136:TRACE,L+140:a.get('weapon',3),L+144:CONTEXT,L+148:a.get('flag',0),L+152:EXTRA,L+48:TARGET,L+52:int(a.get('special',False)),L+56:word(a.get('scalar',35)),L+60:a.get('location',3),L+44:EVENT,ATTACKER:7,TARGET:a.get('victim_id',65),ATTACKER+520:A_CLIENT if a.get('attacker_client',True) else 0,TARGET+520:V_CLIENT if a.get('client',True) else 0,TARGET+688:a.get('damageable',1),TARGET+684:a.get('health',100),TARGET+552:a.get('entity_flags',0),TRACE+52:65,TRACE+48:a.get('surface_flags',0),TRACE+44:a.get('surface_word',99),CONTEXT+64:a.get('count',1),CONTEXT+60:a.get('part',2),CONTEXT+68:a.get('location',3),CONTEXT+84:word(a.get('distance',0)),EXTRA:word(a.get('extra',0.25)),A_CLIENT+3096:1,V_CLIENT+3096:1 if a.get('same_team',False) else 2,V_CLIENT+208:a.get('client_health',100),V_CLIENT+4140:1000,V_CLIENT+220:a.get('client_flags',8),V_CLIENT+4312:word(0),34992532:a.get('now',5000),34074804:3,34090788:a.get('mode',3),34087532:a.get('friendly_fire',0),34066516:a.get('override',0),34081316:a.get('announce',0)})
27
+ for slot,item in [(a.get('vest_slot',-1),17),(a.get('helmet_slot',-1),22)]:
28
+ if slot>=0:m[V_CLIENT+376+4*slot]=0xaa0000|item
29
+ for p,values in [(ATTACKER+24,a.get('attacker_origin',(3,4,0))),(TARGET+24,a.get('target_origin',(0,0,0))),(ATTACKER+60,a.get('angles',(0,0,0))),(TRACE+12,a.get('point',(1.9,-2.9,3.1))),(TRACE+24,a.get('normal',(0,0,1))),(36477748,a.get('forward',(0.3333,-0.002,1))),(36477712,a.get('muzzle',(1.25,-2.5,3.75)))]:
30
+ for j,v in enumerate(values):m[p+4*j]=word(v)
31
+ return m
32
+
33
+
34
+ def inventory(i,m,a):
35
+ m,_,v,r=inventory_check(i,m,a,(0,0,0));return m,v,r
36
+
37
+
38
+ def predicate(i,m,a):
39
+ m,r=mode(i,m,a[8]);return m,r['result'],r
40
+
41
+
42
+ def factor(i,m,a):
43
+ for o,v in a.items():m[DAMAGE_LOCAL+84+o]=v
44
+ before={str(o):m[a[12]+o] for o in [684,836]};notices=[]
45
+ m,e=resume(i,289595,290491,m,DAMAGE_LOCAL,handlers={32155:inventory,278003:captured(278003,notices)})
46
+ return m,e['return'],{'execution':e,'location':m[a[24]],'extra_word':m[a[28]]&0xffffffff,'initial_target':before,'notices':notices}
47
+
48
+
49
+ def feedback(i,m,a):
50
+ for o,v in a.items():m[FEEDBACK_LOCAL+28+o]=v
51
+ m,e=resume(i,285143,285225,m,FEEDBACK_LOCAL,handlers={33715:predicate})
52
+ return m,0,e
53
+
54
+
55
+ def handlers(effects):
56
+ def event(i,m,a):
57
+ m,r=temp_event(i,m,a[8],a[12]);effects['events'].append(r);return m,EVENT,r
58
+ def codec(i,m,a):
59
+ m,r=encode(i,m,[m[a[8]+4*j] for j in range(3)],pointer=a[8]);return m,r['result'],r
60
+ def trig(fn):
61
+ def call(i,m,a):v=word(fn(number(a[8])));return m,v,{'input_word':a[8]&0xffffffff,'result_word':v,'native_math_modeled':True}
62
+ return call
63
+ def bookkeeping(i,m,a):
64
+ m,v,r=feedback(i,m,a);effects['feedback'].append({'arguments':a,'execution':r});return m,v,r
65
+ return {289594:factor,32155:inventory,33715:predicate,277695:event,60317:codec,-104:trig(math.sin),-105:trig(math.cos),-107:trig(math.sqrt),7259:captured(7259,effects['announcements']),278003:captured(278003,effects['notices']),285142:bookkeeping,211239:captured(211239,effects['damage'])}
66
+
67
+
68
+ def run(ins,m,start,stop,boundaries=(),include_event=False):
69
+ effects={k:[] for k in ['events','announcements','notices','feedback','damage']}
70
+ m,e=resume(ins,start,stop,m,L,handlers=handlers(effects),boundaries=boundaries,limit=20000)
71
+ p={'scalar_word':m[L+56]&0xffffffff,'location':m[L+60],'special':m[L+52],'extra_word':m[EXTRA]&0xffffffff,'target_health':m[TARGET+684],'execution':e,'effects':effects}
72
+ if include_event:p['event_record']=record(m,EVENT)
73
+ return m,p
scripts/test_qvm_contact_factor.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json,unittest
2
+ from pathlib import Path
3
+ from test_qvm_ground import word,n,f
4
+ class ContactFactorTests(unittest.TestCase):
5
+ @classmethod
6
+ def setUpClass(cls):cls.ps=json.loads(Path('docs/research/qvm-contact-factor.json').read_text())['probes']
7
+ def test_actual_factor_return_scaled_in_float32(self):
8
+ for p in self.ps:
9
+ children=p['execution']['children'];self.assertEqual(len(children),1);factor=children[0]['effect'];v=0 if p['location']==0 else f(n(factor['execution']['return'])*100);self.assertEqual(p['scalar_word'],word(v))
10
+ def test_output_location_comes_from_child_pointer(self):
11
+ from qvm_contact_support import L
12
+ for p in self.ps:
13
+ child=p['execution']['children'][0];self.assertEqual(child['call']['args']['24'],L+60);self.assertEqual(p['location'],child['effect']['location'])
14
+ def test_zero_location_suppresses_even_override(self):
15
+ rows=[p for p in self.ps if p['input']['location']==0 and p['input']['override'] and p['input']['now']==4000];self.assertTrue(rows)
16
+ for p in rows:self.assertEqual(p['scalar_word'],0)
17
+ def test_only_factor_call_and_no_health_or_events(self):
18
+ self.assertEqual(len(self.ps),96)
19
+ for p in self.ps:self.assertEqual([c['target'] for c in p['execution']['calls']],[289594]);self.assertEqual(p['target_health'],100);self.assertTrue(all(not v for v in p['effects'].values()))
20
+ if __name__=='__main__':unittest.main()