nmarshall commited on
Commit
dce4f2f
·
1 Parent(s): 0b0d696

gameplay: compose recovered camera fallback

Browse files
crates/urt-gameplay/src/bin/urt-camera-world.rs CHANGED
@@ -304,6 +304,146 @@ fn run() -> Result<String, String> {
304
  Err(e) => Err(format!("{e:?}")),
305
  }
306
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
307
  _ => Err("unknown command".into()),
308
  }
309
  }
 
304
  Err(e) => Err(format!("{e:?}")),
305
  }
306
  }
307
+
308
+ "spawn-camera" => {
309
+ if w.len() < 9 || w[1] > 1 || w[2] > 1024 {
310
+ return Err("invalid camera header".into());
311
+ }
312
+ let mut state = w[0];
313
+ let slot = w[1] as usize;
314
+ let n = w[2] as usize;
315
+ let fallback = urt_gameplay::transition::View {
316
+ origin: w[3..6].try_into().unwrap(),
317
+ angles: w[6..9].try_into().unwrap(),
318
+ };
319
+ let mut p = 9;
320
+ let mut world = vec![];
321
+ for _ in 0..n {
322
+ let v = w.get(p..p + 9).ok_or("missing world record")?;
323
+ p += 9;
324
+ let class = HashEntity {
325
+ in_use: v[0] as i32,
326
+ class_present: v[1] != 0,
327
+ class_hash: v[2],
328
+ };
329
+ let origin = v[3..6].try_into().unwrap();
330
+ let angles = v[6..9].try_into().unwrap();
331
+ let aim = blob(&w, &mut p)?;
332
+ let label = blob(&w, &mut p)?;
333
+ world.push(WorldEntity {
334
+ class,
335
+ origin,
336
+ angles,
337
+ aim,
338
+ label,
339
+ });
340
+ }
341
+ let blocked = w
342
+ .get(p..p + n)
343
+ .ok_or("missing telefrag flags")?
344
+ .iter()
345
+ .map(|v| *v != 0)
346
+ .collect::<Vec<_>>();
347
+ p += n;
348
+ let mut math = math_calls(&w, &mut p)?;
349
+ if p != w.len() {
350
+ return Err("trailing camera words".into());
351
+ }
352
+ let mut entity = urt_gameplay::transition::CameraEntity {
353
+ origin: [0x12345678; 3],
354
+ clear: [0x12345678; 6],
355
+ };
356
+ let blank = urt_gameplay::transition::CameraPlayer {
357
+ movement: 0xaabbccdd,
358
+ origin: [0xaabbccdd; 3],
359
+ velocity: [0xaabbccdd; 3],
360
+ field104: 0xaabbccdd,
361
+ angles: [0xaabbccdd; 3],
362
+ mode3108: 0xaabbccdd,
363
+ field4324: 0xaabbccdd,
364
+ };
365
+ let mut clients = [blank.clone(), blank];
366
+ let (out, spawn) = configure_camera_with_spawns(
367
+ &world,
368
+ &blocked,
369
+ &mut state,
370
+ &mut math,
371
+ &mut entity,
372
+ &mut clients[slot],
373
+ )
374
+ .map_err(|e| format!("{e:?}"))?;
375
+ math.finish().map_err(|e| format!("{e:?}"))?;
376
+ let e = word_object(
377
+ &[4, 8, 92, 96, 100, 156, 160, 180, 460],
378
+ &[
379
+ entity.clear[0],
380
+ entity.clear[1],
381
+ entity.origin[0],
382
+ entity.origin[1],
383
+ entity.origin[2],
384
+ entity.clear[2],
385
+ entity.clear[3],
386
+ entity.clear[4],
387
+ entity.clear[5],
388
+ ],
389
+ );
390
+ let clients = clients
391
+ .iter()
392
+ .map(|p| {
393
+ word_object(
394
+ &[4, 20, 24, 28, 32, 36, 40, 104, 152, 156, 160, 3108, 4324],
395
+ &[
396
+ p.movement,
397
+ p.origin[0],
398
+ p.origin[1],
399
+ p.origin[2],
400
+ p.velocity[0],
401
+ p.velocity[1],
402
+ p.velocity[2],
403
+ p.field104,
404
+ p.angles[0],
405
+ p.angles[1],
406
+ p.angles[2],
407
+ p.mode3108,
408
+ p.field4324,
409
+ ],
410
+ )
411
+ })
412
+ .collect::<Vec<_>>()
413
+ .join(",");
414
+ let effects = out
415
+ .effects
416
+ .iter()
417
+ .map(|e| match e {
418
+ CameraEffect::FindClass => 276433,
419
+ CameraEffect::Fallback => 168114,
420
+ CameraEffect::PickTarget => 276586,
421
+ CameraEffect::Angles => 62944,
422
+ })
423
+ .collect::<Vec<_>>();
424
+ let matches = out.matches.iter().map(|j| *j as u32).collect::<Vec<_>>();
425
+ let diagnostic = match out.diagnostic {
426
+ None => "null",
427
+ Some(TargetDiagnostic::Null) => "\"null\"",
428
+ Some(TargetDiagnostic::Missing) => "\"missing\"",
429
+ };
430
+ let spawn_index = spawn.as_ref().map(|v| v.index as i32).unwrap_or(-1);
431
+ let spawn_candidates = array(
432
+ &spawn
433
+ .as_ref()
434
+ .map(|v| v.candidates.iter().map(|j| *j as u32).collect::<Vec<_>>())
435
+ .unwrap_or_default(),
436
+ );
437
+ Ok(format!(
438
+ "{{\"spawn_index\":{spawn_index},\"spawn_candidates\":{spawn_candidates},\"class_index\":{},\"target_index\":{},\"rng_state\":{state},\"matches\":{},\"diagnostic\":{diagnostic},\"effects\":{},\"view\":{{\"origin\":{},\"angles\":{}}},\"entity\":{e},\"clients\":[{clients}]}}",
439
+ out.class_index.map(|j| j as i32).unwrap_or(-1),
440
+ out.target_index.map(|j| j as i32).unwrap_or(-1),
441
+ array(&matches),
442
+ array(&effects),
443
+ array(&out.view.origin),
444
+ array(&out.view.angles)
445
+ ))
446
+ }
447
  _ => Err("unknown command".into()),
448
  }
449
  }
crates/urt-gameplay/src/camera_world.rs CHANGED
@@ -709,3 +709,128 @@ mod spawn_tests {
709
  assert_eq!(state, 42);
710
  }
711
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
709
  assert_eq!(state, 42);
710
  }
711
  }
712
+
713
+ /// Configure a camera with actual recovered fallback selection. Telefrag flags
714
+ /// are resolved by the caller; math responses remain supplied through AngleMath.
715
+ pub fn configure_camera_with_spawns<M: AngleMath>(
716
+ world: &[WorldEntity],
717
+ blocked: &[bool],
718
+ state: &mut u32,
719
+ math: &mut M,
720
+ entity: &mut crate::transition::CameraEntity,
721
+ player: &mut crate::transition::CameraPlayer,
722
+ ) -> Result<(CameraResolution, Option<SpawnSelection>), Error> {
723
+ if blocked.len() != world.len() {
724
+ return Err(Error::InvalidCount);
725
+ }
726
+ let classes = world.iter().map(|e| e.class).collect::<Vec<_>>();
727
+ if find_class(&classes, world.len(), None, (-2104846030i32) as u32)?.is_some() {
728
+ return Ok((
729
+ configure_camera(
730
+ world,
731
+ state,
732
+ crate::transition::View {
733
+ origin: [0; 3],
734
+ angles: [0; 3],
735
+ },
736
+ math,
737
+ entity,
738
+ player,
739
+ )?,
740
+ None,
741
+ ));
742
+ }
743
+ let spawns = world
744
+ .iter()
745
+ .zip(blocked)
746
+ .map(|(e, b)| SpawnEntity {
747
+ class: e.class,
748
+ blocked: *b,
749
+ origin: e.origin,
750
+ angles: e.angles,
751
+ })
752
+ .collect::<Vec<_>>();
753
+ let selected = fallback_spawn(&spawns, [0; 3], state, math)?;
754
+ let camera = CameraResolution {
755
+ class_index: None,
756
+ target_index: None,
757
+ matches: vec![],
758
+ diagnostic: None,
759
+ effects: vec![CameraEffect::FindClass, CameraEffect::Fallback],
760
+ view: selected.view,
761
+ };
762
+ crate::transition::place_camera(entity, player, selected.view.origin, selected.view.angles);
763
+ Ok((camera, Some(selected)))
764
+ }
765
+ #[cfg(test)]
766
+ mod composed_spawn_tests {
767
+ use super::*;
768
+ use crate::transition::{CameraEntity, CameraPlayer};
769
+ fn player() -> CameraPlayer {
770
+ CameraPlayer {
771
+ movement: 7,
772
+ origin: [7; 3],
773
+ velocity: [7; 3],
774
+ field104: 7,
775
+ angles: [7; 3],
776
+ mode3108: 2,
777
+ field4324: 3,
778
+ }
779
+ }
780
+ #[test]
781
+ fn blocked_spawn_still_places_offset_fallback() {
782
+ let e = WorldEntity {
783
+ class: HashEntity {
784
+ in_use: 1,
785
+ class_present: true,
786
+ class_hash: (-103258081i32) as u32,
787
+ },
788
+ origin: [0, 0, 2.0f32.to_bits()],
789
+ angles: [4; 3],
790
+ aim: None,
791
+ label: None,
792
+ };
793
+ let mut state = 9;
794
+ let mut entity = CameraEntity {
795
+ origin: [7; 3],
796
+ clear: [7; 6],
797
+ };
798
+ let mut player = player();
799
+ let (out, spawn) = configure_camera_with_spawns(
800
+ &[e],
801
+ &[true],
802
+ &mut state,
803
+ &mut HostMath,
804
+ &mut entity,
805
+ &mut player,
806
+ )
807
+ .unwrap();
808
+ assert_eq!(spawn.unwrap().index, 0);
809
+ assert_eq!(out.view.origin[2], 11.0f32.to_bits());
810
+ assert_eq!(entity.origin, out.view.origin);
811
+ assert_eq!(state, 9);
812
+ }
813
+ #[test]
814
+ fn no_spawn_rejects_without_placement() {
815
+ let mut state = 9;
816
+ let mut entity = CameraEntity {
817
+ origin: [7; 3],
818
+ clear: [7; 6],
819
+ };
820
+ let old = entity.clone();
821
+ let mut player = player();
822
+ assert_eq!(
823
+ configure_camera_with_spawns(
824
+ &[],
825
+ &[],
826
+ &mut state,
827
+ &mut HostMath,
828
+ &mut entity,
829
+ &mut player
830
+ ),
831
+ Err(Error::NoSpawn)
832
+ );
833
+ assert_eq!(entity, old);
834
+ assert_eq!(state, 9);
835
+ }
836
+ }
docs/FINDINGS.md CHANGED
@@ -1598,3 +1598,6 @@ RE-813–822 replaces the scoreboard decimal assumption with actual37914 executi
1598
 
1599
 
1600
  **RE-866:** 128 coherent setup→placement cases replace supplied fallback view writes with actual168114 selection, composing searches, capped distance ranking, RNG and angle execution. Existing intermission-camera paths and two player indirections are checked together. Telefrag answers and finite native math remain supplied; respawn/runtime integration is open. [Evidence](research/QVM_CAMERA_SPAWN.md).
 
 
 
 
1598
 
1599
 
1600
  **RE-866:** 128 coherent setup→placement cases replace supplied fallback view writes with actual168114 selection, composing searches, capped distance ranking, RNG and angle execution. Existing intermission-camera paths and two player indirections are checked together. Telefrag answers and finite native math remain supplied; respawn/runtime integration is open. [Evidence](research/QVM_CAMERA_SPAWN.md).
1601
+
1602
+
1603
+ **RE-867:** Rust matches128 continuous source camera/fallback/placement cases with shared RNG state and checked native-math transcripts. The supplied fallback view has been replaced by recovered spawn ranking and +9 Z. Telefrag answers stay caller-owned; no-spawn errors preserve placement state. Native collision/math and runtime integration remain open. [Evidence](research/RUST_CAMERA_SPAWN.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1623,3 +1623,6 @@ RE-865: Rust matches112 original fallback cases with checked distance-math respo
1623
 
1624
 
1625
  RE-866: 128 coherent setup→placement cases replace supplied fallback view writes with actual168114 selection, composing searches, capped distance ranking, RNG and angle execution. Existing intermission-camera paths and two player indirections are checked together. Telefrag answers and finite native math remain supplied; respawn/runtime integration is open. Full runtime completion remains open. [Evidence](research/QVM_CAMERA_SPAWN.md).
 
 
 
 
1623
 
1624
 
1625
  RE-866: 128 coherent setup→placement cases replace supplied fallback view writes with actual168114 selection, composing searches, capped distance ranking, RNG and angle execution. Existing intermission-camera paths and two player indirections are checked together. Telefrag answers and finite native math remain supplied; respawn/runtime integration is open. Full runtime completion remains open. [Evidence](research/QVM_CAMERA_SPAWN.md).
1626
+
1627
+
1628
+ RE-867: Rust matches128 continuous source camera/fallback/placement cases with shared RNG state and checked native-math transcripts. The supplied fallback view has been replaced by recovered spawn ranking and +9 Z. Telefrag answers stay caller-owned; no-spawn errors preserve placement state. Native collision/math and runtime integration remain open. Full runtime completion remains open. [Evidence](research/RUST_CAMERA_SPAWN.md).
docs/RESEARCH_LEDGER.md CHANGED
@@ -2031,3 +2031,8 @@ Rust matches112 original fallback cases with checked distance-math responses, st
2031
  128 coherent setup→placement cases replace supplied fallback view writes with actual168114 selection, composing searches, capped distance ranking, RNG and angle execution. Existing intermission-camera paths and two player indirections are checked together. Telefrag answers and finite native math remain supplied; respawn/runtime integration is open. Focused `cargo xtask test-group camera-spawn` passes (5 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.
2032
 
2033
  RE-866 review: expanded the standalone source fallback grid to80 entities so filtering still leaves more than64 matches, and asserted actual cap coverage. Focused spawn-fallback regeneration and downstream Rust parity checks pass (9 Python tests); existing case count remains112.
 
 
 
 
 
 
2031
  128 coherent setup→placement cases replace supplied fallback view writes with actual168114 selection, composing searches, capped distance ranking, RNG and angle execution. Existing intermission-camera paths and two player indirections are checked together. Telefrag answers and finite native math remain supplied; respawn/runtime integration is open. Focused `cargo xtask test-group camera-spawn` passes (5 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.
2032
 
2033
  RE-866 review: expanded the standalone source fallback grid to80 entities so filtering still leaves more than64 matches, and asserted actual cap coverage. Focused spawn-fallback regeneration and downstream Rust parity checks pass (9 Python tests); existing case count remains112.
2034
+
2035
+
2036
+ ## 2026-10-27 — RE-867 Rust camera composition with recovered fallback
2037
+
2038
+ Rust matches128 continuous source camera/fallback/placement cases with shared RNG state and checked native-math transcripts. The supplied fallback view has been replaced by recovered spawn ranking and +9 Z. Telefrag answers stay caller-owned; no-spawn errors preserve placement state. Native collision/math and runtime integration remain open. Focused `cargo xtask test-group rust-camera-spawn` 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
@@ -1660,3 +1660,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1660
 
1661
 
1662
  - [x] RE-866 Original camera setup with actual fallback spawn selection ([evidence](research/QVM_CAMERA_SPAWN.md)).
 
 
 
 
1660
 
1661
 
1662
  - [x] RE-866 Original camera setup with actual fallback spawn selection ([evidence](research/QVM_CAMERA_SPAWN.md)).
1663
+
1664
+
1665
+ - [x] RE-867 Rust camera composition with recovered fallback ([evidence](research/RUST_CAMERA_SPAWN.md)).
docs/research/README.md CHANGED
@@ -1196,3 +1196,6 @@ The extension collector adds five focused tests for native tail calls, command-r
1196
 
1197
 
1198
  [RE-866: Original camera setup with actual fallback spawn selection](QVM_CAMERA_SPAWN.md).
 
 
 
 
1196
 
1197
 
1198
  [RE-866: Original camera setup with actual fallback spawn selection](QVM_CAMERA_SPAWN.md).
1199
+
1200
+
1201
+ [RE-867: Rust camera composition with recovered fallback](RUST_CAMERA_SPAWN.md).
docs/research/RUST_CAMERA_SPAWN.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Rust camera composition with recovered fallback
2
+
3
+ RE-867. Reproduce with `python3 scripts/analyze_rust_camera_spawn.py`; focused validation: `cargo xtask test-group rust-camera-spawn`. [Machine report](rust-camera-spawn.json).
4
+
5
+ Rust matches128 continuous source camera/fallback/placement cases with shared RNG state and checked native-math transcripts. The supplied fallback view has been replaced by recovered spawn ranking and +9 Z. Telefrag answers stay caller-owned; no-spawn errors preserve placement state. Native collision/math and runtime integration remain open.
6
+
7
+ Original executable/native runtime and complete Linux/macOS Rust/Bevy/Solari acceptance remain unverified.
docs/research/rust-camera-spawn.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": 121,
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": 123,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
scripts/analyze_rust_camera_spawn.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import hashlib,json
3
+ from pathlib import Path
4
+ from rust_camera_world_support import binary,result,invoke
5
+ from analyze_qvm import write_json
6
+ from analyze_qvm_camera_spawn import scene
7
+ def encode(s):
8
+ a=s['input'];c,fallback=scene(a['layout'],a['profile'],a['spawns'],a['blocked']);out=[a['seed'],a['slot'],len(c),*fallback[0],*fallback[1]]
9
+ from rust_camera_world_support import blob
10
+ for e in c:out.extend([e['active'],e['name'],e['hash'],*e['origin'],*e['angles'],*blob(None if e['aim'] is None else e['aim'].hex()),*blob(None if e['label'] is None else e['label'].hex())])
11
+ out.extend(int(e.get('blocked',False)) for e in c);out.append(len(s['math']))
12
+ for m in s['math']:out.extend([m['target'],m['arguments']['8'],m['arguments'].get('12',0),m['result']])
13
+ return out
14
+ def project(s):return {k:s[k] for k in ['spawn_index','spawn_candidates','class_index','target_index','rng_state','matches','diagnostic','effects','view','entity','clients']}
15
+
16
+ def main():
17
+ b=binary();p=Path('docs/research/qvm-camera-spawn.json');data=p.read_bytes();rows=[]
18
+ for s in json.loads(data)['probes']:rows.append({'source':project(s),'rust':result(b,'spawn-camera',encode(s))})
19
+ write_json(Path('docs/research/rust-camera-spawn.json'),{'source_report':str(p),'source_report_sha256':hashlib.sha256(data).hexdigest(),'probes':rows,'limits':'Rust matches128 continuous source camera/fallback/placement cases with shared RNG state and checked native-math transcripts. The supplied fallback view has been replaced by recovered spawn ranking and +9 Z. Telefrag answers stay caller-owned; no-spawn errors preserve placement state. Native collision/math and runtime integration remain open.'})
20
+ if __name__=='__main__':main()
scripts/test_rust_camera_spawn.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-camera-spawn.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-camera-spawn.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
@@ -2446,3 +2446,17 @@ report scripts/analyze_qvm_camera_spawn.py docs/research/qvm-camera-spawn.json
2446
  edge docs/research/qvm-camera-spawn.json scripts/test_qvm_camera_spawn.py
2447
  group camera-spawn scripts/analyze_qvm_camera_spawn.py
2448
  edge scripts/analyze_qvm_camera_spawn.py scripts/analyze_qvm_resumer_audit.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2446
  edge docs/research/qvm-camera-spawn.json scripts/test_qvm_camera_spawn.py
2447
  group camera-spawn scripts/analyze_qvm_camera_spawn.py
2448
  edge scripts/analyze_qvm_camera_spawn.py scripts/analyze_qvm_resumer_audit.py
2449
+ edge Cargo.toml scripts/analyze_rust_camera_spawn.py
2450
+ edge Cargo.lock scripts/analyze_rust_camera_spawn.py
2451
+ edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_camera_spawn.py
2452
+ edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_camera_spawn.py
2453
+ edge crates/urt-gameplay/src/camera_world.rs scripts/analyze_rust_camera_spawn.py
2454
+ edge crates/urt-gameplay/src/bin/urt-camera-world.rs scripts/analyze_rust_camera_spawn.py
2455
+ edge docs/research/qvm-camera-spawn.json scripts/analyze_rust_camera_spawn.py
2456
+ group rust-camera-spawn scripts/analyze_rust_health_impulse.py
2457
+ group gameplay scripts/analyze_rust_camera_spawn.py
2458
+ edge crates/urt-gameplay/src/transition.rs scripts/analyze_rust_camera_spawn.py
2459
+ report scripts/analyze_rust_camera_spawn.py docs/research/rust-camera-spawn.json
2460
+ edge docs/research/rust-camera-spawn.json scripts/test_rust_camera_spawn.py
2461
+ group rust-camera-spawn scripts/analyze_rust_camera_spawn.py
2462
+ edge scripts/analyze_rust_camera_spawn.py scripts/analyze_qvm_resumer_audit.py