nmarshall commited on
Commit
9ff198d
·
1 Parent(s): e2a6045

qvm: server firing handler chains

Browse files
docs/ASSET_PIPELINE_TODO.md CHANGED
@@ -487,3 +487,6 @@ Research progress: RE-600 [report](research/QVM_GRENADE_SETUP.md); remaining nat
487
 
488
 
489
  Research progress: RE-601 [report](research/QVM_BASE_FIRE_GATE.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
487
 
488
 
489
  Research progress: RE-601 [report](research/QVM_BASE_FIRE_GATE.md); remaining native/content/runtime/platform scope stays open.
490
+
491
+
492
+ Research progress: RE-602 [report](research/QVM_SERVER_FIRE_CHAIN.md); remaining native/content/runtime/platform scope stays open.
docs/FINDINGS.md CHANGED
@@ -845,3 +845,6 @@ Bounded qagame paths recover bomb-plant/inactivity correction requests, direct f
845
 
846
 
847
  **RE-601 — Base-dependent firing gate scan and distance contract:** 288903 requires mode8 and active!=1, scans type18 bases, then checks64-unit distance/ground and chooses nearest point with ties selecting the second; invalid base cardinalities are explicit stop boundaries. [Evidence](research/QVM_BASE_FIRE_GATE.md).
 
 
 
 
845
 
846
 
847
  **RE-601 — Base-dependent firing gate scan and distance contract:** 288903 requires mode8 and active!=1, scans type18 bases, then checks64-unit distance/ground and chooses nearest point with ties selecting the second; invalid base cardinalities are explicit stop boundaries. [Evidence](research/QVM_BASE_FIRE_GATE.md).
848
+
849
+
850
+ **RE-602 — Published firing event to bounded server handler chains:** Actual31 publication now chains through server reset/basis/muzzle/dispatch into selected setup; event-loop continuation resumes only recovered returns and stays suspended at opaque shot/allocation/handler boundaries. [Evidence](research/QVM_SERVER_FIRE_CHAIN.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -805,3 +805,8 @@ Research progress: RE-600 [report](research/QVM_GRENADE_SETUP.md); remaining nat
805
 
806
 
807
  Research progress: RE-601 [report](research/QVM_BASE_FIRE_GATE.md); remaining native/content/runtime/platform scope stays open.
 
 
 
 
 
 
805
 
806
 
807
  Research progress: RE-601 [report](research/QVM_BASE_FIRE_GATE.md); remaining native/content/runtime/platform scope stays open.
808
+
809
+ The [RE-593–602 server firing batch](research/QVM_SERVER_FIRE_BATCH.md) recovers event31 routing, server basis/muzzle/dispatch and selected temp-event/grenade/base setup. Its next ten tasks prioritize293732/295018, real entity allocation/query/linking and projectile/damage callbacks. Original reference, asset mapping and full Rust/Bevy/Solari macOS/Linux/native/rendering/network scope remain open.
810
+
811
+
812
+ Research progress: RE-602 [report](research/QVM_SERVER_FIRE_CHAIN.md); remaining native/content/runtime/platform scope stays open.
docs/RESEARCH_LEDGER.md CHANGED
@@ -695,3 +695,10 @@ Actual outer31 publication reaches server151634, which writes client4140=0 and43
695
  ## 2026-10-20 — RE-601 Base-dependent firing gate scan and distance contract (separate task commit)
696
 
697
  288903 requires mode8 and active!=1, scans type18 bases, then checks64-unit distance/ground and chooses nearest point with ties selecting the second; invalid base cardinalities are explicit stop boundaries. 36 probes independently check mode/active priority,0/1/2/3 bases, exact65655 float distances and short-circuit calls,64 equality, airborne rejection and tie selection. Full suite: 915 tests pass. Report regenerates byte-identically (169227 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
 
 
 
 
 
 
 
 
695
  ## 2026-10-20 — RE-601 Base-dependent firing gate scan and distance contract (separate task commit)
696
 
697
  288903 requires mode8 and active!=1, scans type18 bases, then checks64-unit distance/ground and chooses nearest point with ties selecting the second; invalid base cardinalities are explicit stop boundaries. 36 probes independently check mode/active priority,0/1/2/3 bases, exact65655 float distances and short-circuit calls,64 equality, airborne rejection and tie selection. Full suite: 915 tests pass. Report regenerates byte-identically (169227 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
698
+
699
+
700
+ ## 2026-10-20 — RE-602 Published firing event to bounded server handler chains (separate task commit)
701
+
702
+ Actual31 publication now chains through server reset/basis/muzzle/dispatch into selected setup; event-loop continuation resumes only recovered returns and stays suspended at opaque shot/allocation/handler boundaries. 12 independent compositions verify all state/vector/record handoffs, queue re-entry and retained window, exact pending-call stacks and explicit engine-response contracts. Full suite: 919 tests pass. Report regenerates byte-identically (603514 bytes). Storage/filter, links and whitespace checks pass. No assets/Rust runtime/shared VM evaluator changed; native/reference/legal/full platform scope remains open.
703
+
704
+ RE-602 additional regression:19 related reports from RE-582–591 and RE-593–601 regenerate byte-identically. Storage/filter, documentation-link and whitespace checks pass; actual engine allocation/link/query effects and opaque firing/damage continuations remain open rather than supplied successful outcomes.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -816,7 +816,7 @@ Purpose: turn the supplied installation into an evidence-backed specification of
816
  - [x] RE-590 Complete bounded weapon body and actual main consumer suffix ([report](research/QVM_WEAPON_COMPLETE_BODY.md)).
817
 
818
 
819
- - [ ] RE-592 Recover event31 consumers and actual projectile/hitscan/damage effects; native vector snap and full53418/54475 step/scheduler composition; validate original legal-state traces, map numerical weapon/animation/audio IDs to assets, and continue Rust/Bevy/Solari macOS/Linux gameplay. See the [next ten tasks](research/QVM_FIRING_BATCH.md#boundaries-and-next-ten-tasks).
820
 
821
 
822
  - [x] RE-591 Timer-coupled firing sequences and actual outer publication ([report](research/QVM_FIRE_SEQUENCES.md)).
@@ -847,3 +847,9 @@ Purpose: turn the supplied installation into an evidence-backed specification of
847
 
848
 
849
  - [x] RE-601 Base-dependent firing gate scan and distance contract ([report](research/QVM_BASE_FIRE_GATE.md)).
 
 
 
 
 
 
 
816
  - [x] RE-590 Complete bounded weapon body and actual main consumer suffix ([report](research/QVM_WEAPON_COMPLETE_BODY.md)).
817
 
818
 
819
+ - [ ] RE-592 Partially closed by RE-593–602: event31 routing, server firing basis/muzzle/dispatch and selected bounded setup are recovered. Actual projectile/hitscan/damage, allocator/query/linking, native snap, full step/scheduler, original legal-state reference and Rust/Bevy/Solari macOS/Linux gameplay remain open. See [server firing batch](research/QVM_SERVER_FIRE_BATCH.md).
820
 
821
 
822
  - [x] RE-591 Timer-coupled firing sequences and actual outer publication ([report](research/QVM_FIRE_SEQUENCES.md)).
 
847
 
848
 
849
  - [x] RE-601 Base-dependent firing gate scan and distance contract ([report](research/QVM_BASE_FIRE_GATE.md)).
850
+
851
+
852
+ - [ ] RE-603 Recover293732/295018 shot processing,277165/277125 allocator,276433 query/native linking, grenade223156 and remaining dispatcher/base/event consumers. Validate real collision/damage/lifetime and original traces, complete step/scheduler and native snap, map asset identities and implement Rust/Bevy/Solari macOS/Linux gameplay. See the [next ten tasks](research/QVM_SERVER_FIRE_BATCH.md#next-ten-concrete-tasks).
853
+
854
+
855
+ - [x] RE-602 Published firing event to bounded server handler chains ([report](research/QVM_SERVER_FIRE_CHAIN.md)).
docs/research/QVM_FIRING_BATCH.md CHANGED
@@ -55,3 +55,5 @@ No original executable/native library, collision/physics or renderer runs. Anima
55
  8. Compose outer54475 scheduling with the recovered step and event publication; check timestamp boundaries, substep aggregation and queue pressure.
56
  9. Capture original/reference execution and compare legal multi-step behavior, then continue broader native/DLL/network/rendering/content coverage.
57
  10. Start the Rust/Bevy/Solari executable slice with recorded fixture parity and measured macOS/Linux platform/rendering paths; this research does not count as a playable game.
 
 
 
55
  8. Compose outer54475 scheduling with the recovered step and event publication; check timestamp boundaries, substep aggregation and queue pressure.
56
  9. Capture original/reference execution and compare legal multi-step behavior, then continue broader native/DLL/network/rendering/content coverage.
57
  10. Start the Rust/Bevy/Solari executable slice with recorded fixture parity and measured macOS/Linux platform/rendering paths; this research does not count as a playable game.
58
+
59
+ The subsequent [RE-593–602 server firing batch](QVM_SERVER_FIRE_BATCH.md) follows event31 into server preparation and selected bounded setup handlers. It retains explicit allocation/link/query and pending shot-processing boundaries rather than claiming native shot outcomes.
docs/research/QVM_SERVER_FIRE_BATCH.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Server firing research batch: RE-593–602
2
+
3
+ Ten separately committed tasks follow the [firing producer batch](QVM_FIRING_BATCH.md) into server-side event consumption, firing basis/muzzle preparation and selected handler setup. Recovered QVM effects are distinguished from supplied allocator/query replies, captured native requests and suspended opaque children.
4
+
5
+ | Task | Recovered scope | Evidence |
6
+ | --- | --- | --- |
7
+ | RE-593 |151254 two-slot event window and recognized-consumer routing | [Event route](QVM_SERVER_EVENT_ROUTE.md) |
8
+ | RE-594 | Actual31 publication→server prelude and pending289235 ABI | [Firing event](QVM_SERVER_FIRE_EVENT.md) |
9
+ | RE-595 |289235 suppression/statistic and finite angle basis before muzzle | [Basis](QVM_SERVER_FIRE_BASIS.md) |
10
+ | RE-596 | Complete288779/288871 muzzle helpers and actual firing caller | [Muzzle](QVM_SERVER_MUZZLE.md) |
11
+ | RE-597 | Entity weapon dispatch table and pending handler sequences | [Dispatcher](QVM_SERVER_FIRE_DISPATCH.md) |
12
+ | RE-598 |277695 tempEntity/278138 position setup with explicit engine boundaries | [Temporary event](QVM_TEMP_EVENT.md) |
13
+ | RE-599 |285840 event57, quantized direction and actual36717 PRNG before295018 | [Shot setup](QVM_SERVER_SHOT_SETUP.md) |
14
+ | RE-600 |285971 grenade descriptor, launch quantization and inherited velocity | [Grenade setup](QVM_GRENADE_SETUP.md) |
15
+ | RE-601 |288903 mode/base/range/ground gates before allocation | [Base gate](QVM_BASE_FIRE_GATE.md) |
16
+ | RE-602 | Published31→server→selected setup, with event-loop resume only on recovered returns | [Server chain](QVM_SERVER_FIRE_CHAIN.md) |
17
+
18
+ ## Findings for the Rust port
19
+
20
+ - The server consumes only the latest two events from an ordinary signed sequence window. Event31 first writes client4140=0 and4328=1, then calls289235(entity); its prelude does not read the event parameter.
21
+ - Client6164 suppresses the firing function before statistic or basis effects. Otherwise client4116 increments unless entity weaponID1, including IDs subsequently rejected by dispatch.
22
+ - Forward/right/up are derived from client angles with native sin/cos requests. Finite ordinary-math float32 responses model those natives here; platform libm/reference bit parity is unverified.
23
+ - The muzzle uses entity+24 plus viewheight and14×forward. It does not use entity+488 or PS origin, normalize the vector, trace or snap. The alternate helper omits14×forward.
24
+ - Dispatch uses entity+192, independent of the selected packed slot. The initialized table has direct returns for12/18, an18-ID shared293732 handler and distinct handlers for the remaining groups. This is not proof that IDs12/18 never fire elsewhere.
25
+ - tempEntity construction truncates coordinates before its actual position setter, issues a link request, then returns the allocation pointer. The selected285840 caller patches quantized direction, seed, shooter and weapon fields after that link request.
26
+ - Actual36717 mutates a32-bit seed using xor shifts5/21/27 and returns low15 bits;285840 stores low8 before pending295018 shot processing.
27
+ - The file-named grenade constructor biases forwardZ by.2, normalizes, scales by a mode speed, truncates, then adds owner velocity and matching-ground velocity. Its descriptor is constructed, not simulated through physics/explosion/damage.
28
+ - The base-dependent handler requires mode8/active!=1 and two type18 bases. Both farther than64 or ground1023 reject; otherwise the nearer point is chosen, with ties selecting the second. Invalid cardinalities stop at the source assumptions/error boundary rather than inventing valid pointers.
29
+
30
+ ## Integration and limits
31
+
32
+ RE-602 has twelve source-order chains. It resumes the actual event-loop continuation after suppressed firing, table returns, the bounded grenade constructor and recovered base-handler returns. It stays suspended at295018, base allocation277165 or other opaque handlers. Three publications demonstrate the two-event retained window and actual re-entry for returning paths. Full record, vector, queue and field handoffs have independent oracles.
33
+
34
+ Allocation277165 and world-query276433 replies are explicitly supplied; native link−31 is captured without engine effects. These contracts permit finite QVM data-flow checks, not a claim that an entity was actually allocated, linked, collided or damaged. A complete bounded constructor return is not a successful gameplay action. Other native/game callbacks and legal input domains remain open.
35
+
36
+ The879-test baseline gains40 independent checks. Reports regenerate deterministically and stay below the2MiB normal-Git cap. No original assets enter these commits; normal code/docs and filtered assets remain separated. No original executable/native library, full VM, native physics/rendering or macOS/Linux Rust runtime executes.
37
+
38
+ ## Next ten concrete tasks
39
+
40
+ 1. Partition shared handler293732 by call/return boundaries; decode its weapon-slot/configuration inputs and every unresolved child.
41
+ 2. Recover295018's direction/seed/spread setup and first trace requests, retaining exact finite arithmetic and table provenance.
42
+ 3. Follow reached trace/contact/material consumers into damage211239 only after their actual contracts are recovered; do not assume a hit from a supplied trace.
43
+ 4. Recover allocator277165 and record initializer277125, including free-slot reuse, timing, capacity and failure/error paths.
44
+ 5. Recover query276433 and native link−31 contracts so grenade/base construction can be tested against an actual entity list/world.
45
+ 6. Recover grenade callback223156, trajectory integration, contact/bounce, expiry and reached explosion/damage paths.
46
+ 7. Continue288903 after allocator289113 through its temp_bomb descriptor, linking, state and round callbacks, preserving the two-base assumptions.
47
+ 8. Recover remaining dispatcher handlers298842/293732/294171/286473/287330 and event consumers21/12/14/15/49/50/87, with explicit asset/animation/audio mappings.
48
+ 9. Complete native vector snap and movement/scheduler composition, then capture original reference execution and legal multi-step event/weapon outcomes.
49
+ 10. Continue the native/DLL/network/rendering/content inventory and first Rust/Bevy/Solari gameplay slice on macOS/Linux with measured parity. Bounded server data flow is not whole-game completion.
docs/research/QVM_SERVER_FIRE_CHAIN.md ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Published firing event to bounded server handler chains
2
+
3
+ RE-602. Reproduce with `python3 scripts/analyze_qvm_server_fire_chain.py`; check with `python3 scripts/test_qvm_server_fire_chain.py`. The [machine report](qvm-server-fire-chain.json) retains source identity and bounded fixtures.
4
+
5
+ The staged source bridge publishes31 with actual29913, routes via151254, applies the two-field prelude, then evaluates289235 preparation and dispatch. Suppression and table-return paths reach the actual firing return. Selected285971 executes its bounded record constructor with explicit allocation/query responses;288903 resumes its caller only when its recovered gate actually returns. Actual event continuation151658 runs only after those source returns, retaining cursor/client locals across re-entry.
6
+
7
+ 285840 stops before295018; a valid base action stops before277165; other handlers remain opaque at their dispatch call sites. Their parents are not resumed and no successful shot/round outcome is supplied.12 scenarios include three-publication ring pressure, IDs1/4/7/12/16/18/22/27/28, suppression, mode/range rejection and pending allocation. Independent oracles reconstruct basis/muzzle, temp-event/PRNG record and grenade descriptor, alongside exact queue/statistic/reset handoffs.
8
+
9
+ Source CALL/return boundary bridges are explicit, not a full VM implementation. Allocator/query replies and finite native trig/sqrt responses are supplied; native link requests are captured without engine effects. No original collision, damage, entity lifetime, whole tick or platform gameplay parity is claimed. See the [ten-task server batch](QVM_SERVER_FIRE_BATCH.md).
10
+
11
+ 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
@@ -406,3 +406,8 @@ The extension collector adds five focused tests for native tail calls, command-r
406
 
407
 
408
  [RE-601: Base-dependent firing gate scan and distance contract](QVM_BASE_FIRE_GATE.md). Reproduce with `python3 scripts/analyze_qvm_base_fire_gate.py`; check with `python3 scripts/test_qvm_base_fire_gate.py`.
 
 
 
 
 
 
406
 
407
 
408
  [RE-601: Base-dependent firing gate scan and distance contract](QVM_BASE_FIRE_GATE.md). Reproduce with `python3 scripts/analyze_qvm_base_fire_gate.py`; check with `python3 scripts/test_qvm_base_fire_gate.py`.
409
+
410
+ [RE-593–602 server firing batch and next ten tasks](QVM_SERVER_FIRE_BATCH.md): source-ordered event31 consumers, selected handler setup and explicit engine/opaque-call boundaries.
411
+
412
+
413
+ [RE-602: Published firing event to bounded server handler chains](QVM_SERVER_FIRE_CHAIN.md). Reproduce with `python3 scripts/analyze_qvm_server_fire_chain.py`; check with `python3 scripts/test_qvm_server_fire_chain.py`.
docs/research/qvm-server-fire-chain.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_server_fire_chain.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Published event31 through server preparation and explicitly bounded handlers."""
3
+ from pathlib import Path
4
+ from qvm_research_support import source,execute
5
+ from analyze_deferred_auth import trace_slice
6
+ from analyze_bsp_references import trace_fields
7
+ from qvm_server_fire_support import seed,state,ENTITY,BASES
8
+ from analyze_qvm_server_event_route import route,LOCAL as EVENT_LOCAL,TARGETS
9
+ from analyze_qvm_server_fire_event import prelude
10
+ from analyze_qvm_server_fire_dispatch import prepared
11
+ from analyze_qvm_server_fire_basis import LOCAL as FIRE_LOCAL
12
+ from analyze_qvm_server_shot_setup import shot_setup
13
+ from analyze_qvm_grenade_setup import grenade
14
+ from analyze_qvm_base_fire_gate import base_gate,setup
15
+ from analyze_qvm_temp_event import record
16
+ from analyze_qvm_fire_sequences import publish
17
+ from analyze_qvm_acceleration import PS,FRAME,word
18
+ from analyze_qvm import write_json
19
+
20
+ class PendingChild(Exception):
21
+ def __init__(self,m,entry,report,pending):self.memory=m;self.entry=entry;self.report=report;self.pending=pending
22
+
23
+ def server_path(ins,m):
24
+ m,p=prepared(ins,m);d=p['dispatch'];p['selected_child']=None;p['return_execution']=None;p['pending']=None;p['returned']=d is None or d['returned']
25
+ if d and not d['returned']:
26
+ entry=d['pending_call_sequence'][0]['target'];site=d['pending_call_sequence'][0]['instruction']
27
+ def child(i,m,a,c):
28
+ assert a[8]==ENTITY
29
+ if entry==285840:
30
+ m,r=shot_setup(i,m);raise PendingChild(m,entry,r,r['pending_call'])
31
+ if entry==285971:m,r=grenade(i,m);return m,c,0,r
32
+ m,r=base_gate(i,m)
33
+ if r['stop']!=289233:
34
+ pending={289111:{'instruction':289113,'target':277165},288942:{'instruction':288945,'target':2319}}.get(r['stop'],{'next_instruction':r['stop'],'reason':'base pointers not initialized by this source path'})
35
+ raise PendingChild(m,entry,r,pending)
36
+ return m,c,0,r
37
+ if entry in [285840,285971,288903]:
38
+ try:
39
+ m,c,r=execute(ins,d['execution']['stop'],289592,m,(0,0,0),FIRE_LOCAL,{entry:child});p['return_execution']=r;p['selected_child']={'entry':entry,'report':r['children'][0]['effect']};p['returned']=True
40
+ except PendingChild as e:m=e.memory;p['selected_child']={'entry':e.entry,'report':e.report};p['pending']={'server_call_instruction':site,'child_entry':entry,'nested_boundary':e.pending}
41
+ else:p['pending']={'server_call_instruction':site,'child_entry':entry,'reason':'unrecovered handler body'}
42
+ p['final']=state(m);return m,p
43
+
44
+ def chain(ins,m,count=1,parameter=0):
45
+ initial=state(m);old=m[PS+108];publications=[]
46
+ for _ in range(count):
47
+ m.update({FRAME+48:0,FRAME+252:0,FRAME+256:0,FRAME+260:1,FRAME+264:parameter});m,r=publish(ins,m);publications.append({'calls':r['execution']['calls'],'queue':r['final']['queue']})
48
+ m,r=route(ins,m,old);routing=r;stop=r['execution']['stop'];attempts=[];completed=0
49
+ for _ in range(3):
50
+ if stop!=151634:break
51
+ cursor=m[EVENT_LOCAL+44];m,p=prelude(ins,m);m,s=server_path(ins,m);continuation=None
52
+ if s['returned']:
53
+ t=trace_slice(ins,151255,151952,m,{},resume_at=151658,stop_targets=TARGETS,local_base=EVENT_LOCAL,limit=4096);m=t['memory'];continuation={'stop':t['stop'],'calls':t['calls'],**trace_fields(t,False)};stop=continuation['stop'];completed+=1
54
+ attempts.append({'cursor':cursor,'prelude':p,'server':s,'event_continuation':continuation,'final':state(m)})
55
+ if not s['returned']:break
56
+ else:raise AssertionError('Two-slot event window exceeded')
57
+ return {'initial':initial,'publication_count':count,'parameter':parameter,'publications':publications,'route':routing,'attempts':attempts,'events_completed':completed,'complete':stop==151952,'server_parent_suspended':bool(attempts and not attempts[-1]['server']['returned']),'final':state(m),'constructed_record':record(m) if any(a['server']['selected_child'] and a['server']['selected_child']['entry'] in [285840,285971] for a in attempts) else None}
58
+
59
+ def main():
60
+ q,raw,h,ins=source();rows=[]
61
+ cases=[('suppressed',22,3,{PS+6164:1}),('table-return12',12,3,{}),('table-return18',18,1,{}),('invalid-id',28,1,{}),('event57-pending-shot',4,1,{}),('grenade-record-return',7,1,{}),('base-allocation-pending',16,1,{}),('base-mode-return',16,1,{34090788:9}),('base-distance-return',16,1,{}),('id1-handler-pending',1,1,{}),('shared-handler-pending',22,1,{}),('handler27-pending',27,1,{})]
62
+ for name,ident,count,changes in cases:
63
+ m=seed(raw,h,ident);m.update({21044:0x12345678,PS+68:8,PS+44:99,PS+148:3});m.update(changes)
64
+ for o in range(0,880,4):m.setdefault(BASES+o,0)
65
+ for j,v in enumerate((30,60,15)):m[PS+152+4*j]=word(v)
66
+ if ident==16:
67
+ points=[(1000,0,0),(1000,1,0)] if name=='base-distance-return' else [(0,0,0),(1,0,0)];setup(m,[18,18],points)
68
+ for j in range(3):m[ENTITY+488+4*j]=word(0)
69
+ p=chain(ins,m,count,7);p['name']=name;rows.append(p)
70
+ write_json(Path('docs/research/qvm-server-fire-chain.json'),{'source':q['source'],'sha256':q['sha256'],'probes':rows,'limits':'Staged source event31→289235 basis/muzzle/dispatch→selected bounded setup. Parent event loop resumes only actual recovered QVM returns. Allocation/query replies supplied and native link effects captured;295018/other handlers remain suspended. No original physics/damage or full tick/gameplay/reference claim.'})
71
+ if __name__=='__main__':main()
scripts/test_qvm_server_fire_chain.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json,unittest,copy
2
+ from pathlib import Path
3
+ from test_qvm_server_fire_basis import vectors
4
+ from test_qvm_server_muzzle import output
5
+ from test_qvm_grenade_setup import projectile,normalized
6
+ from test_qvm_base_fire_gate import expected as base_expected
7
+ from test_qvm_temp_event import event_record
8
+ from test_qvm_server_shot_setup import random_oracle
9
+ from test_qvm_ground import word,n,f
10
+
11
+ def shot_record(p):
12
+ r=event_record(p['execution']['children'][0]['effect']);_,random=random_oracle(p['initial_seed'])
13
+ for j,v in enumerate(p['initial']['forward']):r[str(104+4*j)]=word(float(int(f(n(v)*4096))))
14
+ r.update({'184':random&255,'140':p['initial']['entity_id'],'192':p['initial']['entity_weapon']});return r
15
+
16
+ def server_state(test,p):
17
+ b=p['basis'];a=copy.deepcopy(b['initial'])
18
+ if not a['blocked6164']:
19
+ if a['entity_weapon']!=1:a['stat4116']=(a['stat4116']+1)&0xffffffff
20
+ a['forward'],a['right'],a['up']=vectors(a['angles'])
21
+ test.assertEqual(b['final'],a)
22
+ if p['muzzle_execution']:
23
+ child=p['muzzle_execution']['children'][0]['effect'];test.assertEqual(child['initial'],a);a['muzzle']=output(child);test.assertEqual(child['final'],a);test.assertEqual(p['dispatch']['initial'],a)
24
+ if p['selected_child']:
25
+ entry,r=p['selected_child']['entry'],p['selected_child']['report'];test.assertEqual(r['initial'],a)
26
+ if entry==285840:test.assertEqual(r['final_record'],shot_record(r))
27
+ elif entry==285971:test.assertEqual(r['final_record'],projectile(r));a['forward']=list(map(word,normalized(r)))
28
+ else:a,point,status,stop=base_expected(r);test.assertEqual((r['selected_point_after'],r['status'],r['stop']),(point,status,stop))
29
+ test.assertEqual(r['final'],a)
30
+ test.assertEqual(p['final'],a);return a
31
+ class ServerFireChainTests(unittest.TestCase):
32
+ @classmethod
33
+ def setUpClass(cls):cls.r=json.loads(Path('docs/research/qvm-server-fire-chain.json').read_text());cls.rows={p['name']:p for p in cls.r['probes']}
34
+ def test_actual_event_preparation_and_selected_child_handoffs(self):
35
+ self.assertEqual(len(self.r['probes']),12)
36
+ for p in self.r['probes']:
37
+ a=copy.deepcopy(p['route']['final'])
38
+ for attempt in p['attempts']:
39
+ q=attempt['prelude'];self.assertEqual(q['initial'],a);a.update(field4140=0,field4328=1);self.assertEqual(q['final'],a);self.assertEqual(attempt['server']['basis']['initial'],a);a=server_state(self,attempt['server']);self.assertEqual(attempt['final'],a)
40
+ self.assertEqual(p['final'],a);self.assertEqual(a['queue'],p['publications'][-1]['queue'])
41
+ def test_only_recovered_returns_resume_event_loop(self):
42
+ complete={'suppressed','table-return12','table-return18','invalid-id','grenade-record-return','base-mode-return','base-distance-return'}
43
+ for p in self.r['probes']:
44
+ self.assertEqual(p['complete'],p['name'] in complete);self.assertEqual(p['server_parent_suspended'],p['name'] not in complete);self.assertEqual(p['events_completed'],sum(bool(a['server']['returned']) for a in p['attempts']))
45
+ for a in p['attempts']:self.assertEqual(a['event_continuation'] is not None,a['server']['returned'])
46
+ def test_nested_pending_boundaries_not_reported_as_shot_outcomes(self):
47
+ expected={'event57-pending-shot':(285840,295018),'base-allocation-pending':(288903,277165),'id1-handler-pending':(298842,None),'shared-handler-pending':(293732,None),'handler27-pending':(294171,None)}
48
+ for name,(entry,target) in expected.items():
49
+ p=self.rows[name];pending=p['attempts'][-1]['server']['pending'];self.assertEqual(pending['child_entry'],entry);self.assertEqual(p['events_completed'],0)
50
+ if target:self.assertEqual(pending['nested_boundary']['target'],target)
51
+ def test_queue_window_reentry_and_complete_record_evidence(self):
52
+ for name in ['suppressed','table-return12']:
53
+ p=self.rows[name];self.assertEqual(p['publication_count'],3);self.assertEqual([a['cursor'] for a in p['attempts']],[1,2]);self.assertEqual(p['events_completed'],2)
54
+ self.assertEqual(self.rows['table-return12']['final']['stat4116'],12);self.assertEqual(self.rows['suppressed']['final']['stat4116'],10);self.assertEqual(self.rows['id1-handler-pending']['final']['stat4116'],10)
55
+ for name in ['event57-pending-shot','grenade-record-return']:
56
+ p=self.rows[name];self.assertEqual(p['constructed_record'],p['attempts'][0]['server']['selected_child']['report']['final_record'])
57
+ if __name__=='__main__':unittest.main()