gameplay: compose recovered camera fallback
Browse files- crates/urt-gameplay/src/bin/urt-camera-world.rs +140 -0
- crates/urt-gameplay/src/camera_world.rs +125 -0
- docs/FINDINGS.md +3 -0
- docs/PROVISIONAL_TODO.md +3 -0
- docs/RESEARCH_LEDGER.md +5 -0
- docs/REVERSE_ENGINEERING_TODO.md +3 -0
- docs/research/README.md +3 -0
- docs/research/RUST_CAMERA_SPAWN.md +7 -0
- docs/research/rust-camera-spawn.json +0 -0
- docs/research/rust-health-impulse.json +1 -1
- scripts/analyze_rust_camera_spawn.py +20 -0
- scripts/test_rust_camera_spawn.py +9 -0
- xtask/checks.tsv +14 -0
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":
|
| 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
|