qvm: server firing handler chains
Browse files- docs/ASSET_PIPELINE_TODO.md +3 -0
- docs/FINDINGS.md +3 -0
- docs/PROVISIONAL_TODO.md +5 -0
- docs/RESEARCH_LEDGER.md +7 -0
- docs/REVERSE_ENGINEERING_TODO.md +7 -1
- docs/research/QVM_FIRING_BATCH.md +2 -0
- docs/research/QVM_SERVER_FIRE_BATCH.md +49 -0
- docs/research/QVM_SERVER_FIRE_CHAIN.md +11 -0
- docs/research/README.md +5 -0
- docs/research/qvm-server-fire-chain.json +0 -0
- scripts/analyze_qvm_server_fire_chain.py +71 -0
- scripts/test_qvm_server_fire_chain.py +57 -0
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
|
| 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()
|