nmarshall commited on
Commit
2cd958c
·
1 Parent(s): 549f772

gameplay: port camera placement words

Browse files
crates/urt-gameplay/src/bin/urt-transition.rs ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use urt_gameplay::transition::*;
2
+ fn array(v: &[u32]) -> String {
3
+ format!(
4
+ "[{}]",
5
+ v.iter()
6
+ .map(|v| v.to_string())
7
+ .collect::<Vec<_>>()
8
+ .join(",")
9
+ )
10
+ }
11
+ fn object(keys: &[u32], values: &[u32]) -> String {
12
+ format!(
13
+ "{{{}}}",
14
+ keys.iter()
15
+ .zip(values)
16
+ .map(|(k, v)| format!("\"{k}\":{v}"))
17
+ .collect::<Vec<_>>()
18
+ .join(",")
19
+ )
20
+ }
21
+ fn run() -> Result<String, String> {
22
+ let a = std::env::args().skip(1).collect::<Vec<_>>();
23
+ let kind = a.first().ok_or("missing command")?;
24
+ let w = a[1..]
25
+ .iter()
26
+ .map(|s| s.parse::<u32>().map_err(|_| "invalid word".to_string()))
27
+ .collect::<Result<Vec<_>, _>>()?;
28
+ match kind.as_str() {
29
+ "camera" => {
30
+ if w.len() != 9 || w[2] > 1 {
31
+ return Err("invalid camera input".into());
32
+ }
33
+ let blank = CameraPlayer {
34
+ movement: 0xaabbccdd,
35
+ origin: [0xaabbccdd; 3],
36
+ velocity: [0xaabbccdd; 3],
37
+ field104: 0xaabbccdd,
38
+ angles: [0xaabbccdd; 3],
39
+ mode3108: 0xaabbccdd,
40
+ field4324: 0xaabbccdd,
41
+ };
42
+ let mut players = [blank.clone(), blank];
43
+ let slot = w[2] as usize;
44
+ players[slot].mode3108 = w[0];
45
+ players[slot].field4324 = w[1];
46
+ let mut entity = CameraEntity {
47
+ origin: [0x12345678; 3],
48
+ clear: [0x12345678; 6],
49
+ };
50
+ place_camera(
51
+ &mut entity,
52
+ &mut players[slot],
53
+ w[3..6].try_into().unwrap(),
54
+ w[6..9].try_into().unwrap(),
55
+ );
56
+ let e = object(
57
+ &[4, 8, 92, 96, 100, 156, 160, 180, 460],
58
+ &[
59
+ entity.clear[0],
60
+ entity.clear[1],
61
+ entity.origin[0],
62
+ entity.origin[1],
63
+ entity.origin[2],
64
+ entity.clear[2],
65
+ entity.clear[3],
66
+ entity.clear[4],
67
+ entity.clear[5],
68
+ ],
69
+ );
70
+ let c = players
71
+ .iter()
72
+ .map(|p| {
73
+ object(
74
+ &[4, 20, 24, 28, 32, 36, 40, 104, 152, 156, 160, 3108, 4324],
75
+ &[
76
+ p.movement,
77
+ p.origin[0],
78
+ p.origin[1],
79
+ p.origin[2],
80
+ p.velocity[0],
81
+ p.velocity[1],
82
+ p.velocity[2],
83
+ p.field104,
84
+ p.angles[0],
85
+ p.angles[1],
86
+ p.angles[2],
87
+ p.mode3108,
88
+ p.field4324,
89
+ ],
90
+ )
91
+ })
92
+ .collect::<Vec<_>>()
93
+ .join(",");
94
+ Ok(format!("{{\"entity\":{e},\"clients\":[{c}]}}"))
95
+ }
96
+ _ => Err("unknown command".into()),
97
+ }
98
+ }
99
+ fn main() {
100
+ match run() {
101
+ Ok(s) => println!("{s}"),
102
+ Err(e) => {
103
+ eprintln!("{e}");
104
+ std::process::exit(2);
105
+ }
106
+ }
107
+ }
crates/urt-gameplay/src/lib.rs CHANGED
@@ -173,3 +173,5 @@ pub mod exit_prefix;
173
  pub mod exit_rules;
174
 
175
  pub mod intermission;
 
 
 
173
  pub mod exit_rules;
174
 
175
  pub mod intermission;
176
+
177
+ pub mod transition;
crates/urt-gameplay/src/transition.rs ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ //! Bounded original camera, map transition and temporary event contracts.
2
+ #[derive(Clone, Debug, PartialEq, Eq)]
3
+ pub struct CameraEntity {
4
+ pub origin: [u32; 3],
5
+ pub clear: [u32; 6],
6
+ }
7
+ #[derive(Clone, Debug, PartialEq, Eq)]
8
+ pub struct CameraPlayer {
9
+ pub movement: u32,
10
+ pub origin: [u32; 3],
11
+ pub velocity: [u32; 3],
12
+ pub field104: u32,
13
+ pub angles: [u32; 3],
14
+ pub mode3108: u32,
15
+ pub field4324: u32,
16
+ }
17
+ pub fn place_camera(
18
+ entity: &mut CameraEntity,
19
+ player: &mut CameraPlayer,
20
+ origin: [u32; 3],
21
+ angles: [u32; 3],
22
+ ) {
23
+ entity.origin = origin;
24
+ entity.clear = [0; 6];
25
+ player.origin = origin;
26
+ player.angles = angles;
27
+ player.velocity = [0; 3];
28
+ player.movement = 5;
29
+ player.field104 = 0;
30
+ }
31
+ #[cfg(test)]
32
+ mod camera_tests {
33
+ use super::*;
34
+ #[test]
35
+ fn exact_raw_words_and_clears() {
36
+ let mut e = CameraEntity {
37
+ origin: [1; 3],
38
+ clear: [1; 6],
39
+ };
40
+ let mut p = CameraPlayer {
41
+ movement: 1,
42
+ origin: [1; 3],
43
+ velocity: [1; 3],
44
+ field104: 1,
45
+ angles: [1; 3],
46
+ mode3108: 2,
47
+ field4324: 7,
48
+ };
49
+ let v = [0x7fc01234, 0x80000000, 0x7f800000];
50
+ place_camera(&mut e, &mut p, v, v);
51
+ assert_eq!(e.origin, v);
52
+ assert_eq!(p.angles, v);
53
+ assert_eq!((p.mode3108, p.field4324), (2, 7));
54
+ assert_eq!(
55
+ (e.clear, p.velocity, p.movement, p.field104),
56
+ ([0; 6], [0; 3], 5, 0)
57
+ );
58
+ }
59
+ #[test]
60
+ fn repeated_placement_replaces_view() {
61
+ let mut e = CameraEntity {
62
+ origin: [1; 3],
63
+ clear: [1; 6],
64
+ };
65
+ let mut p = CameraPlayer {
66
+ movement: 1,
67
+ origin: [1; 3],
68
+ velocity: [1; 3],
69
+ field104: 1,
70
+ angles: [1; 3],
71
+ mode3108: 0,
72
+ field4324: 0,
73
+ };
74
+ place_camera(&mut e, &mut p, [2; 3], [3; 3]);
75
+ place_camera(&mut e, &mut p, [4; 3], [5; 3]);
76
+ assert_eq!((e.origin, p.origin, p.angles), ([4; 3], [4; 3], [5; 3]));
77
+ }
78
+ }
docs/FINDINGS.md CHANGED
@@ -1532,3 +1532,6 @@ RE-813–822 replaces the scoreboard decimal assumption with actual37914 executi
1532
 
1533
 
1534
  **RE-844:** 72 original 6575 fixtures recover entity/player origin and view-angle copies, velocity clear, movement type5 and cleared entity/client fields. Coherent byte copies preserve arbitrary float words, including NaN payloads; two entity-to-client targets prove indirection. Mode/field4324 gates converge without extra effects. Native renderer/camera integration remains open. [Evidence](research/QVM_CAMERA_PLACE.md).
 
 
 
 
1532
 
1533
 
1534
  **RE-844:** 72 original 6575 fixtures recover entity/player origin and view-angle copies, velocity clear, movement type5 and cleared entity/client fields. Coherent byte copies preserve arbitrary float words, including NaN payloads; two entity-to-client targets prove indirection. Mode/field4324 gates converge without extra effects. Native renderer/camera integration remains open. [Evidence](research/QVM_CAMERA_PLACE.md).
1535
+
1536
+
1537
+ **RE-845:** Rust matches all 72 original camera fixtures, copying raw float words exactly and clearing the verified fields. The parity adapter resolves either entity-to-client target; the library accepts an already-resolved mutable client. Native camera/rendering and unrestricted pointer alias behavior remain open. [Evidence](research/RUST_CAMERA_PLACE.md).
docs/PROVISIONAL_TODO.md CHANGED
@@ -1551,3 +1551,6 @@ RE-843: Rust composes recovered tie, readiness, begin and exit-log policies with
1551
 
1552
 
1553
  RE-844: 72 original 6575 fixtures recover entity/player origin and view-angle copies, velocity clear, movement type5 and cleared entity/client fields. Coherent byte copies preserve arbitrary float words, including NaN payloads; two entity-to-client targets prove indirection. Mode/field4324 gates converge without extra effects. Native renderer/camera integration remains open. Full runtime completion remains open. [Evidence](research/QVM_CAMERA_PLACE.md).
 
 
 
 
1551
 
1552
 
1553
  RE-844: 72 original 6575 fixtures recover entity/player origin and view-angle copies, velocity clear, movement type5 and cleared entity/client fields. Coherent byte copies preserve arbitrary float words, including NaN payloads; two entity-to-client targets prove indirection. Mode/field4324 gates converge without extra effects. Native renderer/camera integration remains open. Full runtime completion remains open. [Evidence](research/QVM_CAMERA_PLACE.md).
1554
+
1555
+
1556
+ RE-845: Rust matches all 72 original camera fixtures, copying raw float words exactly and clearing the verified fields. The parity adapter resolves either entity-to-client target; the library accepts an already-resolved mutable client. Native camera/rendering and unrestricted pointer alias behavior remain open. Full runtime completion remains open. [Evidence](research/RUST_CAMERA_PLACE.md).
docs/RESEARCH_LEDGER.md CHANGED
@@ -1898,3 +1898,8 @@ Batch RE-834–843 boundary: the single `cargo xtask test-full --since 5f7b2df8`
1898
  ## 2026-10-27 — RE-844 Original intermission camera placement and client indirection
1899
 
1900
  72 original 6575 fixtures recover entity/player origin and view-angle copies, velocity clear, movement type5 and cleared entity/client fields. Coherent byte copies preserve arbitrary float words, including NaN payloads; two entity-to-client targets prove indirection. Mode/field4324 gates converge without extra effects. Native renderer/camera integration remains open. Focused `cargo xtask test-group camera-place` 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.
 
 
 
 
 
 
1898
  ## 2026-10-27 — RE-844 Original intermission camera placement and client indirection
1899
 
1900
  72 original 6575 fixtures recover entity/player origin and view-angle copies, velocity clear, movement type5 and cleared entity/client fields. Coherent byte copies preserve arbitrary float words, including NaN payloads; two entity-to-client targets prove indirection. Mode/field4324 gates converge without extra effects. Native renderer/camera integration remains open. Focused `cargo xtask test-group camera-place` 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.
1901
+
1902
+
1903
+ ## 2026-10-27 — RE-845 Rust intermission camera placement
1904
+
1905
+ Rust matches all 72 original camera fixtures, copying raw float words exactly and clearing the verified fields. The parity adapter resolves either entity-to-client target; the library accepts an already-resolved mutable client. Native camera/rendering and unrestricted pointer alias behavior remain open. Focused `cargo xtask test-group rust-camera-place` 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
@@ -1588,3 +1588,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
1588
 
1589
 
1590
  - [x] RE-844 Original intermission camera placement and client indirection ([evidence](research/QVM_CAMERA_PLACE.md)).
 
 
 
 
1588
 
1589
 
1590
  - [x] RE-844 Original intermission camera placement and client indirection ([evidence](research/QVM_CAMERA_PLACE.md)).
1591
+
1592
+
1593
+ - [x] RE-845 Rust intermission camera placement ([evidence](research/RUST_CAMERA_PLACE.md)).
docs/research/README.md CHANGED
@@ -1126,3 +1126,6 @@ The extension collector adds five focused tests for native tail calls, command-r
1126
 
1127
 
1128
  [RE-844: Original intermission camera placement and client indirection](QVM_CAMERA_PLACE.md).
 
 
 
 
1126
 
1127
 
1128
  [RE-844: Original intermission camera placement and client indirection](QVM_CAMERA_PLACE.md).
1129
+
1130
+
1131
+ [RE-845: Rust intermission camera placement](RUST_CAMERA_PLACE.md).
docs/research/RUST_CAMERA_PLACE.md ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Rust intermission camera placement
2
+
3
+ RE-845. Reproduce with `python3 scripts/analyze_rust_camera_place.py`; focused validation: `cargo xtask test-group rust-camera-place`. [Machine report](rust-camera-place.json).
4
+
5
+ Rust matches all 72 original camera fixtures, copying raw float words exactly and clearing the verified fields. The parity adapter resolves either entity-to-client target; the library accepts an already-resolved mutable client. Native camera/rendering and unrestricted pointer alias behavior remain open.
6
+
7
+ Original executable/native runtime and complete Linux/macOS Rust/Bevy/Solari acceptance remain unverified.
docs/research/rust-camera-place.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": 99,
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": 101,
2989
  "source_report": "docs/research/qvm-health-impulse.json",
2990
  "source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
2991
  }
scripts/analyze_rust_camera_place.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import hashlib,json
3
+ from pathlib import Path
4
+ from rust_transition_support import binary,result,invoke
5
+ from analyze_qvm import write_json
6
+ def encode(s):
7
+ a=s['input'];return [a['mode'],a['special'],a['slot'],*a['origin'],*a['angles']]
8
+ def project(s):return {k:s[k] for k in ['entity','clients']}
9
+ def main():
10
+ b=binary();p=Path('docs/research/qvm-camera-place.json');data=p.read_bytes();rows=[]
11
+ for s in json.loads(data)['probes']:rows.append({'source':project(s),'rust':result(b,'camera',encode(s))})
12
+ write_json(Path('docs/research/rust-camera-place.json'),{'source_report':str(p),'source_report_sha256':hashlib.sha256(data).hexdigest(),'probes':rows,'limits':'Rust matches all 72 original camera fixtures, copying raw float words exactly and clearing the verified fields. The parity adapter resolves either entity-to-client target; the library accepts an already-resolved mutable client. Native camera/rendering and unrestricted pointer alias behavior remain open.'})
13
+ if __name__=='__main__':main()
scripts/rust_transition_support.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ """Build and invoke the scoped transition parity adapter."""
2
+ import os,subprocess
3
+ from pathlib import Path
4
+ from rust_lifecycle_support import result,invoke
5
+ def binary():
6
+ subprocess.run(['cargo','build','--offline','--package','urt-gameplay','--bin','urt-transition'],capture_output=True,text=True,check=True)
7
+ return Path(os.environ.get('CARGO_TARGET_DIR','target'))/'debug'/'urt-transition'
scripts/test_rust_camera_place.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-place.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-place.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
@@ -2249,3 +2249,18 @@ edge docs/research/qvm-camera-place.json scripts/test_qvm_camera_place.py
2249
  group camera-place scripts/analyze_qvm_camera_place.py
2250
  edge scripts/analyze_qvm_camera_place.py scripts/analyze_qvm_resumer_audit.py
2251
  edge scripts/qvm_transition_support.py scripts/analyze_qvm_resumer_audit.py
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2249
  group camera-place scripts/analyze_qvm_camera_place.py
2250
  edge scripts/analyze_qvm_camera_place.py scripts/analyze_qvm_resumer_audit.py
2251
  edge scripts/qvm_transition_support.py scripts/analyze_qvm_resumer_audit.py
2252
+ edge Cargo.toml scripts/analyze_rust_camera_place.py
2253
+ edge Cargo.lock scripts/analyze_rust_camera_place.py
2254
+ edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_camera_place.py
2255
+ edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_camera_place.py
2256
+ edge crates/urt-gameplay/src/transition.rs scripts/analyze_rust_camera_place.py
2257
+ edge crates/urt-gameplay/src/bin/urt-transition.rs scripts/analyze_rust_camera_place.py
2258
+ edge docs/research/qvm-camera-place.json scripts/analyze_rust_camera_place.py
2259
+ edge crates/urt-gameplay/src/transition.rs scripts/analyze_rust_health_impulse.py
2260
+ group rust-camera-place scripts/analyze_rust_health_impulse.py
2261
+ group gameplay scripts/analyze_rust_camera_place.py
2262
+ report scripts/analyze_rust_camera_place.py docs/research/rust-camera-place.json
2263
+ edge docs/research/rust-camera-place.json scripts/test_rust_camera_place.py
2264
+ group rust-camera-place scripts/analyze_rust_camera_place.py
2265
+ edge scripts/analyze_rust_camera_place.py scripts/analyze_qvm_resumer_audit.py
2266
+ edge scripts/rust_transition_support.py scripts/analyze_qvm_resumer_audit.py