nmarshall commited on
Commit
c7ae523
·
1 Parent(s): cd738dd

gameplay: integrate post-map session persistence

Browse files
crates/urt-gameplay/src/bin/urt-native-policy.rs CHANGED
@@ -208,6 +208,29 @@ fn run() -> Result<String, String> {
208
  out.ring_advance
209
  ))
210
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
  _ => Err("unknown command".into()),
212
  }
213
  }
@@ -264,3 +287,45 @@ fn sets_json(sets: &[(Vec<u8>, Vec<u8>)]) -> String {
264
  .join(",")
265
  )
266
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208
  out.ring_advance
209
  ))
210
  }
211
+ "post-session" => {
212
+ if w.len() != 210 {
213
+ return Err("invalid post session input".into());
214
+ }
215
+ let count = w[0] as i32;
216
+ let mutation = w[1];
217
+ let indices = w[2..66].iter().map(|v| *v as usize).collect::<Vec<_>>();
218
+ let flags = std::array::from_fn(|j| w[66 + j] as i32);
219
+ let mut clients = w[130..210]
220
+ .chunks_exact(10)
221
+ .map(|v| SessionClient {
222
+ connection: v[0] as i32,
223
+ values: std::array::from_fn(|j| v[1 + j] as i32),
224
+ ignore: flags,
225
+ })
226
+ .collect::<Vec<_>>();
227
+ let out = persist_post_map(&mut clients, count, &indices, |j, c| {
228
+ apply_session_change(mutation, j, c);
229
+ Ok(())
230
+ })
231
+ .map_err(|e| format!("{e:?}"))?;
232
+ Ok(post_session_json(&out, &clients))
233
+ }
234
  _ => Err("unknown command".into()),
235
  }
236
  }
 
287
  .join(",")
288
  )
289
  }
290
+
291
+ fn apply_session_change(mutation: u32, j: usize, c: &mut [SessionClient]) {
292
+ match mutation {
293
+ 1 => c[j].connection = 0,
294
+ 2 => {
295
+ if j + 1 < c.len() {
296
+ c[j + 1].connection = 2
297
+ }
298
+ }
299
+ 3 => c[j].values[0] = 777 + j as i32,
300
+ _ => {}
301
+ }
302
+ }
303
+ fn post_session_json(out: &PostSession, c: &[SessionClient]) -> String {
304
+ let calls = out
305
+ .calls
306
+ .iter()
307
+ .map(|(k, j, v)| {
308
+ vec![
309
+ if *k == PostMapChild::First {
310
+ 246746
311
+ } else {
312
+ 246963
313
+ },
314
+ *j as u32,
315
+ *v as u32,
316
+ ]
317
+ })
318
+ .collect::<Vec<_>>();
319
+ let values = c
320
+ .iter()
321
+ .map(|c| c.values.map(|v| v as u32).to_vec())
322
+ .collect::<Vec<_>>();
323
+ format!(
324
+ "{{\"calls\":{},\"sets\":{},\"connections\":{},\"values\":{},\"ring\":{}}}",
325
+ nested(&calls),
326
+ sets_json(&out.sets),
327
+ list(&c.iter().map(|c| c.connection as u32).collect::<Vec<_>>()),
328
+ nested(&values),
329
+ out.ring_advance
330
+ )
331
+ }
crates/urt-gameplay/src/native_policy.rs CHANGED
@@ -458,3 +458,95 @@ mod session_tests {
458
  );
459
  }
460
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
458
  );
459
  }
460
  }
461
+
462
+ #[derive(Clone, Debug, PartialEq, Eq)]
463
+ pub struct SessionClient {
464
+ pub connection: i32,
465
+ pub values: [i32; 9],
466
+ pub ignore: [i32; 64],
467
+ }
468
+ #[derive(Clone, Debug, PartialEq, Eq)]
469
+ pub struct PostSession {
470
+ pub calls: Vec<(PostMapChild, usize, i32)>,
471
+ pub sets: Vec<(Vec<u8>, Vec<u8>)>,
472
+ pub ring_advance: u32,
473
+ }
474
+ /// Compose249007 and246963. First-child effects are supplied, applied before
475
+ /// serialization, and staged together with client changes on callback errors.
476
+ pub fn persist_post_map<F: FnMut(usize, &mut [SessionClient]) -> Result<(), Error>>(
477
+ clients: &mut [SessionClient],
478
+ count: i32,
479
+ entity_clients: &[usize],
480
+ mut first: F,
481
+ ) -> Result<PostSession, Error> {
482
+ let n = count.max(0) as usize;
483
+ if clients.len() > 64 || n > clients.len() || entity_clients.len() < n {
484
+ return Err(Error::InvalidCount);
485
+ }
486
+ if entity_clients[..n].iter().any(|j| *j >= 64) {
487
+ return Err(Error::InvalidIndex);
488
+ }
489
+ let mut staged = clients.to_vec();
490
+ let mut out = PostSession {
491
+ calls: vec![],
492
+ sets: vec![],
493
+ ring_advance: 0,
494
+ };
495
+ for j in 0..n {
496
+ if staged[j].connection != 2 {
497
+ continue;
498
+ }
499
+ out.calls
500
+ .push((PostMapChild::First, j, staged[j].connection));
501
+ first(j, &mut staged)?;
502
+ out.calls
503
+ .push((PostMapChild::Second, j, staged[j].connection));
504
+ let written = session_write(
505
+ j,
506
+ count,
507
+ staged[j].values,
508
+ entity_clients,
509
+ &staged[j].ignore,
510
+ )?;
511
+ out.sets.extend(written.sets);
512
+ out.ring_advance = out.ring_advance.wrapping_add(written.ring_advance);
513
+ }
514
+ clients.clone_from_slice(&staged);
515
+ Ok(out)
516
+ }
517
+ #[cfg(test)]
518
+ mod post_session_tests {
519
+ use super::*;
520
+ fn client() -> SessionClient {
521
+ SessionClient {
522
+ connection: 2,
523
+ values: [0; 9],
524
+ ignore: [0; 64],
525
+ }
526
+ }
527
+ #[test]
528
+ fn callback_fields_and_disconnect_persist() {
529
+ let mut c = [client()];
530
+ let out = persist_post_map(&mut c, 1, &[0], |j, c| {
531
+ c[j].connection = 0;
532
+ c[j].values[0] = 77;
533
+ Ok(())
534
+ })
535
+ .unwrap();
536
+ assert_eq!(out.sets[0].1, b"77,0,0,0,0,0,0,0,0");
537
+ assert_eq!(out.calls[1], (PostMapChild::Second, 0, 0));
538
+ }
539
+ #[test]
540
+ fn callback_error_is_atomic() {
541
+ let mut c = [client()];
542
+ let before = c.clone();
543
+ assert_eq!(
544
+ persist_post_map(&mut c, 1, &[0], |j, c| {
545
+ c[j].connection = 0;
546
+ Err(Error::InvalidInput)
547
+ }),
548
+ Err(Error::InvalidInput)
549
+ );
550
+ assert_eq!(c, before);
551
+ }
552
+ }
docs/FINDINGS.md CHANGED
@@ -1670,3 +1670,6 @@ RE-813–822 replaces the scoreboard decimal assumption with actual37914 executi
1670
 
1671
 
1672
  **RE-890:** 64 continuous249007/246963 compositions execute session cvar serialization after the captured246746 child. Supplied first-child disconnection still allows persistence; later activation affects dispatch, and changed session fields are serialized immediately. Source formatter ring carries between clients. Native first-child behavior, engine cvars and count/base changes remain open. [Evidence](research/QVM_POST_MAP_SESSION.md).
 
 
 
 
1670
 
1671
 
1672
  **RE-890:** 64 continuous249007/246963 compositions execute session cvar serialization after the captured246746 child. Supplied first-child disconnection still allows persistence; later activation affects dispatch, and changed session fields are serialized immediately. Source formatter ring carries between clients. Native first-child behavior, engine cvars and count/base changes remain open. [Evidence](research/QVM_POST_MAP_SESSION.md).
1673
+
1674
+
1675
+ **RE-891:** Rust matches64 original249007/246963 compositions, including first-child disconnect/activation/field updates, exact cvar bytes and carried formatter-ring advance. Client changes stage atomically on typed callback errors. The first child and engine storage remain caller-owned; native effects and count/base mutation are outside this contract. [Evidence](research/RUST_POST_MAP_SESSION.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1701,3 +1701,6 @@ RE-889: Rust matches60 actual246963 byte-for-byte cvar fixtures, preserving reco
1701
 
1702
 
1703
  RE-890: 64 continuous249007/246963 compositions execute session cvar serialization after the captured246746 child. Supplied first-child disconnection still allows persistence; later activation affects dispatch, and changed session fields are serialized immediately. Source formatter ring carries between clients. Native first-child behavior, engine cvars and count/base changes remain open. Full runtime completion remains open. [Evidence](research/QVM_POST_MAP_SESSION.md).
 
 
 
 
1701
 
1702
 
1703
  RE-890: 64 continuous249007/246963 compositions execute session cvar serialization after the captured246746 child. Supplied first-child disconnection still allows persistence; later activation affects dispatch, and changed session fields are serialized immediately. Source formatter ring carries between clients. Native first-child behavior, engine cvars and count/base changes remain open. Full runtime completion remains open. [Evidence](research/QVM_POST_MAP_SESSION.md).
1704
+
1705
+
1706
+ RE-891: Rust matches64 original249007/246963 compositions, including first-child disconnect/activation/field updates, exact cvar bytes and carried formatter-ring advance. Client changes stage atomically on typed callback errors. The first child and engine storage remain caller-owned; native effects and count/base mutation are outside this contract. Full runtime completion remains open. [Evidence](research/RUST_POST_MAP_SESSION.md).
docs/RESEARCH_LEDGER.md CHANGED
@@ -2160,3 +2160,8 @@ Rust matches60 actual246963 byte-for-byte cvar fixtures, preserving recovered in
2160
  ## 2026-10-28 — RE-890 Original post-map dispatch with session persistence
2161
 
2162
  64 continuous249007/246963 compositions execute session cvar serialization after the captured246746 child. Supplied first-child disconnection still allows persistence; later activation affects dispatch, and changed session fields are serialized immediately. Source formatter ring carries between clients. Native first-child behavior, engine cvars and count/base changes remain open. Focused `cargo xtask test-group qvm-post-map-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.
 
 
 
 
 
 
2160
  ## 2026-10-28 — RE-890 Original post-map dispatch with session persistence
2161
 
2162
  64 continuous249007/246963 compositions execute session cvar serialization after the captured246746 child. Supplied first-child disconnection still allows persistence; later activation affects dispatch, and changed session fields are serialized immediately. Source formatter ring carries between clients. Native first-child behavior, engine cvars and count/base changes remain open. Focused `cargo xtask test-group qvm-post-map-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.
2163
+
2164
+
2165
+ ## 2026-10-28 — RE-891 Rust post-map persistence composition
2166
+
2167
+ Rust matches64 original249007/246963 compositions, including first-child disconnect/activation/field updates, exact cvar bytes and carried formatter-ring advance. Client changes stage atomically on typed callback errors. The first child and engine storage remain caller-owned; native effects and count/base mutation are outside this contract. Focused `cargo xtask test-group rust-post-map-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.
docs/REVERSE_ENGINEERING_TODO.md CHANGED
@@ -1738,3 +1738,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1738
 
1739
 
1740
  - [x] RE-890 Original post-map dispatch with session persistence ([evidence](research/QVM_POST_MAP_SESSION.md)).
 
 
 
 
1738
 
1739
 
1740
  - [x] RE-890 Original post-map dispatch with session persistence ([evidence](research/QVM_POST_MAP_SESSION.md)).
1741
+
1742
+
1743
+ - [x] RE-891 Rust post-map persistence composition ([evidence](research/RUST_POST_MAP_SESSION.md)).
docs/research/README.md CHANGED
@@ -1273,3 +1273,6 @@ The extension collector adds five focused tests for native tail calls, command-r
1273
 
1274
 
1275
  [RE-890: Original post-map dispatch with session persistence](QVM_POST_MAP_SESSION.md).
 
 
 
 
1273
 
1274
 
1275
  [RE-890: Original post-map dispatch with session persistence](QVM_POST_MAP_SESSION.md).
1276
+
1277
+
1278
+ [RE-891: Rust post-map persistence composition](RUST_POST_MAP_SESSION.md).
docs/research/RUST_POST_MAP_SESSION.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Rust post-map persistence composition
2
+
3
+ RE-891. Reproduce with `python3 scripts/analyze_rust_post_map_session.py`; focused validation: `cargo xtask test-group rust-post-map-session`. [Machine report](rust-post-map-session.json).
4
+
5
+ Rust matches64 original249007/246963 compositions, including first-child disconnect/activation/field updates, exact cvar bytes and carried formatter-ring advance. Client changes stage atomically on typed callback errors. The first child and engine storage remain caller-owned; native effects and count/base mutation are outside this contract.
6
+
7
+ Original executable/native runtime and complete Linux/macOS Rust/Bevy/Solari acceptance remain unverified.
docs/research/rust-health-impulse.json CHANGED
@@ -2985,7 +2985,7 @@
2985
  "exit_code": 2
2986
  }
2987
  ],
2988
- "rust_unit_tests": 145,
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": 147,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
docs/research/rust-post-map-session.json ADDED
The diff for this file is too large to render. See raw diff
 
scripts/analyze_rust_post_map_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['count'],a['mutation'],*a['indices'],*a['flags'],*[v for connection,values in zip(a['connections'],a['values']) for v in [connection,*values]]]
8
+ def project(s):return {k:s[k] for k in ['calls','sets','connections','values','ring']}
9
+
10
+ def main():
11
+ b=binary();p=Path('docs/research/qvm-post-map-session.json');data=p.read_bytes();rows=[]
12
+ for s in json.loads(data)['probes']:rows.append({'source':project(s),'rust':result(b,'post-session',encode(s))})
13
+ write_json(Path('docs/research/rust-post-map-session.json'),{'source_report':str(p),'source_report_sha256':hashlib.sha256(data).hexdigest(),'probes':rows,'limits':'Rust matches64 original249007/246963 compositions, including first-child disconnect/activation/field updates, exact cvar bytes and carried formatter-ring advance. Client changes stage atomically on typed callback errors. The first child and engine storage remain caller-owned; native effects and count/base mutation are outside this contract.'})
14
+ if __name__=='__main__':main()
scripts/test_rust_post_map_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-post-map-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-post-map-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
@@ -2671,3 +2671,17 @@ report scripts/analyze_qvm_post_map_session.py docs/research/qvm-post-map-sessio
2671
  edge docs/research/qvm-post-map-session.json scripts/test_qvm_post_map_session.py
2672
  group qvm-post-map-session scripts/analyze_qvm_post_map_session.py
2673
  edge scripts/analyze_qvm_post_map_session.py scripts/analyze_qvm_resumer_audit.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2671
  edge docs/research/qvm-post-map-session.json scripts/test_qvm_post_map_session.py
2672
  group qvm-post-map-session scripts/analyze_qvm_post_map_session.py
2673
  edge scripts/analyze_qvm_post_map_session.py scripts/analyze_qvm_resumer_audit.py
2674
+ edge Cargo.toml scripts/analyze_rust_post_map_session.py
2675
+ edge Cargo.lock scripts/analyze_rust_post_map_session.py
2676
+ edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_post_map_session.py
2677
+ edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_post_map_session.py
2678
+ edge crates/urt-gameplay/src/native_policy.rs scripts/analyze_rust_post_map_session.py
2679
+ edge crates/urt-gameplay/src/bin/urt-native-policy.rs scripts/analyze_rust_post_map_session.py
2680
+ edge docs/research/qvm-post-map-session.json scripts/analyze_rust_post_map_session.py
2681
+ group rust-post-map-session scripts/analyze_rust_health_impulse.py
2682
+ group gameplay scripts/analyze_rust_post_map_session.py
2683
+ edge crates/urt-gameplay/src/qvm_format.rs scripts/analyze_rust_post_map_session.py
2684
+ report scripts/analyze_rust_post_map_session.py docs/research/rust-post-map-session.json
2685
+ edge docs/research/rust-post-map-session.json scripts/test_rust_post_map_session.py
2686
+ group rust-post-map-session scripts/analyze_rust_post_map_session.py
2687
+ edge scripts/analyze_rust_post_map_session.py scripts/analyze_qvm_resumer_audit.py