nmarshall commited on
Commit
2b1ac7e
·
1 Parent(s): d3ab5c7

gameplay: integrate connect session lifecycle branch

Browse files
crates/urt-gameplay/src/bin/urt-native-policy.rs CHANGED
@@ -713,6 +713,66 @@ fn run() -> Result<String, String> {
713
  out.ring_advance
714
  ))
715
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716
  _ => Err("unknown command".into()),
717
  }
718
  }
 
713
  out.ring_advance
714
  ))
715
  }
716
+ "connect-session" => {
717
+ if w.len() != 6 {
718
+ return Err("invalid connect header".into());
719
+ }
720
+ let mut client = vec![0xdd; CLIENT_BYTES];
721
+ let mut entity = ConnectEntity {
722
+ linked_slot: None,
723
+ field764: 0xaabbccdd,
724
+ field768: 0xaabbccdd,
725
+ };
726
+ let mut selector = 0;
727
+ let out = connect_session(
728
+ &mut client,
729
+ &mut entity,
730
+ ConnectInput {
731
+ first_time: w[1] as i32,
732
+ new_session: w[2] as i32,
733
+ flags: 1,
734
+ initialize: InitializeInput {
735
+ mode: w[0] as i32,
736
+ slot: w[4] as usize,
737
+ userinfo: b"\\team\\S\\cl_guid\\ID",
738
+ limit: 2,
739
+ occupancy: 1,
740
+ seconds: 0,
741
+ clock: 100,
742
+ ticks: 17,
743
+ handle: w[5],
744
+ client_id: 999,
745
+ enabled: 1,
746
+ mapname: b"ut4_test",
747
+ file_length: 11,
748
+ file_data: b"9,8,7,6,5,4",
749
+ },
750
+ session: b"1,-2,3,4,5,1,1,1,1",
751
+ ignore_text: b"1,3,",
752
+ scratch: [w[3]; 5],
753
+ },
754
+ &mut selector,
755
+ )
756
+ .map_err(|e| format!("{e:?}"))?;
757
+ let nonzero = client
758
+ .chunks_exact(4)
759
+ .enumerate()
760
+ .filter_map(|(j, b)| {
761
+ let v = u32::from_le_bytes(b.try_into().unwrap());
762
+ (v != 0).then_some(vec![4 * j as u32, v])
763
+ })
764
+ .collect::<Vec<_>>();
765
+ Ok(format!(
766
+ "{{\"raw\":\"{}\",\"nonzero\":{},\"choice\":[{}],\"entity_linked\":{},\"entity_clear\":[{},{}],\"ring\":{},\"info_ring\":{selector}}}",
767
+ hex(&client[3096..3096 + SESSION_BYTES]),
768
+ nested(&nonzero),
769
+ if out.fresh { 247520 } else { 247219 },
770
+ entity.linked_slot == Some(w[4] as usize),
771
+ entity.field764,
772
+ entity.field768,
773
+ out.ring_advance
774
+ ))
775
+ }
776
  _ => Err("unknown command".into()),
777
  }
778
  }
crates/urt-gameplay/src/native_policy.rs CHANGED
@@ -1894,3 +1894,201 @@ mod memory_persistence_tests {
1894
  assert_eq!(selector, 0);
1895
  }
1896
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1894
  assert_eq!(selector, 0);
1895
  }
1896
  }
1897
+
1898
+ pub const CLIENT_BYTES: usize = 6336;
1899
+ #[derive(Clone, Debug, PartialEq, Eq)]
1900
+ pub struct ConnectEntity {
1901
+ pub linked_slot: Option<usize>,
1902
+ pub field764: u32,
1903
+ pub field768: u32,
1904
+ }
1905
+ pub struct ConnectInput<'a> {
1906
+ pub first_time: i32,
1907
+ pub new_session: i32,
1908
+ pub flags: u32,
1909
+ pub initialize: InitializeInput<'a>,
1910
+ pub session: &'a [u8],
1911
+ pub ignore_text: &'a [u8],
1912
+ pub scratch: [u32; 5],
1913
+ }
1914
+ #[derive(Clone, Debug, PartialEq, Eq)]
1915
+ pub struct ConnectedSession {
1916
+ pub fresh: bool,
1917
+ pub initialized: Option<InitializedSession>,
1918
+ pub restored: Option<RestoredSession>,
1919
+ pub ring_advance: u32,
1920
+ }
1921
+ /// Bounded173979 caller path after authentication and before later team
1922
+ /// normalization. Clearing client memory does not initialize caller scratch.
1923
+ pub fn connect_session(
1924
+ client: &mut [u8],
1925
+ entity: &mut ConnectEntity,
1926
+ input: ConnectInput<'_>,
1927
+ info_ring: &mut u32,
1928
+ ) -> Result<ConnectedSession, Error> {
1929
+ let mut cfg = input.initialize;
1930
+ let slot = cfg.slot;
1931
+ if client.len() != CLIENT_BYTES || slot >= 64 {
1932
+ return Err(Error::InvalidCount);
1933
+ }
1934
+ let mut bytes = vec![0; CLIENT_BYTES];
1935
+ let mut selector = *info_ring;
1936
+ raw_store(&mut bytes, 468, 1);
1937
+ raw_store(&mut bytes, 6156, slot as u32);
1938
+ cfg.client_id = 0;
1939
+ let fresh = input.first_time != 0 || input.new_session != 0;
1940
+ let mut initialized = None;
1941
+ let mut restored = None;
1942
+ let ring_advance;
1943
+ if fresh {
1944
+ let out = initialize_session(&mut bytes[3096..3096 + SESSION_BYTES], cfg, &mut selector)?;
1945
+ ring_advance = out.ring_advance;
1946
+ initialized = Some(out);
1947
+ } else {
1948
+ let mut state = RestoreState {
1949
+ values: [0; 9],
1950
+ ignore: [0; 64],
1951
+ position: [0; 6],
1952
+ };
1953
+ let out = restore_session(
1954
+ &mut state,
1955
+ RestoreInput {
1956
+ flags: input.flags,
1957
+ session: input.session,
1958
+ ignore_text: input.ignore_text,
1959
+ scratch: input.scratch,
1960
+ position: PositionInput {
1961
+ mode: cfg.mode,
1962
+ enabled: cfg.enabled,
1963
+ slot,
1964
+ userinfo: cfg.userinfo,
1965
+ mapname: cfg.mapname,
1966
+ name: b"",
1967
+ values: [0; 6],
1968
+ handle: cfg.handle,
1969
+ },
1970
+ file_length: cfg.file_length,
1971
+ file_data: cfg.file_data,
1972
+ },
1973
+ &mut selector,
1974
+ )?;
1975
+ for (j, o) in [0, 60, 48, 44, 52, 56, 612, 616, 644]
1976
+ .into_iter()
1977
+ .enumerate()
1978
+ {
1979
+ raw_store(&mut bytes, 3096 + o, state.values[j] as u32);
1980
+ }
1981
+ for (j, v) in state.ignore.iter().enumerate() {
1982
+ raw_store(&mut bytes, 3780 + 4 * j, *v as u32);
1983
+ }
1984
+ for (j, v) in state.position.iter().enumerate() {
1985
+ raw_store(&mut bytes, 3716 + 4 * j, *v);
1986
+ }
1987
+ ring_advance = out.ring_advance;
1988
+ restored = Some(out);
1989
+ }
1990
+ client.copy_from_slice(&bytes);
1991
+ *entity = ConnectEntity {
1992
+ linked_slot: Some(slot),
1993
+ field764: 0,
1994
+ field768: 0,
1995
+ };
1996
+ *info_ring = selector;
1997
+ Ok(ConnectedSession {
1998
+ fresh,
1999
+ initialized,
2000
+ restored,
2001
+ ring_advance,
2002
+ })
2003
+ }
2004
+ #[cfg(test)]
2005
+ mod connect_tests {
2006
+ use super::*;
2007
+ #[test]
2008
+ fn rejected_client_preserves_entity_and_selector() {
2009
+ let mut client = [];
2010
+ let mut entity = ConnectEntity {
2011
+ linked_slot: None,
2012
+ field764: 7,
2013
+ field768: 8,
2014
+ };
2015
+ let before = entity.clone();
2016
+ let mut selector = 1;
2017
+ let out = connect_session(
2018
+ &mut client,
2019
+ &mut entity,
2020
+ ConnectInput {
2021
+ first_time: 1,
2022
+ new_session: 0,
2023
+ flags: 0,
2024
+ initialize: InitializeInput {
2025
+ mode: 0,
2026
+ slot: 0,
2027
+ userinfo: b"",
2028
+ limit: 0,
2029
+ occupancy: 0,
2030
+ seconds: 0,
2031
+ clock: 0,
2032
+ ticks: 0,
2033
+ handle: 0,
2034
+ client_id: 0,
2035
+ enabled: 1,
2036
+ mapname: b"m",
2037
+ file_length: 0,
2038
+ file_data: b"",
2039
+ },
2040
+ session: b"",
2041
+ ignore_text: b"",
2042
+ scratch: [0; 5],
2043
+ },
2044
+ &mut selector,
2045
+ );
2046
+ assert_eq!(out, Err(Error::InvalidCount));
2047
+ assert_eq!(entity, before);
2048
+ assert_eq!(selector, 1);
2049
+ }
2050
+ #[test]
2051
+ fn clear_client_does_not_zero_supplied_scratch() {
2052
+ let mut client = vec![0xdd; CLIENT_BYTES];
2053
+ let mut entity = ConnectEntity {
2054
+ linked_slot: None,
2055
+ field764: 7,
2056
+ field768: 8,
2057
+ };
2058
+ let mut selector = 0;
2059
+ let out = connect_session(
2060
+ &mut client,
2061
+ &mut entity,
2062
+ ConnectInput {
2063
+ first_time: 0,
2064
+ new_session: 0,
2065
+ flags: 1,
2066
+ initialize: InitializeInput {
2067
+ mode: 3,
2068
+ slot: 0,
2069
+ userinfo: b"",
2070
+ limit: 0,
2071
+ occupancy: 0,
2072
+ seconds: 0,
2073
+ clock: 0,
2074
+ ticks: 0,
2075
+ handle: 0,
2076
+ client_id: 0,
2077
+ enabled: 1,
2078
+ mapname: b"m",
2079
+ file_length: 0,
2080
+ file_data: b"",
2081
+ },
2082
+ session: b"1,-2,3",
2083
+ ignore_text: b"",
2084
+ scratch: [1; 5],
2085
+ },
2086
+ &mut selector,
2087
+ )
2088
+ .unwrap();
2089
+ assert!(!out.fresh);
2090
+ assert_eq!(raw_word(&client, 3152), 1);
2091
+ assert_eq!(raw_word(&client, 468), 1);
2092
+ assert_eq!(client[6332..], [0; 4]);
2093
+ }
2094
+ }
docs/FINDINGS.md CHANGED
@@ -1736,3 +1736,6 @@ RE-813–822 replaces the scoreboard decimal assumption with actual37914 executi
1736
 
1737
 
1738
  **RE-912:** 96 bounded173979 caller-path executions cover174081..174173 after userinfo/authentication: entity-client linking, actual6336-byte clear via modeled native memset, connecting-state/client-slot writes and the first-time OR new-session choice between actual247520 initialization and247219 restore. Both children execute recovered load/format/scanner policies. Uppercase team S below capacity is not the lowercase s shortcut. Authentication before the slice, post-branch normalization and native engine integration remain open. [Evidence](research/QVM_CONNECT_SESSION.md).
 
 
 
 
1736
 
1737
 
1738
  **RE-912:** 96 bounded173979 caller-path executions cover174081..174173 after userinfo/authentication: entity-client linking, actual6336-byte clear via modeled native memset, connecting-state/client-slot writes and the first-time OR new-session choice between actual247520 initialization and247219 restore. Both children execute recovered load/format/scanner policies. Uppercase team S below capacity is not the lowercase s shortcut. Authentication before the slice, post-branch normalization and native engine integration remain open. [Evidence](research/QVM_CONNECT_SESSION.md).
1739
+
1740
+
1741
+ **RE-913:** Rust matches96 bounded173979 caller paths, including client/entity initialization, first-time OR new-session dispatch, actual initialization/restore policies and saved-position loading. Client memory clear does not erase supplied caller scratch. Errors stage client/entity/selector changes. Authentication and later caller normalization, native IO and full client-connect acceptance remain open. [Evidence](research/RUST_CONNECT_SESSION.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1773,3 +1773,9 @@ RE-911: Rust matches144 original save/session-write/restore/read compositions us
1773
 
1774
 
1775
  RE-912: 96 bounded173979 caller-path executions cover174081..174173 after userinfo/authentication: entity-client linking, actual6336-byte clear via modeled native memset, connecting-state/client-slot writes and the first-time OR new-session choice between actual247520 initialization and247219 restore. Both children execute recovered load/format/scanner policies. Uppercase team S below capacity is not the lowercase s shortcut. Authentication before the slice, post-branch normalization and native engine integration remain open. Full runtime completion remains open. [Evidence](research/QVM_CONNECT_SESSION.md).
 
 
 
 
 
 
 
1773
 
1774
 
1775
  RE-912: 96 bounded173979 caller-path executions cover174081..174173 after userinfo/authentication: entity-client linking, actual6336-byte clear via modeled native memset, connecting-state/client-slot writes and the first-time OR new-session choice between actual247520 initialization and247219 restore. Both children execute recovered load/format/scanner policies. Uppercase team S below capacity is not the lowercase s shortcut. Authentication before the slice, post-branch normalization and native engine integration remain open. Full runtime completion remains open. [Evidence](research/QVM_CONNECT_SESSION.md).
1776
+
1777
+
1778
+ RE-913: Rust matches96 bounded173979 caller paths, including client/entity initialization, first-time OR new-session dispatch, actual initialization/restore policies and saved-position loading. Client memory clear does not erase supplied caller scratch. Errors stage client/entity/selector changes. Authentication and later caller normalization, native IO and full client-connect acceptance remain open. Full runtime completion remains open. [Evidence](research/RUST_CONNECT_SESSION.md).
1779
+
1780
+
1781
+ RE-904–913 completed bounded initialization, full persistence, in-memory round trips and connect clear/dispatch policies. RE-914 tracks full connect/native IO, remaining scanner/alias domains and real runtime integration. [Batch and next work](research/QVM_SESSION_LIFECYCLE_BATCH.md).
docs/RESEARCH_LEDGER.md CHANGED
@@ -2280,3 +2280,13 @@ Rust matches144 original save/session-write/restore/read compositions using owne
2280
  ## 2026-10-28 — RE-912 Original connect clear and session-branch caller
2281
 
2282
  96 bounded173979 caller-path executions cover174081..174173 after userinfo/authentication: entity-client linking, actual6336-byte clear via modeled native memset, connecting-state/client-slot writes and the first-time OR new-session choice between actual247520 initialization and247219 restore. Both children execute recovered load/format/scanner policies. Uppercase team S below capacity is not the lowercase s shortcut. Authentication before the slice, post-branch normalization and native engine integration remain open. Focused `cargo xtask test-group qvm-connect-session` passes (4 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
 
 
 
 
 
 
 
 
 
 
 
2280
  ## 2026-10-28 — RE-912 Original connect clear and session-branch caller
2281
 
2282
  96 bounded173979 caller-path executions cover174081..174173 after userinfo/authentication: entity-client linking, actual6336-byte clear via modeled native memset, connecting-state/client-slot writes and the first-time OR new-session choice between actual247520 initialization and247219 restore. Both children execute recovered load/format/scanner policies. Uppercase team S below capacity is not the lowercase s shortcut. Authentication before the slice, post-branch normalization and native engine integration remain open. Focused `cargo xtask test-group qvm-connect-session` passes (4 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
2283
+
2284
+
2285
+ ## 2026-10-28 — RE-913 Rust connect clear and session lifecycle branch
2286
+
2287
+ Rust matches96 bounded173979 caller paths, including client/entity initialization, first-time OR new-session dispatch, actual initialization/restore policies and saved-position loading. Client memory clear does not erase supplied caller scratch. Errors stage client/entity/selector changes. Authentication and later caller normalization, native IO and full client-connect acceptance remain open. Focused `cargo xtask test-group rust-connect-session` passes (9 Python tests); selected reports regenerate twice deterministically, with storage, documentation links and whitespace guards. Full suite deferred to task ten. Original assets unchanged; source fixtures do not establish complete native/runtime parity.
2288
+
2289
+
2290
+ ### RE-904–913 full batch validation (2026-10-28)
2291
+
2292
+ At the tenth-commit boundary, the first full attempt exposed a helper filename collision with the pre-existing health lifecycle module. The original helper was restored byte-for-byte, session helpers moved to qvm_session_lifecycle_support.py, and an import smoke check added to the scoped registry regression. After focused repair checks, `cargo xtask test-full --since 6d0ce63f` completed successfully with the flake-declared Nix Python: 1,768 Python tests,207 Rust tests (assets10, gameplay167, xtask30),39 affected report producers regenerated twice deterministically, storage policy,480 documentation link checks and whitespace guards. No generated-report drift. Ten task commits since6d0ce63f; repair and results are amended into the tenth commit. [Findings and next work](research/QVM_SESSION_LIFECYCLE_BATCH.md).
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -1782,7 +1782,7 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1782
  - [x] RE-903 Rust session restore with caller-scratch ownership ([evidence](research/RUST_SESSION_RESTORE.md)).
1783
 
1784
 
1785
- - [ ] RE-904 Complete numeric/fatal/alias domains, scratch provenance, real reload and native IO/runtime integration ([next tasks](research/QVM_SAVED_RESTORE_BATCH.md)).
1786
 
1787
 
1788
  - [x] RE-904 Original session initialization and immediate persistence ([evidence](research/QVM_SESSION_INIT.md)).
@@ -1810,3 +1810,9 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1810
 
1811
 
1812
  - [x] RE-912 Original connect clear and session-branch caller ([evidence](research/QVM_CONNECT_SESSION.md)).
 
 
 
 
 
 
 
1782
  - [x] RE-903 Rust session restore with caller-scratch ownership ([evidence](research/RUST_SESSION_RESTORE.md)).
1783
 
1784
 
1785
+ - [ ] Follow-up after RE-903: complete numeric/fatal/alias domains, scratch provenance, real reload and native IO/runtime integration ([next tasks](research/QVM_SAVED_RESTORE_BATCH.md)).
1786
 
1787
 
1788
  - [x] RE-904 Original session initialization and immediate persistence ([evidence](research/QVM_SESSION_INIT.md)).
 
1810
 
1811
 
1812
  - [x] RE-912 Original connect clear and session-branch caller ([evidence](research/QVM_CONNECT_SESSION.md)).
1813
+
1814
+
1815
+ - [x] RE-913 Rust connect clear and session lifecycle branch ([evidence](research/RUST_CONNECT_SESSION.md)).
1816
+
1817
+
1818
+ - [ ] RE-914 Complete connect authentication/normalization, native IO/reload, scanner/alias domains and full runtime integration ([next work](research/QVM_SESSION_LIFECYCLE_BATCH.md)).
docs/TESTING.md CHANGED
@@ -166,3 +166,6 @@ RE-884–893 adds focused groups `qvm-queried-spawn`, `rust-queried-spawn`, `qvm
166
 
167
 
168
  RE-894–903 adds focused groups `qvm-info-lookup`, `rust-info-lookup`, `qvm-numeric-scan`, `rust-numeric-scan`, `qvm-position-save`, `rust-position-save`, `qvm-position-load`, `rust-position-load`, `qvm-session-restore` and `rust-session-restore`. Use one group per invocation. Full validation after task ten uses `cargo xtask test-full --since 6e01995b` with explicit flake-declared Nix PYTHON. [Source domains and scratch ownership](research/QVM_SAVED_RESTORE_BATCH.md).
 
 
 
 
166
 
167
 
168
  RE-894–903 adds focused groups `qvm-info-lookup`, `rust-info-lookup`, `qvm-numeric-scan`, `rust-numeric-scan`, `qvm-position-save`, `rust-position-save`, `qvm-position-load`, `rust-position-load`, `qvm-session-restore` and `rust-session-restore`. Use one group per invocation. Full validation after task ten uses `cargo xtask test-full --since 6e01995b` with explicit flake-declared Nix PYTHON. [Source domains and scratch ownership](research/QVM_SAVED_RESTORE_BATCH.md).
169
+
170
+
171
+ RE-904–913 adds focused groups `qvm-session-init`, `rust-session-init`, `qvm-post-map-files`, `rust-post-map-files`, `qvm-normal-map-files`, `rust-normal-map-files`, `qvm-persistence-roundtrip`, `rust-persistence-roundtrip`, `qvm-connect-session` and `rust-connect-session`. Invoke one group per command. The full suite after task ten uses `cargo xtask test-full --since 6d0ce63f` with explicit flake-declared Nix PYTHON. [Bounded lifecycle evidence](research/QVM_SESSION_LIFECYCLE_BATCH.md).
docs/research/QVM_SAVED_RESTORE_BATCH.md CHANGED
@@ -43,7 +43,7 @@ Each task used focused `cargo xtask test-group <name>` before its commit, with d
43
 
44
  Dependency-selection regression coverage includes all five new source-to-Rust report pairs, native_policy consumers, formatting dependencies and intermission classification used by restore. Every Python process uses the resolved Nix runtime declared by flake.nix, with explicit PYTHON for xtask and Git hooks. No original asset payload changed. After commit ten, `cargo xtask test-full --since 6e01995b` passed once: 1,753 Python tests and199 Rust tests (assets10, gameplay159, xtask30). All22 affected report producers regenerated twice deterministically. Storage policy, links across469 documentation files and whitespace checks passed with no generated-report drift. These notes were amended into task ten.
45
 
46
- ## Next work: RE-904
47
 
48
  - [ ] Recover uppercase exponent behavior and remaining38355 conversions, non-ASCII parsing, oversized/fatal userinfo and pointer/buffer alias contracts.
49
  - [ ] Trace actual scratch provenance on malformed session restore; decide a documented production compatibility/default policy after validating native behavior.
@@ -53,3 +53,5 @@ Dependency-selection regression coverage includes all five new source-to-Rust re
53
  - [ ] Replace supplied spatial-query answers with recovered native broadphase, integrate bot rotation/native genetic behavior, and recover221662 map-list parsing.
54
  - [ ] Compose complete respawn/begin-intermission, event lifetime and ranking/scoreboard/network execution.
55
  - [ ] Validate actual Linux/macOS Rust/Bevy/Solari gameplay/rendering. Full-game completion remains open.
 
 
 
43
 
44
  Dependency-selection regression coverage includes all five new source-to-Rust report pairs, native_policy consumers, formatting dependencies and intermission classification used by restore. Every Python process uses the resolved Nix runtime declared by flake.nix, with explicit PYTHON for xtask and Git hooks. No original asset payload changed. After commit ten, `cargo xtask test-full --since 6e01995b` passed once: 1,753 Python tests and199 Rust tests (assets10, gameplay159, xtask30). All22 affected report producers regenerated twice deterministically. Storage policy, links across469 documentation files and whitespace checks passed with no generated-report drift. These notes were amended into task ten.
45
 
46
+ ## Follow-up work (expanded by RE-904–913)
47
 
48
  - [ ] Recover uppercase exponent behavior and remaining38355 conversions, non-ASCII parsing, oversized/fatal userinfo and pointer/buffer alias contracts.
49
  - [ ] Trace actual scratch provenance on malformed session restore; decide a documented production compatibility/default policy after validating native behavior.
 
53
  - [ ] Replace supplied spatial-query answers with recovered native broadphase, integrate bot rotation/native genetic behavior, and recover221662 map-list parsing.
54
  - [ ] Compose complete respawn/begin-intermission, event lifetime and ranking/scoreboard/network execution.
55
  - [ ] Validate actual Linux/macOS Rust/Bevy/Solari gameplay/rendering. Full-game completion remains open.
56
+
57
+ [RE-904–913](QVM_SESSION_LIFECYCLE_BATCH.md) composes initialization, full persistence, byte-fed round trips and the bounded connect caller. Its RE-914 list supersedes the historical follow-up list above.
docs/research/QVM_SESSION_LIFECYCLE_BATCH.md ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Session lifecycle and complete persistence: RE-904–913
2
+
3
+ Ten separately committed tasks connect original QVM lifecycle routines and their Rust policies across448 paired source cases. They cover initialization, both post-map children, normal map flow, byte-fed write/restore and the bounded connect caller branch. Native files, userinfo/cvars, timing and selected callbacks remain supplied inputs or captured intentions. This evidence does not establish real network/client-connect acceptance or a complete playable game.
4
+
5
+ | Task | Evidence | Cases |
6
+ | --- | --- | ---: |
7
+ | RE-904 | [Original247520 initializer](QVM_SESSION_INIT.md) | 96 |
8
+ | RE-905 | [Rust initializer and raw-field ownership](RUST_SESSION_INIT.md) | 96 |
9
+ | RE-906 | [Original249007 with246746/246963](QVM_POST_MAP_FILES.md) | 48 |
10
+ | RE-907 | [Rust complete post-map persistence](RUST_POST_MAP_FILES.md) | 48 |
11
+ | RE-908 | [Normal6939 with both persistence children](QVM_NORMAL_MAP_FILES.md) | 64 |
12
+ | RE-909 | [Rust normal map/file/cvar flow](RUST_NORMAL_MAP_FILES.md) | 64 |
13
+ | RE-910 | [Original byte-fed write/restore](QVM_PERSISTENCE_ROUNDTRIP.md) | 144 |
14
+ | RE-911 | [Rust owned in-memory persistence](RUST_PERSISTENCE_ROUNDTRIP.md) | 144 |
15
+ | RE-912 | [Bounded173979 caller clear/branch](QVM_CONNECT_SESSION.md) | 96 |
16
+ | RE-913 | [Rust connect lifecycle branch](RUST_CONNECT_SESSION.md) | 96 |
17
+
18
+ ## Initialization ownership
19
+
20
+ Actual247520 selects the initial team field at3096 through original33715 classification. Team-classified modes preserve a nonzero field, while zero becomes3. Other modes query userinfo key team through70503: a first byte of lowercase s selects3; otherwise a positive capacity limit34089308 reached by34992584 selects3, and other cases select0. The caller fixtures additionally verify uppercase S below capacity selects0.
21
+
22
+ The initializer writes clock34992532 into selected fields, sets field3108 to1, and computes the timeout at3140 with wrapping arithmetic. A nonzero configured34085460 contributes seconds*1000, including negative values; zero uses60000. Field3148 receives a clock write and is later cleared. Native -3 supplies the tick snapshot stored at3156. A36-byte bounded copy writes one space and NUL padding at3164. Several fields are word-cleared, while the starts of string-like regions are only byte-cleared; neighboring bytes remain observable in warm-state fixtures. The64 ignore words at3780 are cleared. Coordinates3716..3736 survive unless the actual246554 load succeeds.
23
+
24
+ Initialization then executes actual246963 session serialization and captures7259 diagnostics. The Rust API uses a944-byte block beginning at3096 to preserve byte/word distinctions, with explicit timing and file answers. It stages state/selector changes on typed errors. Metadata meanings, native timing/file semantics and full engine lifecycle remain open.
25
+
26
+ ## Post-map ordering and map transitions
27
+
28
+ Actual249007 now executes both recovered client children. The first246746 child produces ordered userinfo/map/open/write/close intentions; the second246963 writes sessionN and sessIgnoreN. Each eligible client's file operations precede its cvar pair. The48 fixtures vary connected-state patterns, level count, mode, enable and supplied open handles, while formatter and info selectors carry across clients.
29
+
30
+ The64 normal6939 fixtures compose122230,7188,249007 and both persistence children. Score/global resets precede persistence, and the final max-client state2-to1 scan follows it. Reset maximum and level count vary independently. The engine map bytes supplied to the map-command and saved-file paths are consistent. Rust shares action assembly between standalone and normal persistence; no file action is dispatched to an operating system. Bot callback internals,221662 map-list selection, actual cvar/console command dispatch and series-path composition remain open.
31
+
32
+ ## Byte-fed write/restore and caller integration
33
+
34
+ The144 round trips execute actual246746 and246963, then feed their captured cvar/file bytes into actual247219/246554 on a fresh destination snapshot. The destination starts with explicit metadata/position values and a nonzero ignore table; caller scratch is also supplied. Session text containing negative/INT_MIN fields is not assumed invertible: recovered formatter and scanner behavior can produce partial writes and scratch-dependent booleans. Restored ignore entries accumulate, and six-float file prefixes remain readable with appended PLAYER text.
35
+
36
+ Rust MemoryPersistence owns ordered cvar/file byte maps. It applies captured write intentions and supplies those bytes to the restore policies, staging adapter/destination/selector changes on errors. This validates an in-memory transcript; it does not validate a filesystem, native handle lifetime, actual reload or arbitrary concurrent engine effects.
37
+
38
+ The final source fixtures execute173979's174081..174173 caller portion after its userinfo/authentication prefix. They link entity offset520 to the selected client, clear entity fields764/768, model the actual native6336-byte client memset, set connection468 to1, and copy the slot into6156. The first-time argument or nonzero global34992572 chooses actual247520 initialization; otherwise actual247219 restores. Both children execute recovered scanner/formatter/load policies. Clearing the client does not clear the separate VM caller scratch. The Rust API stages raw client bytes, typed entity linkage and selector together. Authentication before this slice, later team normalization from174174 onward, bot connection behavior, alias domains and real engine acceptance remain open.
39
+
40
+ ## Validation and storage
41
+
42
+ Focused `cargo xtask test-group <name>` ran before each commit, with selected reports regenerated twice, source/Rust parity, scoped gameplay checks and storage/documentation/whitespace guards. The ten groups are qvm-session-init, rust-session-init, qvm-post-map-files, rust-post-map-files, qvm-normal-map-files, rust-normal-map-files, qvm-persistence-roundtrip, rust-persistence-roundtrip, qvm-connect-session and rust-connect-session. Shared persistence action assembly also received its standalone regression check.
43
+
44
+ The dependency-selection regression covers the five source/Rust report pairs, native_policy consumers and shared transition/map/intermission dependencies. Its import smoke check also keeps the existing health lifecycle and new session lifecycle helper namespaces independent. The first full-suite attempt caught a helper filename collision: the original health helper was restored byte-for-byte from the batch baseline, the new code moved to qvm_session_lifecycle_support.py, and affected checks were rerun before final full validation. Python runs only through the resolved Nix runtime declared by flake.nix, with explicit PYTHON inherited by xtask and Git hooks. Original assets are unchanged. After the namespace repair documented above, `cargo xtask test-full --since 6d0ce63f` completed successfully at the tenth-commit boundary: 1,768 Python tests and207 Rust tests (assets10, gameplay167, xtask30). All39 affected report producers regenerated twice deterministically. Storage policy, links across480 documentation files and whitespace checks passed with no generated-report drift. Repair and validation notes were amended into task ten.
45
+
46
+ ## Next work: RE-914
47
+
48
+ - [ ] Recover the173979 authentication/userinfo prefix and post-174174 team normalization, bot branch and downstream connect callbacks; compose the complete function.
49
+ - [ ] Recover native file-open return/handle/close behavior and attach a platform-neutral engine IO adapter; validate actual reload and failure ownership on macOS/Linux.
50
+ - [ ] Trace real VM scratch provenance, non-ASCII/tail parsing, uppercase exponent and other scanner conversions, oversized/fatal strings and pointer aliases.
51
+ - [ ] Recover direct/indirect246938 session-clear use, remaining metadata producers, ignore-table mutations and live count/base changes during callbacks.
52
+ - [ ] Integrate both persistence children into series6939/readiness, plus real cvar refresh/command dispatch, bot rotation/native genetics and221662 map-list parsing.
53
+ - [ ] Replace supplied spatial-query answers with native broadphase/world collision and compare native math.
54
+ - [ ] Compose complete respawn/intermission, event lifetime and ranking/scoreboard/network execution.
55
+ - [ ] Validate actual Linux/macOS Rust/Bevy/Solari gameplay/rendering. Full-game completion remains open.
docs/research/README.md CHANGED
@@ -1345,3 +1345,9 @@ The extension collector adds five focused tests for native tail calls, command-r
1345
 
1346
 
1347
  [RE-912: Original connect clear and session-branch caller](QVM_CONNECT_SESSION.md).
 
 
 
 
 
 
 
1345
 
1346
 
1347
  [RE-912: Original connect clear and session-branch caller](QVM_CONNECT_SESSION.md).
1348
+
1349
+
1350
+ [RE-913: Rust connect clear and session lifecycle branch](RUST_CONNECT_SESSION.md).
1351
+
1352
+
1353
+ [RE-904–913 lifecycle and complete persistence batch](QVM_SESSION_LIFECYCLE_BATCH.md).
docs/research/RUST_CONNECT_SESSION.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Rust connect clear and session lifecycle branch
2
+
3
+ RE-913. Reproduce with `python3 scripts/analyze_rust_connect_session.py`; focused validation: `cargo xtask test-group rust-connect-session`. [Machine report](rust-connect-session.json).
4
+
5
+ Rust matches96 bounded173979 caller paths, including client/entity initialization, first-time OR new-session dispatch, actual initialization/restore policies and saved-position loading. Client memory clear does not erase supplied caller scratch. Errors stage client/entity/selector changes. Authentication and later caller normalization, native IO and full client-connect acceptance remain open.
6
+
7
+ Original executable/native runtime and complete Linux/macOS Rust/Bevy/Solari acceptance remain unverified.
docs/research/qvm-resumer-audit.json CHANGED
@@ -8919,6 +8919,17 @@
8919
  "repeated_calls_opt_in": false,
8920
  "repeated_calls_setting": "default_false"
8921
  },
 
 
 
 
 
 
 
 
 
 
 
8922
  {
8923
  "backward_conditional_branches": [],
8924
  "calls": [],
@@ -10025,10 +10036,10 @@
10025
  }
10026
  },
10027
  "summary": {
10028
- "call_expressions": 202,
10029
- "files": 154,
10030
- "legacy_expressions": 157,
10031
- "nonliteral_ranges": 27,
10032
  "unresolved_mode_expressions": 3
10033
  }
10034
  }
 
8919
  "repeated_calls_opt_in": false,
8920
  "repeated_calls_setting": "default_false"
8921
  },
8922
+ {
8923
+ "backward_conditional_branches": [],
8924
+ "calls": [],
8925
+ "explicit_result_offsets": null,
8926
+ "helper": "qvm_trace_support.resume",
8927
+ "line": 39,
8928
+ "literal_range": null,
8929
+ "path": "scripts/qvm_lifecycle_support.py",
8930
+ "repeated_calls_opt_in": false,
8931
+ "repeated_calls_setting": "default_false"
8932
+ },
8933
  {
8934
  "backward_conditional_branches": [],
8935
  "calls": [],
 
10036
  }
10037
  },
10038
  "summary": {
10039
+ "call_expressions": 203,
10040
+ "files": 155,
10041
+ "legacy_expressions": 158,
10042
+ "nonliteral_ranges": 28,
10043
  "unresolved_mode_expressions": 3
10044
  }
10045
  }
docs/research/qvm-session-init.json CHANGED
The diff for this file is too large to render. See raw diff
 
docs/research/rust-connect-session.json ADDED
The diff for this file is too large to render. See raw diff
 
docs/research/rust-health-impulse.json CHANGED
@@ -2985,7 +2985,7 @@
2985
  "exit_code": 2
2986
  }
2987
  ],
2988
- "rust_unit_tests": 165,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
 
2985
  "exit_code": 2
2986
  }
2987
  ],
2988
+ "rust_unit_tests": 167,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
docs/research/rust-session-init.json CHANGED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_qvm_connect_session.py CHANGED
@@ -11,7 +11,7 @@ from analyze_qvm import write_json
11
  def main():
12
  q,raw,h,ins=source();rows=[]
13
  from qvm_persistence_support import environment,RESTORE,SCRATCH
14
- from qvm_lifecycle_support import connect_branch,raw_state,CLIENT_BYTES
15
  from qvm_byte_memory_support import write_bytes
16
  for mode,first,new,positive,slot,handle in itertools.product([0,3,9],[0,1],[0,-1],[False,True],[0,3],[0,7]):
17
  m,b=environment(raw,h);write_bytes(m,b,{a:raw[h['data_offset']+a] for a in range(83568,83824)});pointer=CPOOL+6336*slot
 
11
  def main():
12
  q,raw,h,ins=source();rows=[]
13
  from qvm_persistence_support import environment,RESTORE,SCRATCH
14
+ from qvm_session_lifecycle_support import connect_branch,raw_state,CLIENT_BYTES
15
  from qvm_byte_memory_support import write_bytes
16
  for mode,first,new,positive,slot,handle in itertools.product([0,3,9],[0,1],[0,-1],[False,True],[0,3],[0,7]):
17
  m,b=environment(raw,h);write_bytes(m,b,{a:raw[h['data_offset']+a] for a in range(83568,83824)});pointer=CPOOL+6336*slot
scripts/analyze_qvm_normal_map_files.py CHANGED
@@ -11,7 +11,7 @@ from analyze_qvm import write_json
11
  def main():
12
  q,raw,h,ins=source();rows=[]
13
  from qvm_persistence_support import environment
14
- from qvm_lifecycle_support import post_full
15
  from qvm_session_support import records
16
  from qvm_map_commands_support import map_seed,normal_flow,put_string,CURSOR,ENABLED,LIMIT,WRITE,MIRROR
17
  from qvm_transition_support import state_seed,state_words
 
11
  def main():
12
  q,raw,h,ins=source();rows=[]
13
  from qvm_persistence_support import environment
14
+ from qvm_session_lifecycle_support import post_full
15
  from qvm_session_support import records
16
  from qvm_map_commands_support import map_seed,normal_flow,put_string,CURSOR,ENABLED,LIMIT,WRITE,MIRROR
17
  from qvm_transition_support import state_seed,state_words
scripts/analyze_qvm_post_map_files.py CHANGED
@@ -11,7 +11,7 @@ from analyze_qvm import write_json
11
  def main():
12
  q,raw,h,ins=source();rows=[]
13
  from qvm_persistence_support import environment
14
- from qvm_lifecycle_support import post_full
15
  from qvm_session_support import records
16
  from qvm_map_commands_support import put_string
17
  for mode,count,pattern,enabled,opened in itertools.product([0,9],[0,1,4],range(2),[0,1],[False,True]):
 
11
  def main():
12
  q,raw,h,ins=source();rows=[]
13
  from qvm_persistence_support import environment
14
+ from qvm_session_lifecycle_support import post_full
15
  from qvm_session_support import records
16
  from qvm_map_commands_support import put_string
17
  for mode,count,pattern,enabled,opened in itertools.product([0,9],[0,1,4],range(2),[0,1],[False,True]):
scripts/analyze_qvm_session_init.py CHANGED
@@ -11,10 +11,10 @@ from analyze_qvm import write_json
11
  def main():
12
  q,raw,h,ins=source();rows=[]
13
  from qvm_persistence_support import environment
14
- from qvm_lifecycle_support import initialize,raw_state,SIZE
15
  from qvm_byte_memory_support import write_bytes
16
  from qvm_formatter_support import image_bytes
17
- profiles=[(b'',0,0,0,100,11),(b'\\team\\s\\cl_guid\\ID',2,1,0,100,17),(b'\\team\\S\\cl_guid\\ID',2,2,2,100,19),(b'\\team\\spectator\\cl_guid\\ID',2,2,-1,2147483647,0xffffffff)]
18
  for mode,team,profile,slot,handle in itertools.product([0,3,9],[0,2],range(4),[0,3],[0,7]):
19
  info,limit,occupancy,seconds,now,ticks=profiles[profile];m,b=environment(raw,h);write_bytes(m,b,{a:raw[h['data_offset']+a] for a in range(83568,83824)});pointer=CPOOL+6336*slot
20
  for j in range(0,SIZE,4):m[pointer+3096+j]=0xaabbccdd
 
11
  def main():
12
  q,raw,h,ins=source();rows=[]
13
  from qvm_persistence_support import environment
14
+ from qvm_session_lifecycle_support import initialize,raw_state,SIZE
15
  from qvm_byte_memory_support import write_bytes
16
  from qvm_formatter_support import image_bytes
17
+ profiles=[(b'',0,0,0,100,11),(b'\\team\\s\\cl_guid\\ID',2,1,0,100,17),(b'\\team\\S\\cl_guid\\ID',2,2,2,2147483647,19),(b'\\team\\spectator\\cl_guid\\ID',2,2,-1,2147483647,0xffffffff)]
18
  for mode,team,profile,slot,handle in itertools.product([0,3,9],[0,2],range(4),[0,3],[0,7]):
19
  info,limit,occupancy,seconds,now,ticks=profiles[profile];m,b=environment(raw,h);write_bytes(m,b,{a:raw[h['data_offset']+a] for a in range(83568,83824)});pointer=CPOOL+6336*slot
20
  for j in range(0,SIZE,4):m[pointer+3096+j]=0xaabbccdd
scripts/analyze_rust_connect_session.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import hashlib,json
3
+ from pathlib import Path
4
+ from rust_native_policy_support import binary,result,invoke
5
+ from analyze_qvm import write_json
6
+ def encode(s):
7
+ a=s['input'];return [a[k] for k in ['mode','first','new','scratch','slot','handle']]
8
+ def project(s):return {k:s[k] for k in ['raw','nonzero','choice','entity_linked','entity_clear','ring','info_ring']}
9
+
10
+ def main():
11
+ b=binary();p=Path('docs/research/qvm-connect-session.json');data=p.read_bytes();rows=[]
12
+ for s in json.loads(data)['probes']:rows.append({'source':project(s),'rust':result(b,'connect-session',encode(s))})
13
+ write_json(Path('docs/research/rust-connect-session.json'),{'source_report':str(p),'source_report_sha256':hashlib.sha256(data).hexdigest(),'probes':rows,'limits':'Rust matches96 bounded173979 caller paths, including client/entity initialization, first-time OR new-session dispatch, actual initialization/restore policies and saved-position loading. Client memory clear does not erase supplied caller scratch. Errors stage client/entity/selector changes. Authentication and later caller normalization, native IO and full client-connect acceptance remain open.'})
14
+ if __name__=='__main__':main()
scripts/qvm_lifecycle_support.py CHANGED
@@ -1,52 +1,40 @@
1
- """Recovered session initialization and connected persistence compositions."""
2
- from qvm_camera_world_support import B,run
3
- from qvm_exit_rules_support import CPOOL
4
- from qvm_persistence_support import info_lookup,load_position,save_position
5
- from qvm_session_support import session,FIELDS
6
- from qvm_string_memory_support import cstring,copy_bounded
7
- from qvm_byte_memory_support import read_bytes
8
- INIT=B+0x50000
9
- SIZE=944
10
 
11
- def initialize(ins,m,b,pointer,userinfo,handle,ticks):
12
- from qvm_persistence_support import INFO
13
- from qvm_map_commands_support import put_string
14
- put_string(m,b,INFO,userinfo);parts={};diagnostics=[]
15
- def classify(i,m,a,bs):
16
- m,v,p,b=run(i,m,33715,33740,12,a,dict(bs),base=B+0x52000);return m,v,{'actual_source_child':33715}
17
- def lookup(i,m,a,bs):
18
- m,v,p,b=info_lookup(i,m,dict(bs),a);return m,v,{'actual_source_child':70503,'child_memory_bytes':{v+j:x for j,x in enumerate(cstring(m,b,v)+b'\0')}}
19
- def clock(i,m,a,bs):return m,ticks,{'engine_ticks_supplied':True}
20
- def copy(i,m,a,bs):return copy_bounded(i,m,a,dict(bs))
21
- def load(i,m,a,bs):
22
- data=b'9,8,7,6,5,4';m,p,b=load_position(i,m,dict(bs),a[8],userinfo,b'ut4_test',handle,len(data),data);parts['load']=p;return m,0,{'actual_source_child':246554}
23
- def write(i,m,a,bs):
24
- m,p,b=session(i,m,dict(bs),a[8]);parts['session']=p;return m,0,{'actual_source_child':246963}
25
- def diagnostic(i,m,a,bs):diagnostics.append([a[8],a[12]&0xffffffff,a[16]&0xffffffff]);return m,0,{'diagnostic_captured':True}
26
- m,v,p,b=run(ins,m,247520,247806,40,{8:pointer,12:INFO},b,{33715:classify,70503:lookup,-3:clock,69338:copy,246554:load,246963:write,7259:diagnostic},base=INIT);p.update(parts=parts,diagnostics=diagnostics);return m,p,b
27
 
28
- def raw_state(m,b,pointer):return read_bytes(m,b,pointer+3096,SIZE).hex()
 
29
 
30
- def post_full(ins,m,b,userinfos,names,handles):
31
- calls=[];files=[];sets=[];actions=[]
32
- def save(i,m,a,bs):
33
- j=(a[8]-CPOOL)//6336;calls.append([246746,j]);m,p,b=save_position(i,m,dict(bs),a[8],userinfos[j],b'ut4_test',handles[j],-7);files.append([j,p['events']]);actions.extend(dict(e,kind='file',slot=j) for e in p['events']);return m,0,{'actual_source_child':246746}
34
- def write(i,m,a,bs):
35
- j=(a[8]-CPOOL)//6336;calls.append([246963,j]);m,p,b=session(i,m,dict(bs),a[8]);sets.extend(p['sets']);actions.extend(dict(kind='cvar',slot=j,key=k,value=v) for k,v in p['sets']);return m,0,{'actual_source_child':246963}
36
- m,v,p,b=run(ins,m,249007,249058,16,b=b,handlers={246746:save,246963:write},base=B+0x54000);p.update(calls=calls,files=files,sets=sets,actions=actions);return m,p,b
 
 
 
 
 
 
37
 
38
- CONNECT=B+0x58000
39
- CLIENT_BYTES=6336
40
- def connect_branch(ins,m,b,slot,first,userinfo,handle):
41
- import hashlib,json
42
- from qvm_map_commands_support import put_string
43
- from qvm_persistence_support import restore_session
44
- entity=34091368+880*slot;pointer=CPOOL+6336*slot;m[CONNECT+24]=entity;put_string(m,b,CONNECT+28,userinfo,1024);parts={};choice=[]
45
- def zero(i,m,a,bs):
46
- if a[8]!=pointer or a[12]!=0 or a[16]!=6336:raise ValueError('unsupported connect memset')
47
- return m,a[8],{'native_memset_modeled':True,'child_memory_bytes':{pointer+j:0 for j in range(6336)}}
48
- def fresh(i,m,a,bs):
49
- choice.append(247520);m,p,b=initialize(i,m,dict(bs),a[8],userinfo,handle,17);parts.update(p);return m,0,{'actual_source_child':247520}
50
- def restored(i,m,a,bs):
51
- choice.append(247219);data=b'9,8,7,6,5,4';m,p,b=restore_session(i,m,dict(bs),a[8],b'1,-2,3,4,5,1,1,1,1',b'1,3,',userinfo,b'ut4_test',handle,len(data),data);parts.update(p);return m,0,{'actual_source_child':247219}
52
- m,v,p,b=run(ins,m,174080,174174,1080,{8:slot,12:first,16:0},b,{-101:zero,247520:fresh,247219:restored},base=CONNECT);changes=p.pop('changes');p.update(change_count=len(changes),changes_sha256=hashlib.sha256(json.dumps(changes,sort_keys=True).encode()).hexdigest(),choice=choice,parts=parts,entity_linked=m[entity+520]==pointer,entity_clear=[m[entity+764]&0xffffffff,m[entity+768]&0xffffffff]);return m,p,b
 
1
+ """Explicit finite fixtures for protected and mode10 lifecycle source ranges."""
2
+ from qvm_health_effects_support import seed as health_seed,H,TARGET,ATTACKER,CLIENT,A_CLIENT
3
+ from qvm_research_support import constants
4
+ from qvm_trace_support import resume,captured
5
+ from analyze_qvm_acceleration import word
6
+ L=H+0xa0000
7
+ EVENT=L+0x10000
8
+ FIELDS=[4,12,44,76,84,104,140,144,148,184,208,220,232,236,280,3096,4300,4304,4308,4312,4380,4384,4388,4392,6324,6332]
9
+ EFIELDS=[0,140,144,160,168,184,200,204,424]
10
 
11
+ def seed(raw,h,**a):
12
+ m=health_seed(raw,h)
13
+ m.update(constants(raw,h,list(range(29584,29808,4))+list(range(4032,17416,4))))
14
+ for o in range(0,180,4):m[L+o]=0
15
+ m.update({L+72:TARGET,L+76:ATTACKER if a.get('attacker',True) else 0,L+80:a.get('flag',0),L+164:TARGET,L+168:ATTACKER,L+172:a.get('method',12),34090788:a.get('mode',10),34999596:a.get('blocked',0),34992532:a.get('now',10000),98588:a.get('disclose',0),34999668:7,34999672:9,34087532:a.get('penalty',1),34075396:a.get('limit',3),TARGET+168:65,ATTACKER+168:7,ATTACKER+764:a.get('strikes',0),ATTACKER+768:a.get('last',0),ATTACKER+424:a.get('attacker_flags',0)})
16
+ for o in FIELDS:m[CLIENT+o]=a.get('c'+str(o),0x1ffff if o in (104,220,236) else 7)
17
+ m.update({CLIENT+232:a.get('count',31990),CLIENT+6332:word(a.get('heat',10)),CLIENT+4:a.get('state',0),CLIENT+12:a.get('flags12',8),CLIENT+144:0,CLIENT+312:a.get('weapon',1),CLIENT+3096:a.get('team',1),A_CLIENT+3096:a.get('attacker_team',1),A_CLIENT+232:1234,A_CLIENT+140:7})
18
+ return m
 
 
 
 
 
 
 
 
19
 
20
+ def snapshot(m):
21
+ return {'client':{str(o):m[CLIENT+o]&0xffffffff for o in FIELDS},'attacker_count':m[A_CLIENT+232]&0xffffffff,'entity':{str(o):m[TARGET+o]&0xffffffff for o in [684,720]},'attacker':{str(o):m[ATTACKER+o]&0xffffffff for o in [764,768]},'team_counts':[m[34999668]&0xffffffff,m[34999672]&0xffffffff]}
22
 
23
+ def handlers(effects,resolvers=None):
24
+ def event(ins,m,args):
25
+ p=EVENT+len(effects['events'])*880
26
+ for o in range(0,880,4):m[p+o]=0
27
+ effects['events'].append({'arguments':args,'pointer':p,'fixture_allocation':True})
28
+ return m,p,{'supplied_event_pointer':p,'original_allocator_not_executed':True}
29
+ hs={t:captured(t,effects['requests']) for t in [7259,205260,6369,6439,278834,285486,-17,-18]}
30
+ def predicate(ins,m,args):
31
+ from qvm_contact_support import predicate as actual
32
+ return actual(ins,m,args)
33
+ def formatting(ins,m,args):
34
+ effects['requests'].append({'target':70458,'arguments':args,'native_effect_not_executed':True});return m,EVENT+0x10000,{'supplied_string_pointer':EVENT+0x10000}
35
+ hs.update({277695:event,33715:predicate,70458:formatting});hs.update(resolvers or {});return hs
36
 
37
+ def probe(ins,m,start,stop,protected=False,resolvers=None):
38
+ before=snapshot(m);effects={'events':[],'requests':[]};end=152671 if protected else 207154
39
+ m,e=resume(ins,start,end,m,L,handlers=handlers(effects,resolvers),boundaries=[] if stop==end else [stop],limit=50000)
40
+ return m,{'initial':before,'final':snapshot(m),'effects':effects,'events':[{**v,'fields':{str(o):m[v['pointer']+o]&0xffffffff for o in EFIELDS}} for v in effects['events']],'execution':e}
 
 
 
 
 
 
 
 
 
 
 
scripts/qvm_session_lifecycle_support.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Recovered session initialization and connected persistence compositions."""
2
+ from qvm_camera_world_support import B,run
3
+ from qvm_exit_rules_support import CPOOL
4
+ from qvm_persistence_support import info_lookup,load_position,save_position
5
+ from qvm_session_support import session,FIELDS
6
+ from qvm_string_memory_support import cstring,copy_bounded
7
+ from qvm_byte_memory_support import read_bytes
8
+ INIT=B+0x50000
9
+ SIZE=944
10
+
11
+ def initialize(ins,m,b,pointer,userinfo,handle,ticks):
12
+ from qvm_persistence_support import INFO
13
+ from qvm_map_commands_support import put_string
14
+ put_string(m,b,INFO,userinfo);parts={};diagnostics=[]
15
+ def classify(i,m,a,bs):
16
+ m,v,p,b=run(i,m,33715,33740,12,a,dict(bs),base=B+0x52000);return m,v,{'actual_source_child':33715}
17
+ def lookup(i,m,a,bs):
18
+ m,v,p,b=info_lookup(i,m,dict(bs),a);return m,v,{'actual_source_child':70503,'child_memory_bytes':{v+j:x for j,x in enumerate(cstring(m,b,v)+b'\0')}}
19
+ def clock(i,m,a,bs):return m,ticks,{'engine_ticks_supplied':True}
20
+ def copy(i,m,a,bs):return copy_bounded(i,m,a,dict(bs))
21
+ def load(i,m,a,bs):
22
+ data=b'9,8,7,6,5,4';m,p,b=load_position(i,m,dict(bs),a[8],userinfo,b'ut4_test',handle,len(data),data);parts['load']=p;return m,0,{'actual_source_child':246554}
23
+ def write(i,m,a,bs):
24
+ m,p,b=session(i,m,dict(bs),a[8]);parts['session']=p;return m,0,{'actual_source_child':246963}
25
+ def diagnostic(i,m,a,bs):diagnostics.append([a[8],a[12]&0xffffffff,a[16]&0xffffffff]);return m,0,{'diagnostic_captured':True}
26
+ m,v,p,b=run(ins,m,247520,247806,40,{8:pointer,12:INFO},b,{33715:classify,70503:lookup,-3:clock,69338:copy,246554:load,246963:write,7259:diagnostic},base=INIT);p.update(parts=parts,diagnostics=diagnostics);return m,p,b
27
+
28
+ def raw_state(m,b,pointer):return read_bytes(m,b,pointer+3096,SIZE).hex()
29
+
30
+ def post_full(ins,m,b,userinfos,names,handles):
31
+ calls=[];files=[];sets=[];actions=[]
32
+ def save(i,m,a,bs):
33
+ j=(a[8]-CPOOL)//6336;calls.append([246746,j]);m,p,b=save_position(i,m,dict(bs),a[8],userinfos[j],b'ut4_test',handles[j],-7);files.append([j,p['events']]);actions.extend(dict(e,kind='file',slot=j) for e in p['events']);return m,0,{'actual_source_child':246746}
34
+ def write(i,m,a,bs):
35
+ j=(a[8]-CPOOL)//6336;calls.append([246963,j]);m,p,b=session(i,m,dict(bs),a[8]);sets.extend(p['sets']);actions.extend(dict(kind='cvar',slot=j,key=k,value=v) for k,v in p['sets']);return m,0,{'actual_source_child':246963}
36
+ m,v,p,b=run(ins,m,249007,249058,16,b=b,handlers={246746:save,246963:write},base=B+0x54000);p.update(calls=calls,files=files,sets=sets,actions=actions);return m,p,b
37
+
38
+ CONNECT=B+0x58000
39
+ CLIENT_BYTES=6336
40
+ def connect_branch(ins,m,b,slot,first,userinfo,handle):
41
+ import hashlib,json
42
+ from qvm_map_commands_support import put_string
43
+ from qvm_persistence_support import restore_session
44
+ entity=34091368+880*slot;pointer=CPOOL+6336*slot;m[CONNECT+24]=entity;put_string(m,b,CONNECT+28,userinfo,1024);parts={};choice=[]
45
+ def zero(i,m,a,bs):
46
+ if a[8]!=pointer or a[12]!=0 or a[16]!=6336:raise ValueError('unsupported connect memset')
47
+ return m,a[8],{'native_memset_modeled':True,'child_memory_bytes':{pointer+j:0 for j in range(6336)}}
48
+ def fresh(i,m,a,bs):
49
+ choice.append(247520);m,p,b=initialize(i,m,dict(bs),a[8],userinfo,handle,17);parts.update(p);return m,0,{'actual_source_child':247520}
50
+ def restored(i,m,a,bs):
51
+ choice.append(247219);data=b'9,8,7,6,5,4';m,p,b=restore_session(i,m,dict(bs),a[8],b'1,-2,3,4,5,1,1,1,1',b'1,3,',userinfo,b'ut4_test',handle,len(data),data);parts.update(p);return m,0,{'actual_source_child':247219}
52
+ m,v,p,b=run(ins,m,174080,174174,1080,{8:slot,12:first,16:0},b,{-101:zero,247520:fresh,247219:restored},base=CONNECT);changes=p.pop('changes');p.update(change_count=len(changes),changes_sha256=hashlib.sha256(json.dumps(changes,sort_keys=True).encode()).hexdigest(),choice=choice,parts=parts,entity_linked=m[entity+520]==pointer,entity_clear=[m[entity+764]&0xffffffff,m[entity+768]&0xffffffff]);return m,p,b
scripts/test_qvm_session_init.py CHANGED
@@ -4,6 +4,7 @@ class OriginalTests(unittest.TestCase):
4
  def test_instruction_results(self):
5
  rows=json.loads(Path('docs/research/qvm-session-init.json').read_text())['probes']
6
  self.assertEqual(len(rows),96)
 
7
  import struct
8
  from analyze_qvm_acceleration import word
9
  for p in rows:
 
4
  def test_instruction_results(self):
5
  rows=json.loads(Path('docs/research/qvm-session-init.json').read_text())['probes']
6
  self.assertEqual(len(rows),96)
7
+ self.assertTrue(any(int.from_bytes(bytes.fromhex(p["raw"])[44:48],"little")>=2**31 for p in rows))
8
  import struct
9
  from analyze_qvm_acceleration import word
10
  for p in rows:
scripts/test_rust_connect_session.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import hashlib,json,unittest
2
+ from pathlib import Path
3
+ class PortTests(unittest.TestCase):
4
+ def test_all_original_cases(self):
5
+ r=json.loads(Path('docs/research/rust-connect-session.json').read_text());src=json.loads(Path(r['source_report']).read_text());self.assertEqual(len(r['probes']),len(src['probes']));self.assertGreater(len(r['probes']),20)
6
+ for p in r['probes']:self.assertEqual(p['rust'],p['source'])
7
+ def test_source_identity(self):
8
+ r=json.loads(Path('docs/research/rust-connect-session.json').read_text());self.assertEqual(hashlib.sha256(Path(r['source_report']).read_bytes()).hexdigest(),r['source_report_sha256'])
9
+ if __name__=='__main__':unittest.main()
xtask/checks.tsv CHANGED
@@ -2887,3 +2887,21 @@ report scripts/analyze_qvm_connect_session.py docs/research/qvm-connect-session.
2887
  edge docs/research/qvm-connect-session.json scripts/test_qvm_connect_session.py
2888
  group qvm-connect-session scripts/analyze_qvm_connect_session.py
2889
  edge scripts/analyze_qvm_connect_session.py scripts/analyze_qvm_resumer_audit.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2887
  edge docs/research/qvm-connect-session.json scripts/test_qvm_connect_session.py
2888
  group qvm-connect-session scripts/analyze_qvm_connect_session.py
2889
  edge scripts/analyze_qvm_connect_session.py scripts/analyze_qvm_resumer_audit.py
2890
+ edge Cargo.toml scripts/analyze_rust_connect_session.py
2891
+ edge Cargo.lock scripts/analyze_rust_connect_session.py
2892
+ edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_connect_session.py
2893
+ edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_connect_session.py
2894
+ edge crates/urt-gameplay/src/native_policy.rs scripts/analyze_rust_connect_session.py
2895
+ edge crates/urt-gameplay/src/bin/urt-native-policy.rs scripts/analyze_rust_connect_session.py
2896
+ edge docs/research/qvm-connect-session.json scripts/analyze_rust_connect_session.py
2897
+ group rust-connect-session scripts/analyze_rust_health_impulse.py
2898
+ group gameplay scripts/analyze_rust_connect_session.py
2899
+ edge crates/urt-gameplay/src/intermission.rs scripts/analyze_rust_connect_session.py
2900
+ edge crates/urt-gameplay/src/qvm_format.rs scripts/analyze_rust_connect_session.py
2901
+ report scripts/analyze_rust_connect_session.py docs/research/rust-connect-session.json
2902
+ edge docs/research/rust-connect-session.json scripts/test_rust_connect_session.py
2903
+ group rust-connect-session scripts/analyze_rust_connect_session.py
2904
+ edge scripts/analyze_rust_connect_session.py scripts/analyze_qvm_resumer_audit.py
2905
+ group rust-connect-session scripts/analyze_qvm_session_init.py
2906
+ edge scripts/qvm_session_lifecycle_support.py scripts/analyze_qvm_resumer_audit.py
2907
+ group rust-connect-session scripts/analyze_qvm_health_mode10.py
xtask/src/lib.rs CHANGED
@@ -1055,6 +1055,10 @@ mod integration_tests {
1055
  .unwrap();
1056
  g.scanner(&output).unwrap();
1057
  assert!(g.warnings.is_empty(), "{:?}", g.warnings);
 
 
 
 
1058
  let health = plan(
1059
  &g,
1060
  &["scripts/qvm_health_support.py".into()]
@@ -1105,6 +1109,14 @@ mod integration_tests {
1105
  ("native_policy", "position_load"),
1106
  ("native_policy", "session_restore"),
1107
  ("intermission", "session_restore"),
 
 
 
 
 
 
 
 
1108
  ("protected", "protected_update"),
1109
  ("mode10", "mode10_entry"),
1110
  ("intercept_animation", "mode10_animation"),
@@ -1179,6 +1191,11 @@ mod integration_tests {
1179
  ("qvm-position-save", "position_save"),
1180
  ("qvm-position-load", "position_load"),
1181
  ("qvm-session-restore", "session_restore"),
 
 
 
 
 
1182
  ("qvm-rank-small-sort", "rank_sort"),
1183
  ("qvm-rank-large-sort", "rank_sort"),
1184
  ("qvm-ranking-complete", "ranking_complete"),
 
1055
  .unwrap();
1056
  g.scanner(&output).unwrap();
1057
  assert!(g.warnings.is_empty(), "{:?}", g.warnings);
1058
+ // Keep independent health and session helper namespaces importable.
1059
+ // A filename collision must fail this scoped registry regression.
1060
+ capture(root,&Cmd::new(&python_runtime().expect("run cargo test inside nix develop"),["-I","-c","import sys; sys.path.insert(0, 'scripts'); from qvm_lifecycle_support import seed, probe; from qvm_session_lifecycle_support import initialize, post_full, connect_branch"])).unwrap();
1061
+
1062
  let health = plan(
1063
  &g,
1064
  &["scripts/qvm_health_support.py".into()]
 
1109
  ("native_policy", "position_load"),
1110
  ("native_policy", "session_restore"),
1111
  ("intermission", "session_restore"),
1112
+ ("native_policy", "session_init"),
1113
+ ("native_policy", "post_map_files"),
1114
+ ("native_policy", "normal_map_files"),
1115
+ ("native_policy", "persistence_roundtrip"),
1116
+ ("native_policy", "connect_session"),
1117
+ ("map_commands", "normal_map_files"),
1118
+ ("transition", "normal_map_files"),
1119
+ ("intermission", "connect_session"),
1120
  ("protected", "protected_update"),
1121
  ("mode10", "mode10_entry"),
1122
  ("intercept_animation", "mode10_animation"),
 
1191
  ("qvm-position-save", "position_save"),
1192
  ("qvm-position-load", "position_load"),
1193
  ("qvm-session-restore", "session_restore"),
1194
+ ("qvm-session-init", "session_init"),
1195
+ ("qvm-post-map-files", "post_map_files"),
1196
+ ("qvm-normal-map-files", "normal_map_files"),
1197
+ ("qvm-persistence-roundtrip", "persistence_roundtrip"),
1198
+ ("qvm-connect-session", "connect_session"),
1199
  ("qvm-rank-small-sort", "rank_sort"),
1200
  ("qvm-rank-large-sort", "rank_sort"),
1201
  ("qvm-ranking-complete", "ranking_complete"),