qvm: ordinary contact factor handoff
Browse files- docs/ASSET_PIPELINE_TODO.md +3 -0
- docs/FINDINGS.md +3 -0
- docs/PROVISIONAL_TODO.md +3 -0
- docs/RESEARCH_LEDGER.md +5 -0
- docs/REVERSE_ENGINEERING_TODO.md +3 -0
- docs/research/QVM_CONTACT_FACTOR.md +7 -0
- docs/research/README.md +3 -0
- docs/research/qvm-contact-factor.json +0 -0
- scripts/analyze_qvm_contact_factor.py +14 -0
- scripts/qvm_contact_support.py +73 -0
- scripts/test_qvm_contact_factor.py +20 -0
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()
|