nmarshall commited on
Commit
d24d8b9
·
1 Parent(s): 12313a3

qvm: damage protection and armor gates

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -772,3 +772,6 @@ Research progress: RE-700 [report](research/QVM_CONTACT_GATES.md); remaining nat
772
 
773
 
774
  Research progress: RE-701 [report](research/RUST_MD3.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
772
 
773
 
774
  Research progress: RE-701 [report](research/RUST_MD3.md); remaining native/content/runtime/platform scope stays open.
775
+
776
+
777
+ Research progress: RE-703 [report](research/QVM_DAMAGE_PROTECTION.md); remaining native/content/runtime/platform scope stays open.
docs/FINDINGS.md CHANGED
@@ -1118,3 +1118,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
1118
 
1119
 
1120
  **RE-701 — Rust original-byte MD3 asset core and cross-language validation:** A dependency-free Rust asset crate parses all619 originals with Python parity for headers, surfaces, sampled packed geometry, frame/tag float bits, UVs, triangles and shaders. [Evidence](research/RUST_MD3.md).
 
 
 
 
1118
 
1119
 
1120
  **RE-701 — Rust original-byte MD3 asset core and cross-language validation:** A dependency-free Rust asset crate parses all619 originals with Python parity for headers, surfaces, sampled packed geometry, frame/tag float bits, UVs, triangles and shaders. [Evidence](research/RUST_MD3.md).
1121
+
1122
+
1123
+ **RE-703 — Damage protection and armor location selection:** 289594 rejects mode9 and strictly active spawn protection before location output; actual32155/32117 reclassifies head/torso using live inventory low bytes and health, excluding slot16. [Evidence](research/QVM_DAMAGE_PROTECTION.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1107,3 +1107,6 @@ Research progress: RE-701 [report](research/RUST_MD3.md); remaining native/conte
1107
 
1108
 
1109
  RE-692–701 batch complete: [original models and Rust asset foundation](research/ORIGINAL_MODELS_RUST_BATCH.md). RE-702 tracks Bevy integration, native parity and downstream contact/damage; playable macOS/Linux Solari gameplay remains open.
 
 
 
 
1107
 
1108
 
1109
  RE-692–701 batch complete: [original models and Rust asset foundation](research/ORIGINAL_MODELS_RUST_BATCH.md). RE-702 tracks Bevy integration, native parity and downstream contact/damage; playable macOS/Linux Solari gameplay remains open.
1110
+
1111
+
1112
+ Research progress: RE-703 [report](research/QVM_DAMAGE_PROTECTION.md); remaining native/content/runtime/platform scope stays open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -1152,3 +1152,8 @@ Original-asset runs retain measurable source-point differences from conventional
1152
  ## 2026-10-21 — RE-701 Rust original-byte MD3 asset core and cross-language validation (separate task commit)
1153
 
1154
  A dependency-free Rust asset crate parses all619 originals with Python parity for headers, surfaces, sampled packed geometry, frame/tag float bits, UVs, triangles and shaders. Ten offline Rust unit tests plus four Python corpus-parity tests cover all619 originals and malformed CLI inputs; build/CLI execute locally on macOS. Full suite: 1259 tests pass. Report regenerates byte-identically (1812358 bytes). Storage/filter, links and whitespace checks pass. No original assets/shared VM evaluator changed; the safe dependency-free Rust asset crate and offline CLI are introduced; native/reference/legal/full platform scope remains open.
 
 
 
 
 
 
1152
  ## 2026-10-21 — RE-701 Rust original-byte MD3 asset core and cross-language validation (separate task commit)
1153
 
1154
  A dependency-free Rust asset crate parses all619 originals with Python parity for headers, surfaces, sampled packed geometry, frame/tag float bits, UVs, triangles and shaders. Ten offline Rust unit tests plus four Python corpus-parity tests cover all619 originals and malformed CLI inputs; build/CLI execute locally on macOS. Full suite: 1259 tests pass. Report regenerates byte-identically (1812358 bytes). Storage/filter, links and whitespace checks pass. No original assets/shared VM evaluator changed; the safe dependency-free Rust asset crate and offline CLI are introduced; native/reference/legal/full platform scope remains open.
1155
+
1156
+
1157
+ ## 2026-10-21 — RE-703 Damage protection and armor location selection (separate task commit)
1158
+
1159
+ 289594 rejects mode9 and strictly active spawn protection before location output; actual32155/32117 reclassifies head/torso using live inventory low bytes and health, excluding slot16. 356 gate/inventory fixtures and independent boundary/notification/location oracles pass. Full suite: 1263 tests pass. Report regenerates byte-identically (653493 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
@@ -1150,3 +1150,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1150
 
1151
 
1152
  - [ ] RE-702 Integrate the verified Rust MD3 core with Bevy, port original transform/geometry contracts, recover native loader/reference behavior and complete contact/damage/shared-pellet callers toward a playable macOS/Linux Solari slice. See the [next ten tasks](research/ORIGINAL_MODELS_RUST_BATCH.md#remaining-limits-and-next-ten-tasks).
 
 
 
 
1150
 
1151
 
1152
  - [ ] RE-702 Integrate the verified Rust MD3 core with Bevy, port original transform/geometry contracts, recover native loader/reference behavior and complete contact/damage/shared-pellet callers toward a playable macOS/Linux Solari slice. See the [next ten tasks](research/ORIGINAL_MODELS_RUST_BATCH.md#remaining-limits-and-next-ten-tasks).
1153
+
1154
+
1155
+ - [x] RE-703 Damage protection and armor location selection ([report](research/QVM_DAMAGE_PROTECTION.md)).
docs/research/QVM_DAMAGE_PROTECTION.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Damage protection and armor location selection
2
+
3
+ RE-703. Reproduce with `python3 scripts/analyze_qvm_damage_protection.py`; check with `python3 scripts/test_qvm_damage_protection.py`. The [machine report](qvm-damage-protection.json) retains source identity and bounded fixtures.
4
+
5
+ The source range289595–289761 uses signed millisecond arithmetic: time minus client4140 must be at least protection_seconds×1000. Mode9 returns zero first; active protection captures278003(attacker,110,targetID). Accepted inputs copy context68 to the location output. Locations1/2 resolve to1 without item22 or2 with it;3/4 resolve to3 without item17 or4 with it. Actual inventory routines require positive client208 and search exactly16 slots using the low byte. Slot16 is excluded. Clock, client presence and inventory inputs are explicit fixtures. Notification effects and damage continuation remain open.
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
@@ -708,3 +708,6 @@ The extension collector adds five focused tests for native tail calls, command-r
708
 
709
 
710
  [RE-692–701: Original models and Rust asset foundation](ORIGINAL_MODELS_RUST_BATCH.md), with validation limits and the next ten tasks.
 
 
 
 
708
 
709
 
710
  [RE-692–701: Original models and Rust asset foundation](ORIGINAL_MODELS_RUST_BATCH.md), with validation limits and the next ten tasks.
711
+
712
+
713
+ [RE-703: Damage protection and armor location selection](QVM_DAMAGE_PROTECTION.md). Reproduce with `python3 scripts/analyze_qvm_damage_protection.py`; check with `python3 scripts/test_qvm_damage_protection.py`.
docs/research/qvm-damage-protection.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_damage_protection.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Damage-factor protection gates and actual inventory-based location selection."""
3
+ import itertools
4
+ from pathlib import Path
5
+ from qvm_research_support import source
6
+ from qvm_damage_support import seed,run
7
+ from analyze_qvm import write_json
8
+
9
+ def main():
10
+ q,raw,h,ins=source();rows=[]
11
+ cases=[dict(mode=mode,now=now,location=loc,vest_slot=slot if loc in (3,4) else -1,helmet_slot=slot if loc in (1,2) else -1) for mode,now,loc,slot in itertools.product([3,9],[3999,4000,4001],range(1,15),[-1,0,15,16])]
12
+ cases += [dict(location=loc,client=client,health=health,vest_slot=0,helmet_slot=15) for loc,client,health in itertools.product([1,2,3,4,14],[False,True],[0,100])]
13
+ for a in cases:
14
+ _,p=run(ins,seed(raw,h,**a),289595,289762);p['input']=a;rows.append(p)
15
+ write_json(Path('docs/research/qvm-damage-protection.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Actual289595..289761,32155,32117. Notification278003 is captured without its effects; clock/client/inventory fixtures supplied. No health damage.'})
16
+ if __name__=='__main__':main()
scripts/qvm_damage_support.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Explicit bounded fixtures for damage-factor289594 research; no engine model."""
2
+ from qvm_research_support import constants
3
+ from qvm_trace_support import resume,captured
4
+ from analyze_qvm_interaction_request import inventory_check
5
+ from analyze_qvm_acceleration import word
6
+ L=0x79d00000
7
+ ATTACKER=L+4096
8
+ TARGET=L+8192
9
+ CLIENT=L+12288
10
+ CONTEXT=L+16384
11
+ LOCATION=L+20480
12
+ EXTRA=L+24576
13
+
14
+ def seed(raw,h,**x):
15
+ m=constants(raw,h,list(range(4068,4068+432*34,4))+list(range(32796,32856,4)))
16
+ m.update({L+o:0 for o in range(0,120,4)})
17
+ m.update({CLIENT+o:0 for o in range(0,6312,4)})
18
+ m.update({L+92:ATTACKER,L+96:TARGET,L+100:x.get('weapon',3),L+104:CONTEXT,L+108:LOCATION,L+112:EXTRA,LOCATION:x.get('location',3),EXTRA:word(x.get('extra',0.25)),CONTEXT+68:x.get('location',3),CONTEXT+84:word(x.get('distance',0)),TARGET:65,TARGET+520:CLIENT if x.get('client',True) else 0,TARGET+552:x.get('entity_flags',0),TARGET+836:0,CLIENT+208:x.get('health',100),CLIENT+4140:x.get('spawn',1000),CLIENT+4312:word(x.get('stamina',0)),CLIENT+220:x.get('flags',8),CLIENT+192:x.get('location_flags',16),CLIENT+4300:99,CLIENT+4304:x.get('counter',0),CLIENT+4316:77,CLIENT+144:0,CLIENT+312:x.get('held',0),CLIENT+6304:123,34992532:x.get('now',5000),34074804:x.get('protection_seconds',3),34090788:x.get('mode',3),34066516:x.get('override',0),L+20:word(x.get('factor',0.4)),L+24:x.get('wound',1),L+28:x.get('protected',0),L+32:word(x.get('ratio',0)),L+36:word(1)})
19
+ for item,slot in [(17,x.get('vest_slot',-1)),(22,x.get('helmet_slot',-1))]:
20
+ if slot>=0:m[CLIENT+376+4*slot]=0xaa0000|item
21
+ return m
22
+
23
+ def run(ins,m,start,stop):
24
+ notices=[]
25
+ def inventory(i,m,a):
26
+ m,_,v,r=inventory_check(i,m,a,(0,0,0));return m,v,r
27
+ before=snapshot(m)
28
+ m,e=resume(ins,start,stop,m,L,handlers={32155:inventory,278003:captured(278003,notices)})
29
+ return m,{'initial':before,'final':snapshot(m),'notices':notices,'execution':e}
30
+
31
+ def snapshot(m):
32
+ return {'locals':{str(o):m.get(L+o,0)&0xffffffff for o in [20,24,28,32,36,40,44,48,52,56]},'location':m[LOCATION]&0xffffffff,'extra':m[EXTRA]&0xffffffff,'client':{str(o):m[CLIENT+o]&0xffffffff for o in [192,220,4300,4304,4312,4316,6304]},'attacker_pointer':m[TARGET+836]}
scripts/test_qvm_damage_protection.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json,unittest
2
+ from pathlib import Path
3
+ class DamageProtectionTests(unittest.TestCase):
4
+ @classmethod
5
+ def setUpClass(cls):cls.ps=json.loads(Path('docs/research/qvm-damage-protection.json').read_text())['probes']
6
+ def test_strict_clock_and_mode_gates(self):
7
+ for p in self.ps:
8
+ a=p['input'];protected=a.get('now',5000)-1000<3000;blocked=a.get('mode',3)==9 or protected
9
+ self.assertEqual(p['execution']['return'],0 if blocked else None)
10
+ self.assertEqual(p['execution']['stop'],289641 if a.get('mode',3)==9 else 289661 if protected else 289762)
11
+ def test_actual_low_byte_inventory_reclassification(self):
12
+ for p in self.ps:
13
+ a=p['input'];loc=a['location'];blocked=a.get('mode',3)==9 or a.get('now',5000)-1000<3000
14
+ if not blocked and a.get('client',True):
15
+ if loc in (1,2):loc=2 if a.get('health',100)>0 and 0<=a.get('helmet_slot',-1)<16 else 1
16
+ if loc in (3,4):loc=4 if a.get('health',100)>0 and 0<=a.get('vest_slot',-1)<16 else 3
17
+ self.assertEqual(p['final']['location'],loc)
18
+ def test_protection_notification_arguments(self):
19
+ from qvm_damage_support import ATTACKER
20
+ for p in self.ps:
21
+ a=p['input'];notify=a.get('mode',3)!=9 and a.get('now',5000)-1000<3000
22
+ self.assertEqual(len(p['notices']),int(notify))
23
+ if notify:self.assertEqual(p['notices'][0]['arguments'],{'8':ATTACKER,'12':110,'16':65})
24
+ def test_inventory_slot_sixteen_is_excluded(self):
25
+ rows=[p for p in self.ps if p['input'].get('vest_slot')==16 and p['input']['location']==4 and p['execution']['return'] is None]
26
+ self.assertTrue(rows);self.assertTrue(all(p['final']['location']==3 for p in rows))
27
+ if __name__=='__main__':unittest.main()