nmarshall commited on
Commit
bc3e762
·
1 Parent(s): 6d73f71

gameplay: compose complete connect caller policy

Browse files
crates/urt-gameplay/src/bin/urt-native-policy.rs CHANGED
@@ -873,6 +873,92 @@ fn run() -> Result<String, String> {
873
  .map_err(|e| format!("{e:?}"))?;
874
  Ok(tail_json(&out, &client, &entity))
875
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
876
  _ => Err("unknown command".into()),
877
  }
878
  }
@@ -1258,3 +1344,66 @@ fn tail_json(out: &ConnectTail, client: &[u8], entity: &ConnectTailEntity) -> St
1258
  out.ring_advance
1259
  )
1260
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
873
  .map_err(|e| format!("{e:?}"))?;
874
  Ok(tail_json(&out, &client, &entity))
875
  }
876
+ "connect-complete" => {
877
+ if w.len() < 9 {
878
+ return Err("missing complete header".into());
879
+ }
880
+ let n = w[8] as usize;
881
+ if n > 1024 || w.len() < 9 + 2 * n {
882
+ return Err("invalid complete table".into());
883
+ }
884
+ let entries = w[9..9 + 2 * n]
885
+ .chunks_exact(2)
886
+ .map(|v| (v[0], v[1]))
887
+ .collect::<Vec<_>>();
888
+ let mut p = 9 + 2 * n;
889
+ let info = input_blob(&w, &mut p)?.ok_or("null complete info")?;
890
+ let password = input_blob(&w, &mut p)?.ok_or("null complete password")?;
891
+ if p != w.len() {
892
+ return Err("trailing complete words".into());
893
+ }
894
+ let mut client = 0xaabbccddu32.to_le_bytes().repeat(1584);
895
+ client[504..516].fill(0);
896
+ client[504..507].copy_from_slice(b"old");
897
+ let mut entity = CompleteConnectEntity {
898
+ session: ConnectEntity {
899
+ linked_slot: None,
900
+ field764: 0xaabbccdd,
901
+ field768: 0xaabbccdd,
902
+ },
903
+ tail: ConnectTailEntity {
904
+ flags: 32,
905
+ in_use: 0,
906
+ },
907
+ };
908
+ let mut ring = 0;
909
+ let out = complete_connect(
910
+ &mut client,
911
+ &mut entity,
912
+ CompleteConnectInput {
913
+ admission: AdmissionInput {
914
+ slot: 3,
915
+ bot: w[3] as i32,
916
+ userinfo: &info,
917
+ password: &password,
918
+ entries: &entries,
919
+ count: n as i32,
920
+ mode: 1,
921
+ scratch: w[6],
922
+ },
923
+ first_time: w[1] as i32,
924
+ new_session: w[2] as i32,
925
+ flags: 1,
926
+ initialize: InitializeInput {
927
+ mode: w[0] as i32,
928
+ slot: 3,
929
+ userinfo: &info,
930
+ limit: 2,
931
+ occupancy: 1,
932
+ seconds: 0,
933
+ clock: 100,
934
+ ticks: 17,
935
+ handle: 7,
936
+ client_id: 0,
937
+ enabled: 1,
938
+ mapname: b"ut4_test",
939
+ file_length: 11,
940
+ file_data: b"9,8,7,6,5,4",
941
+ },
942
+ session: b"1,-2,3,4,5,1,1,1,1",
943
+ ignore_text: b"1,3,",
944
+ scratch: [w[5]; 5],
945
+ tail: ConnectTailInput {
946
+ mode: w[0] as i32,
947
+ slot: 3,
948
+ first_time: w[1] as i32,
949
+ bot: w[3] as i32,
950
+ bot_answer: w[4] as i32,
951
+ clock: 100,
952
+ seconds: 0,
953
+ after_team: if w[7] != 0 { Some(2) } else { None },
954
+ name: b"new^1name",
955
+ },
956
+ },
957
+ &mut ring,
958
+ )
959
+ .map_err(|e| format!("{e:?}"))?;
960
+ Ok(complete_json(&out, &client, &entity, ring))
961
+ }
962
  _ => Err("unknown command".into()),
963
  }
964
  }
 
1344
  out.ring_advance
1345
  )
1346
  }
1347
+
1348
+ fn complete_json(
1349
+ out: &CompleteConnect,
1350
+ client: &[u8],
1351
+ entity: &CompleteConnectEntity,
1352
+ ring: u32,
1353
+ ) -> String {
1354
+ let message = out
1355
+ .message
1356
+ .as_ref()
1357
+ .map(|v| format!(r#""{}""#, hex(v)))
1358
+ .unwrap_or("null".into());
1359
+ let steps = out
1360
+ .admission
1361
+ .steps
1362
+ .iter()
1363
+ .map(|v| format!(r#""{v}""#))
1364
+ .collect::<Vec<_>>()
1365
+ .join(",");
1366
+ let filter = out
1367
+ .admission
1368
+ .filter
1369
+ .as_ref()
1370
+ .map(|f| {
1371
+ format!(
1372
+ r#"{{"filter_result":{},"parsed":{}}}"#,
1373
+ u32::from(f.rejected),
1374
+ f.parsed.map(|v| v.to_string()).unwrap_or("null".into())
1375
+ )
1376
+ })
1377
+ .unwrap_or("{}".into());
1378
+ let (choice, sets, reads) = if let Some(life) = &out.lifecycle {
1379
+ (
1380
+ format!("[{}]", if life.fresh { 247520 } else { 247219 }),
1381
+ life.initialized
1382
+ .as_ref()
1383
+ .map(|v| sets_json(&v.session.sets))
1384
+ .unwrap_or("[]".into()),
1385
+ life.restored
1386
+ .as_ref()
1387
+ .map(|v| sets_json(&v.cvars))
1388
+ .unwrap_or("[]".into()),
1389
+ )
1390
+ } else {
1391
+ ("[]".into(), "[]".into(), "[]".into())
1392
+ };
1393
+ let checksum = client.iter().fold(14695981039346656037u64, |v, b| {
1394
+ (v ^ *b as u64).wrapping_mul(1099511628211)
1395
+ });
1396
+ format!(
1397
+ r#"{{"accepted":{},"message":{message},"steps":[{steps}],"filter":{filter},"events":{},"choice":{choice},"raw":"{}","client_checksum":{checksum},"words":{},"entity":[{},{},{},{},{}],"sets":{sets},"reads":{reads},"ring":{},"info_ring":{ring}}}"#,
1398
+ out.accepted,
1399
+ tail_events(&out.events),
1400
+ hex(&client[3096..4040]),
1401
+ watched_words(client),
1402
+ entity.tail.flags,
1403
+ entity.tail.in_use as u32,
1404
+ entity.session.field764,
1405
+ entity.session.field768,
1406
+ u32::from(entity.session.linked_slot == Some(3)),
1407
+ out.ring_advance
1408
+ )
1409
+ }
crates/urt-gameplay/src/native_policy.rs CHANGED
@@ -2515,3 +2515,175 @@ mod connect_tail_tests {
2515
  assert_eq!(raw_word(&raw, 3140), 100);
2516
  }
2517
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2515
  assert_eq!(raw_word(&raw, 3140), 100);
2516
  }
2517
  }
2518
+
2519
+ #[derive(Clone, Debug, PartialEq, Eq)]
2520
+ pub struct CompleteConnectEntity {
2521
+ pub session: ConnectEntity,
2522
+ pub tail: ConnectTailEntity,
2523
+ }
2524
+ pub struct CompleteConnectInput<'a> {
2525
+ pub admission: AdmissionInput<'a>,
2526
+ pub first_time: i32,
2527
+ pub new_session: i32,
2528
+ pub flags: u32,
2529
+ pub initialize: InitializeInput<'a>,
2530
+ pub session: &'a [u8],
2531
+ pub ignore_text: &'a [u8],
2532
+ pub scratch: [u32; 5],
2533
+ pub tail: ConnectTailInput<'a>,
2534
+ }
2535
+ #[derive(Clone, Debug, PartialEq, Eq)]
2536
+ pub struct CompleteConnect {
2537
+ pub admission: Admission,
2538
+ pub lifecycle: Option<ConnectedSession>,
2539
+ pub tail: Option<ConnectTail>,
2540
+ pub accepted: bool,
2541
+ pub message: Option<Vec<u8>>,
2542
+ pub events: Vec<ConnectTailEvent>,
2543
+ pub ring_advance: u32,
2544
+ }
2545
+ /// Compose the complete173979 policy. Ordinary denial is a source return, not
2546
+ /// a typed error: auth denial changes only the selector; bot denial keeps prior
2547
+ /// lifecycle/entity changes. Unsupported domains stage all state atomically.
2548
+ pub fn complete_connect(
2549
+ client: &mut [u8],
2550
+ entity: &mut CompleteConnectEntity,
2551
+ input: CompleteConnectInput<'_>,
2552
+ info_ring: &mut u32,
2553
+ ) -> Result<CompleteConnect, Error> {
2554
+ if client.len() != CLIENT_BYTES
2555
+ || input.admission.slot != input.initialize.slot
2556
+ || input.tail.slot != input.initialize.slot
2557
+ || input.tail.mode != input.initialize.mode
2558
+ || input.tail.first_time != input.first_time
2559
+ || input.tail.bot != input.admission.bot
2560
+ {
2561
+ return Err(Error::InvalidInput);
2562
+ }
2563
+ let mut selector = *info_ring;
2564
+ let admission = admit_connect(input.admission, &mut selector)?;
2565
+ if !admission.accepted {
2566
+ let message = admission.message.clone();
2567
+ *info_ring = selector;
2568
+ return Ok(CompleteConnect {
2569
+ admission,
2570
+ lifecycle: None,
2571
+ tail: None,
2572
+ accepted: false,
2573
+ message,
2574
+ events: vec![],
2575
+ ring_advance: 0,
2576
+ });
2577
+ }
2578
+ let mut bytes = client.to_vec();
2579
+ let mut e = entity.clone();
2580
+ let lifecycle = connect_session(
2581
+ &mut bytes,
2582
+ &mut e.session,
2583
+ ConnectInput {
2584
+ first_time: input.first_time,
2585
+ new_session: input.new_session,
2586
+ flags: input.flags,
2587
+ initialize: input.initialize,
2588
+ session: input.session,
2589
+ ignore_text: input.ignore_text,
2590
+ scratch: input.scratch,
2591
+ },
2592
+ &mut selector,
2593
+ )?;
2594
+ let tail = finish_connect(&mut bytes, &mut e.tail, input.tail)?;
2595
+ let accepted = tail.accepted;
2596
+ let message = tail.message.clone();
2597
+ let mut events = vec![ConnectTailEvent::Zero {
2598
+ offset: 0,
2599
+ length: CLIENT_BYTES,
2600
+ }];
2601
+ events.extend(tail.events.clone());
2602
+ let ring_advance = lifecycle.ring_advance + tail.ring_advance;
2603
+ client.copy_from_slice(&bytes);
2604
+ *entity = e;
2605
+ *info_ring = selector;
2606
+ Ok(CompleteConnect {
2607
+ admission,
2608
+ lifecycle: Some(lifecycle),
2609
+ tail: Some(tail),
2610
+ accepted,
2611
+ message,
2612
+ events,
2613
+ ring_advance,
2614
+ })
2615
+ }
2616
+ #[cfg(test)]
2617
+ mod complete_connect_tests {
2618
+ use super::*;
2619
+ #[test]
2620
+ fn invalid_record_is_atomic_before_admission() {
2621
+ let mut bytes = [];
2622
+ let mut entity = CompleteConnectEntity {
2623
+ session: ConnectEntity {
2624
+ linked_slot: None,
2625
+ field764: 7,
2626
+ field768: 8,
2627
+ },
2628
+ tail: ConnectTailEntity {
2629
+ flags: 32,
2630
+ in_use: 0,
2631
+ },
2632
+ };
2633
+ let before = entity.clone();
2634
+ let mut ring = 0;
2635
+ let result = complete_connect(
2636
+ &mut bytes,
2637
+ &mut entity,
2638
+ CompleteConnectInput {
2639
+ admission: AdmissionInput {
2640
+ slot: 0,
2641
+ bot: 0,
2642
+ userinfo: b"",
2643
+ password: b"",
2644
+ entries: &[],
2645
+ count: 0,
2646
+ mode: 1,
2647
+ scratch: 0,
2648
+ },
2649
+ first_time: 0,
2650
+ new_session: 0,
2651
+ flags: 0,
2652
+ initialize: InitializeInput {
2653
+ mode: 0,
2654
+ slot: 0,
2655
+ userinfo: b"",
2656
+ limit: 0,
2657
+ occupancy: 0,
2658
+ seconds: 0,
2659
+ clock: 0,
2660
+ ticks: 0,
2661
+ handle: 0,
2662
+ client_id: 0,
2663
+ enabled: 0,
2664
+ mapname: b"m",
2665
+ file_length: 0,
2666
+ file_data: b"",
2667
+ },
2668
+ session: b"",
2669
+ ignore_text: b"",
2670
+ scratch: [0; 5],
2671
+ tail: ConnectTailInput {
2672
+ mode: 0,
2673
+ slot: 0,
2674
+ first_time: 0,
2675
+ bot: 0,
2676
+ bot_answer: 1,
2677
+ clock: 0,
2678
+ seconds: 0,
2679
+ after_team: None,
2680
+ name: b"n",
2681
+ },
2682
+ },
2683
+ &mut ring,
2684
+ );
2685
+ assert_eq!(result, Err(Error::InvalidInput));
2686
+ assert_eq!(entity, before);
2687
+ assert_eq!(ring, 0);
2688
+ }
2689
+ }
docs/FINDINGS.md CHANGED
@@ -1766,3 +1766,6 @@ RE-813–822 replaces the scoreboard decimal assumption with actual37914 executi
1766
 
1767
 
1768
  **RE-922:** 96 complete173979 executions compose actual userinfo/address/password admission, native client-clear boundary,247520 initialization or247219 restore, team normalization and bot/callback completion. Ordinary authentication rejection preserves client/entity state; bot rejection occurs after lifecycle side effects. Initializer-persisted timeout can precede a first-time overwrite. Client-wide checksums, watched bytes, exact messages, cvar order and selector/formatter carry are verified. Native admission/userinfo/team/ranking/IO/network callbacks remain supplied/captured; this is not native multiplayer acceptance. [Evidence](research/QVM_CONNECT_COMPLETE.md).
 
 
 
 
1766
 
1767
 
1768
  **RE-922:** 96 complete173979 executions compose actual userinfo/address/password admission, native client-clear boundary,247520 initialization or247219 restore, team normalization and bot/callback completion. Ordinary authentication rejection preserves client/entity state; bot rejection occurs after lifecycle side effects. Initializer-persisted timeout can precede a first-time overwrite. Client-wide checksums, watched bytes, exact messages, cvar order and selector/formatter carry are verified. Native admission/userinfo/team/ranking/IO/network callbacks remain supplied/captured; this is not native multiplayer acceptance. [Evidence](research/QVM_CONNECT_COMPLETE.md).
1769
+
1770
+
1771
+ **RE-923:** Rust matches96 complete173979 executions across admission, client clear, initialization/restore, bot admission and callback completion. Auth denial preserves client/entity state; bot denial retains lifecycle effects. Source messages, cvar timing, full-client checksums, raw watched bytes and selector/formatter carry match. Native callback bodies, actual network/IO acceptance and runtime integration remain open. [Evidence](research/RUST_CONNECT_COMPLETE.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1806,3 +1806,9 @@ RE-921: Rust matches144 original173979 suffix cases with ordered bot admission,
1806
 
1807
 
1808
  RE-922: 96 complete173979 executions compose actual userinfo/address/password admission, native client-clear boundary,247520 initialization or247219 restore, team normalization and bot/callback completion. Ordinary authentication rejection preserves client/entity state; bot rejection occurs after lifecycle side effects. Initializer-persisted timeout can precede a first-time overwrite. Client-wide checksums, watched bytes, exact messages, cvar order and selector/formatter carry are verified. Native admission/userinfo/team/ranking/IO/network callbacks remain supplied/captured; this is not native multiplayer acceptance. Full runtime completion remains open. [Evidence](research/QVM_CONNECT_COMPLETE.md).
 
 
 
 
 
 
 
1806
 
1807
 
1808
  RE-922: 96 complete173979 executions compose actual userinfo/address/password admission, native client-clear boundary,247520 initialization or247219 restore, team normalization and bot/callback completion. Ordinary authentication rejection preserves client/entity state; bot rejection occurs after lifecycle side effects. Initializer-persisted timeout can precede a first-time overwrite. Client-wide checksums, watched bytes, exact messages, cvar order and selector/formatter carry are verified. Native admission/userinfo/team/ranking/IO/network callbacks remain supplied/captured; this is not native multiplayer acceptance. Full runtime completion remains open. [Evidence](research/QVM_CONNECT_COMPLETE.md).
1809
+
1810
+
1811
+ RE-923: Rust matches96 complete173979 executions across admission, client clear, initialization/restore, bot admission and callback completion. Auth denial preserves client/entity state; bot denial retains lifecycle effects. Source messages, cvar timing, full-client checksums, raw watched bytes and selector/formatter carry match. Native callback bodies, actual network/IO acceptance and runtime integration remain open. Full runtime completion remains open. [Evidence](research/RUST_CONNECT_COMPLETE.md).
1812
+
1813
+
1814
+ RE-914–923 completed bounded comparison/filter/admission/completion and whole173979 caller composition. RE-924 tracks concrete callback bodies, filter writers, native engine ownership and full runtime integration. [Batch and next work](research/QVM_CONNECT_POLICY_BATCH.md).
docs/RESEARCH_LEDGER.md CHANGED
@@ -2335,3 +2335,13 @@ Rust matches144 original173979 suffix cases with ordered bot admission, first-ti
2335
  ## 2026-10-28 — RE-922 Complete original connect caller with recovered admission/lifecycle/tail
2336
 
2337
  96 complete173979 executions compose actual userinfo/address/password admission, native client-clear boundary,247520 initialization or247219 restore, team normalization and bot/callback completion. Ordinary authentication rejection preserves client/entity state; bot rejection occurs after lifecycle side effects. Initializer-persisted timeout can precede a first-time overwrite. Client-wide checksums, watched bytes, exact messages, cvar order and selector/formatter carry are verified. Native admission/userinfo/team/ranking/IO/network callbacks remain supplied/captured; this is not native multiplayer acceptance. Focused `cargo xtask test-group qvm-connect-complete` passes (13 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.
 
 
 
 
 
 
 
 
 
 
 
2335
  ## 2026-10-28 — RE-922 Complete original connect caller with recovered admission/lifecycle/tail
2336
 
2337
  96 complete173979 executions compose actual userinfo/address/password admission, native client-clear boundary,247520 initialization or247219 restore, team normalization and bot/callback completion. Ordinary authentication rejection preserves client/entity state; bot rejection occurs after lifecycle side effects. Initializer-persisted timeout can precede a first-time overwrite. Client-wide checksums, watched bytes, exact messages, cvar order and selector/formatter carry are verified. Native admission/userinfo/team/ranking/IO/network callbacks remain supplied/captured; this is not native multiplayer acceptance. Focused `cargo xtask test-group qvm-connect-complete` passes (13 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.
2338
+
2339
+
2340
+ ## 2026-10-28 — RE-923 Rust complete connect policy composition
2341
+
2342
+ Rust matches96 complete173979 executions across admission, client clear, initialization/restore, bot admission and callback completion. Auth denial preserves client/entity state; bot denial retains lifecycle effects. Source messages, cvar timing, full-client checksums, raw watched bytes and selector/formatter carry match. Native callback bodies, actual network/IO acceptance and runtime integration remain open. Focused `cargo xtask test-group rust-connect-complete` 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.
2343
+
2344
+
2345
+ ### RE-914–923 full batch validation (2026-10-28)
2346
+
2347
+ After the tenth commit, `cargo xtask test-full --since 2b1ac7ec` passed on its first full attempt with the flake-declared Nix Python: 1,783 Python tests,216 Rust tests (assets10, gameplay176, xtask30),32 affected report producers regenerated twice deterministically, storage policy,491 documentation link checks and whitespace guards. No generated-report drift. Ten task commits since2b1ac7ec; validation notes are amended into the tenth commit. [Findings and next work](research/QVM_CONNECT_POLICY_BATCH.md).
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -1815,7 +1815,7 @@ Purpose: turn the supplied installation into an evidence-backed specification of
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)).
1819
 
1820
 
1821
  - [x] RE-914 Original unsigned byte string comparison ([evidence](research/QVM_BYTE_COMPARE.md)).
@@ -1843,3 +1843,9 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1843
 
1844
 
1845
  - [x] RE-922 Complete original connect caller with recovered admission/lifecycle/tail ([evidence](research/QVM_CONNECT_COMPLETE.md)).
 
 
 
 
 
 
 
1815
  - [x] RE-913 Rust connect clear and session lifecycle branch ([evidence](research/RUST_CONNECT_SESSION.md)).
1816
 
1817
 
1818
+ - [ ] Follow-up after RE-913: complete connect authentication/normalization, native IO/reload, scanner/alias domains and full runtime integration ([next work](research/QVM_SESSION_LIFECYCLE_BATCH.md)).
1819
 
1820
 
1821
  - [x] RE-914 Original unsigned byte string comparison ([evidence](research/QVM_BYTE_COMPARE.md)).
 
1843
 
1844
 
1845
  - [x] RE-922 Complete original connect caller with recovered admission/lifecycle/tail ([evidence](research/QVM_CONNECT_COMPLETE.md)).
1846
+
1847
+
1848
+ - [x] RE-923 Rust complete connect policy composition ([evidence](research/RUST_CONNECT_COMPLETE.md)).
1849
+
1850
+
1851
+ - [ ] RE-924 Recover bot/userinfo/team/ranking callback bodies, filter writers and native engine acceptance, with remaining scalar/alias and full runtime integration ([next work](research/QVM_CONNECT_POLICY_BATCH.md)).
docs/TESTING.md CHANGED
@@ -169,3 +169,6 @@ RE-894–903 adds focused groups `qvm-info-lookup`, `rust-info-lookup`, `qvm-num
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).
 
 
 
 
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).
172
+
173
+
174
+ RE-914–923 adds `qvm-byte-compare`, `rust-byte-compare`, `qvm-address-filter`, `rust-address-filter`, `qvm-connect-admission`, `rust-connect-admission`, `qvm-connect-tail`, `rust-connect-tail`, `qvm-connect-complete` and `rust-connect-complete`. Invoke one group per command. Full validation after task ten uses `cargo xtask test-full --since 2b1ac7ec` with explicit flake-declared Nix PYTHON. [Bounded connect evidence](research/QVM_CONNECT_POLICY_BATCH.md).
docs/research/QVM_CONNECT_POLICY_BATCH.md ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Complete connect caller policy: RE-914–923
2
+
3
+ Ten separately committed tasks recover admission and completion policies and compose the entire original173979 QVM caller across926 paired source/Rust cases. The caller body executes, while bot admission, userinfo changes, team joining, ranking, engine buffers, files, logging and network sends remain supplied/captured boundaries. These finite policies do not establish native multiplayer acceptance or a complete playable game.
4
+
5
+ | Task | Evidence | Cases |
6
+ | --- | --- | ---: |
7
+ | RE-914 | [Unsigned36306 comparison](QVM_BYTE_COMPARE.md) | 256 |
8
+ | RE-915 | [Rust exact byte comparison](RUST_BYTE_COMPARE.md) | 256 |
9
+ | RE-916 | [Address252643 parsing/filtering](QVM_ADDRESS_FILTER.md) | 270 |
10
+ | RE-917 | [Rust address/scratch policy](RUST_ADDRESS_FILTER.md) | 270 |
11
+ | RE-918 | [173979 admission prefix](QVM_CONNECT_ADMISSION.md) | 160 |
12
+ | RE-919 | [Rust admission order](RUST_CONNECT_ADMISSION.md) | 160 |
13
+ | RE-920 | [173979 completion suffix](QVM_CONNECT_TAIL.md) | 144 |
14
+ | RE-921 | [Rust completion policy](RUST_CONNECT_TAIL.md) | 144 |
15
+ | RE-922 | [Complete173979 caller](QVM_CONNECT_COMPLETE.md) | 96 |
16
+ | RE-923 | [Rust complete caller composition](RUST_CONNECT_COMPLETE.md) | 96 |
17
+
18
+ ## Strings, addresses and admission
19
+
20
+ Actual36306 compares words but returns the unsigned-byte difference at the first differing character. It is case-sensitive, terminates at NUL and is distinct from the previously recovered69386 case-folded comparison. Fixtures cover four pointer alignments, embedded NUL, case and high-byte differences. Guard bytes are explicit fixture memory; arbitrary pointer validity/aliases remain open.
21
+
22
+ Actual252643 first compares the input with exact lowercase localhost and bypasses filtering on that match. Otherwise it constructs up to four bytes at a caller-stack word. Each numeric component wraps modulo256; any single non-digit separator advances the component index, while colon or NUL ends parsing. Short/empty strings leave explicitly supplied prior bytes in untouched components. Thus this body is not strict IP validation. The result is a little-endian word tested against mask/value entries at36275952/36275956 with stride12 and signed count36275948. Nonzero34082796 returns true on a match; zero mode returns true on a miss. Negative counts skip scanning. Table population, raw network provenance and actual scratch provenance remain open.
23
+
24
+ The173979 prefix fetches userinfo first. A nonzero bot argument bypasses both address and password checks. Other clients look up ip, invoke the actual filter and return exact Banned. text before any password lookup when rejected. If permitted, password is looked up even when the server password is empty. Nonempty server passwords are compared case-insensitively with none; other values invoke exact36306 comparison against the supplied password. Mismatch returns Invalid password. Rejection precedes entity/client initialization. Userinfo buffer selectors carry in the actual lookup order.
25
+
26
+ ## Completion ordering and partial rejection state
27
+
28
+ After the recovered initialize/restore branch, the caller normalizes team3096: team-classified modes replace zero with3; other modes replace values other than3 with0. Bot paths then set entity flag8 and in-use1 before requesting161744 with restart=!firstTime. A zero supplied result returns exact BotConnectfailed text after those mutations. The Rust policy preserves that ordinary rejection state; typed unsupported-domain errors stage state instead.
29
+
30
+ Successful first-time paths clear client668..695 and compute timeout3140 as clock+seconds*1000, with no initializer60-second default. They log, clear3588 and the3204..3587 message regions, then request171668 userinfo processing. Fixtures supply the resulting team/name changes. The first-time connection announcement uses that updated name and exact original70458/37914 formatting: the logical native command includes its print wrapper, quotes and newline. The Rust boundary removes the formatter's terminating NUL from its logical command bytes. A second team classification/decision observes the updated team, optionally requests182896 with choice-1, and5864 ranking is requested last. Native callback bodies and their arbitrary global effects remain open.
31
+
32
+ ## Whole caller composition
33
+
34
+ The96 complete original173979 executions combine admission, client/entity clear, actual247520 initialization or247219 restore, bot admission and completion. The corpus includes ordinary auth denial, localhost, partial addresses with supplied scratch, bot bypass/failure, negative new-session control, both lifecycle choices and mode0/3/9. Full6336-byte client checksums accompany watched words, the944-byte session block, entity state, exact messages, ordered events/cvar writes/reads and selector/formatter carry. The checksum is an FNV-1a coverage check, not a cryptographic identity claim.
35
+
36
+ Authentication denial leaves client/entity state intact. Bot rejection occurs later and retains prior lifecycle effects, including cvar/file intentions. Initialization can serialize timeout60100 before a first-time suffix overwrites the in-memory value with100; no second persistence write occurs in this caller. Stable engine userinfo snapshots are supplied across nested reads in this corpus. Mutating globals/counts between callbacks, raw VM pointer aliasing and native engine resource/network behavior remain outside the verified domain.
37
+
38
+ The new helper qvm_connect_policy_support.py has a distinct filename. Original packed string regions are seeded directly from the verified QVM image, including initializer literals needed by the whole-call composition. Independent Python state/event oracles and Rust parity caught logical string boundary issues before commit. No original game binary or asset payload was modified.
39
+
40
+ ## Validation
41
+
42
+ Every task ran focused `cargo xtask test-group <name>` before its own commit, with deterministic double regeneration, relevant source/Rust parity, scoped gameplay tests and storage/documentation/whitespace checks. Groups are qvm-byte-compare, rust-byte-compare, qvm-address-filter, rust-address-filter, qvm-connect-admission, rust-connect-admission, qvm-connect-tail, rust-connect-tail, qvm-connect-complete and rust-connect-complete. Shared admission/address/tail helpers also received focused regression checks.
43
+
44
+ Dependency-selection regression coverage includes the five new source/Rust report pairs, native_policy consumers and intermission/formatting dependencies. The existing health/session helper import smoke check remains in place. Every Python process uses the resolved Nix runtime declared by flake.nix, with explicit PYTHON for xtask and Git hooks. After commit ten, `cargo xtask test-full --since 2b1ac7ec` passed on its first full attempt: 1,783 Python tests and216 Rust tests (assets10, gameplay176, xtask30). All32 affected report producers regenerated twice deterministically. Storage policy, links across491 documentation files and whitespace checks passed with no generated-report drift. Validation notes were amended into task ten.
45
+
46
+ ## Next work: RE-924
47
+
48
+ - [ ] Recover161744 bot admission,171668 userinfo mutation and182896 team-join callback bodies, then compose them with173979 and actual5864 ranking/publication.
49
+ - [ ] Recover address-filter table writers and native IP/userinfo provenance; verify partial-address stack provenance, aliases, malformed counts and live configuration changes.
50
+ - [ ] Recover native logging/network/cvar/file-open and handle ownership, attach real engine adapters and validate actual connect/reload/failure behavior on macOS/Linux.
51
+ - [ ] Extend scalar/string domains: non-ASCII tails, uppercase exponents, remaining scanf conversions, oversized/fatal strings and native pointer boundaries.
52
+ - [ ] Compose both persistence children into series6939/readiness, native command dispatch, bot rotation/genetics and221662 map-list parsing.
53
+ - [ ] Replace supplied spatial-query answers with recovered native broadphase/world collision and compare native math.
54
+ - [ ] Compose complete respawn/intermission, event lifetime and scoreboard/network execution.
55
+ - [ ] Validate actual Linux/macOS Rust/Bevy/Solari gameplay/rendering. Full-game completion remains open.
docs/research/QVM_SESSION_LIFECYCLE_BATCH.md CHANGED
@@ -43,7 +43,7 @@ Focused `cargo xtask test-group <name>` ran before each commit, with selected re
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.
@@ -53,3 +53,5 @@ The dependency-selection regression covers the five source/Rust report pairs, na
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.
 
 
 
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
+ ## Follow-up work (expanded by RE-914–923)
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.
 
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.
56
+
57
+ [RE-914–923](QVM_CONNECT_POLICY_BATCH.md) recovers admission/completion and composes the complete bounded173979 caller. Its RE-924 list supersedes the historical follow-up list above.
docs/research/README.md CHANGED
@@ -1378,3 +1378,9 @@ The extension collector adds five focused tests for native tail calls, command-r
1378
 
1379
 
1380
  [RE-922: Complete original connect caller with recovered admission/lifecycle/tail](QVM_CONNECT_COMPLETE.md).
 
 
 
 
 
 
 
1378
 
1379
 
1380
  [RE-922: Complete original connect caller with recovered admission/lifecycle/tail](QVM_CONNECT_COMPLETE.md).
1381
+
1382
+
1383
+ [RE-923: Rust complete connect policy composition](RUST_CONNECT_COMPLETE.md).
1384
+
1385
+
1386
+ [RE-914–923 complete connect caller policy batch](QVM_CONNECT_POLICY_BATCH.md).
docs/research/RUST_CONNECT_COMPLETE.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Rust complete connect policy composition
2
+
3
+ RE-923. Reproduce with `python3 scripts/analyze_rust_connect_complete.py`; focused validation: `cargo xtask test-group rust-connect-complete`. [Machine report](rust-connect-complete.json).
4
+
5
+ Rust matches96 complete173979 executions across admission, client clear, initialization/restore, bot admission and callback completion. Auth denial preserves client/entity state; bot denial retains lifecycle effects. Source messages, cvar timing, full-client checksums, raw watched bytes and selector/formatter carry match. Native callback bodies, actual network/IO acceptance and runtime integration remain open.
6
+
7
+ Original executable/native runtime and complete Linux/macOS Rust/Bevy/Solari acceptance remain unverified.
docs/research/rust-connect-complete.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": 175,
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": 176,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
scripts/analyze_rust_connect_complete.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 blob(s):return [len(bytes.fromhex(s)),*bytes.fromhex(s)]
7
+ def encode(s):
8
+ a=s['input'];return [a[k] for k in ['mode','first','new','bot','answer','scratch','filter_scratch','changed']]+[len(a['entries']),*[v for row in a['entries'] for v in row],*blob(a['userinfo']),*blob(a['password'])]
9
+ def project(s):return {k:s[k] for k in ['accepted','message','steps','filter','events','choice','raw','client_checksum','words','entity','sets','reads','ring','info_ring']}
10
+
11
+ def main():
12
+ b=binary();p=Path('docs/research/qvm-connect-complete.json');data=p.read_bytes();rows=[]
13
+ for s in json.loads(data)['probes']:rows.append({'source':project(s),'rust':result(b,'connect-complete',encode(s))})
14
+ write_json(Path('docs/research/rust-connect-complete.json'),{'source_report':str(p),'source_report_sha256':hashlib.sha256(data).hexdigest(),'probes':rows,'limits':'Rust matches96 complete173979 executions across admission, client clear, initialization/restore, bot admission and callback completion. Auth denial preserves client/entity state; bot denial retains lifecycle effects. Source messages, cvar timing, full-client checksums, raw watched bytes and selector/formatter carry match. Native callback bodies, actual network/IO acceptance and runtime integration remain open.'})
15
+ if __name__=='__main__':main()
scripts/test_rust_connect_complete.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-complete.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-complete.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
@@ -2990,3 +2990,18 @@ edge scripts/analyze_qvm_connect_complete.py scripts/analyze_qvm_resumer_audit.p
2990
  edge scripts/qvm_connect_complete_oracle.py scripts/analyze_qvm_resumer_audit.py
2991
  group qvm-connect-complete scripts/analyze_qvm_connect_admission.py
2992
  group qvm-connect-complete scripts/analyze_qvm_address_filter.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2990
  edge scripts/qvm_connect_complete_oracle.py scripts/analyze_qvm_resumer_audit.py
2991
  group qvm-connect-complete scripts/analyze_qvm_connect_admission.py
2992
  group qvm-connect-complete scripts/analyze_qvm_address_filter.py
2993
+ edge Cargo.toml scripts/analyze_rust_connect_complete.py
2994
+ edge Cargo.lock scripts/analyze_rust_connect_complete.py
2995
+ edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_connect_complete.py
2996
+ edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_connect_complete.py
2997
+ edge crates/urt-gameplay/src/native_policy.rs scripts/analyze_rust_connect_complete.py
2998
+ edge crates/urt-gameplay/src/bin/urt-native-policy.rs scripts/analyze_rust_connect_complete.py
2999
+ edge docs/research/qvm-connect-complete.json scripts/analyze_rust_connect_complete.py
3000
+ group rust-connect-complete scripts/analyze_rust_health_impulse.py
3001
+ group gameplay scripts/analyze_rust_connect_complete.py
3002
+ edge crates/urt-gameplay/src/intermission.rs scripts/analyze_rust_connect_complete.py
3003
+ edge crates/urt-gameplay/src/qvm_format.rs scripts/analyze_rust_connect_complete.py
3004
+ report scripts/analyze_rust_connect_complete.py docs/research/rust-connect-complete.json
3005
+ edge docs/research/rust-connect-complete.json scripts/test_rust_connect_complete.py
3006
+ group rust-connect-complete scripts/analyze_rust_connect_complete.py
3007
+ edge scripts/analyze_rust_connect_complete.py scripts/analyze_qvm_resumer_audit.py
xtask/src/lib.rs CHANGED
@@ -1117,6 +1117,12 @@ mod integration_tests {
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"),
@@ -1196,6 +1202,11 @@ mod integration_tests {
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"),
 
1117
  ("map_commands", "normal_map_files"),
1118
  ("transition", "normal_map_files"),
1119
  ("intermission", "connect_session"),
1120
+ ("native_policy", "byte_compare"),
1121
+ ("native_policy", "address_filter"),
1122
+ ("native_policy", "connect_admission"),
1123
+ ("native_policy", "connect_tail"),
1124
+ ("native_policy", "connect_complete"),
1125
+ ("intermission", "connect_complete"),
1126
  ("protected", "protected_update"),
1127
  ("mode10", "mode10_entry"),
1128
  ("intercept_animation", "mode10_animation"),
 
1202
  ("qvm-normal-map-files", "normal_map_files"),
1203
  ("qvm-persistence-roundtrip", "persistence_roundtrip"),
1204
  ("qvm-connect-session", "connect_session"),
1205
+ ("qvm-byte-compare", "byte_compare"),
1206
+ ("qvm-address-filter", "address_filter"),
1207
+ ("qvm-connect-admission", "connect_admission"),
1208
+ ("qvm-connect-tail", "connect_tail"),
1209
+ ("qvm-connect-complete", "connect_complete"),
1210
  ("qvm-rank-small-sort", "rank_sort"),
1211
  ("qvm-rank-large-sort", "rank_sort"),
1212
  ("qvm-ranking-complete", "ranking_complete"),