gameplay: integrate lazy camera collision queries
Browse files- crates/urt-gameplay/src/bin/urt-camera-world.rs +159 -0
- crates/urt-gameplay/src/camera_world.rs +86 -4
- 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_QUERIED_CAMERA.md +7 -0
- docs/research/rust-health-impulse.json +1 -1
- docs/research/rust-queried-camera.json +0 -0
- scripts/analyze_rust_queried_camera.py +15 -0
- scripts/test_rust_queried_camera.py +9 -0
- xtask/checks.tsv +16 -0
crates/urt-gameplay/src/bin/urt-camera-world.rs
CHANGED
|
@@ -503,6 +503,165 @@ fn run() -> Result<String, String> {
|
|
| 503 |
array(&out.view.angles)
|
| 504 |
))
|
| 505 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 506 |
_ => Err("unknown command".into()),
|
| 507 |
}
|
| 508 |
}
|
|
|
|
| 503 |
array(&out.view.angles)
|
| 504 |
))
|
| 505 |
}
|
| 506 |
+
"queried-camera" => {
|
| 507 |
+
if w.len() < 9 || w[1] > 1 || w[2] > 1024 {
|
| 508 |
+
return Err("invalid camera header".into());
|
| 509 |
+
}
|
| 510 |
+
let mut state = w[0];
|
| 511 |
+
let slot = w[1] as usize;
|
| 512 |
+
let n = w[2] as usize;
|
| 513 |
+
let _fallback = urt_gameplay::transition::View {
|
| 514 |
+
origin: w[3..6].try_into().unwrap(),
|
| 515 |
+
angles: w[6..9].try_into().unwrap(),
|
| 516 |
+
};
|
| 517 |
+
let mut p = 9;
|
| 518 |
+
let mut world = vec![];
|
| 519 |
+
for _ in 0..n {
|
| 520 |
+
let v = w.get(p..p + 9).ok_or("missing world record")?;
|
| 521 |
+
p += 9;
|
| 522 |
+
let class = HashEntity {
|
| 523 |
+
in_use: v[0] as i32,
|
| 524 |
+
class_present: v[1] != 0,
|
| 525 |
+
class_hash: v[2],
|
| 526 |
+
};
|
| 527 |
+
let origin = v[3..6].try_into().unwrap();
|
| 528 |
+
let angles = v[6..9].try_into().unwrap();
|
| 529 |
+
let aim = blob(&w, &mut p)?;
|
| 530 |
+
let label = blob(&w, &mut p)?;
|
| 531 |
+
world.push(WorldEntity {
|
| 532 |
+
class,
|
| 533 |
+
origin,
|
| 534 |
+
angles,
|
| 535 |
+
aim,
|
| 536 |
+
label,
|
| 537 |
+
});
|
| 538 |
+
}
|
| 539 |
+
let blocked = w
|
| 540 |
+
.get(p..p + n)
|
| 541 |
+
.ok_or("missing telefrag flags")?
|
| 542 |
+
.iter()
|
| 543 |
+
.map(|v| *v != 0)
|
| 544 |
+
.collect::<Vec<_>>();
|
| 545 |
+
p += n;
|
| 546 |
+
let bound_words = w.get(p..p + 6).ok_or("missing query bounds")?;
|
| 547 |
+
p += 6;
|
| 548 |
+
let bounds = std::array::from_fn(|h| {
|
| 549 |
+
std::array::from_fn(|j| f32::from_bits(bound_words[h * 3 + j]))
|
| 550 |
+
});
|
| 551 |
+
let mut queries = vec![];
|
| 552 |
+
let mut client_words = vec![0; 1024];
|
| 553 |
+
client_words[901] = u32::MAX;
|
| 554 |
+
let mut math = math_calls(&w, &mut p)?;
|
| 555 |
+
if p != w.len() {
|
| 556 |
+
return Err("trailing camera words".into());
|
| 557 |
+
}
|
| 558 |
+
let mut entity = urt_gameplay::transition::CameraEntity {
|
| 559 |
+
origin: [0x12345678; 3],
|
| 560 |
+
clear: [0x12345678; 6],
|
| 561 |
+
};
|
| 562 |
+
let blank = urt_gameplay::transition::CameraPlayer {
|
| 563 |
+
movement: 0xaabbccdd,
|
| 564 |
+
origin: [0xaabbccdd; 3],
|
| 565 |
+
velocity: [0xaabbccdd; 3],
|
| 566 |
+
field104: 0xaabbccdd,
|
| 567 |
+
angles: [0xaabbccdd; 3],
|
| 568 |
+
mode3108: 0xaabbccdd,
|
| 569 |
+
field4324: 0xaabbccdd,
|
| 570 |
+
};
|
| 571 |
+
let mut clients = [blank.clone(), blank];
|
| 572 |
+
let (out, spawn) = configure_camera_queried(
|
| 573 |
+
&world,
|
| 574 |
+
&mut state,
|
| 575 |
+
&mut math,
|
| 576 |
+
&mut entity,
|
| 577 |
+
&mut clients[slot],
|
| 578 |
+
SpawnQueryWorld {
|
| 579 |
+
bounds,
|
| 580 |
+
client_words: &client_words,
|
| 581 |
+
},
|
| 582 |
+
|j, b| {
|
| 583 |
+
let indices = query_results(j, if blocked[j] { 2 } else { 0 });
|
| 584 |
+
queries.push(query_json(j, b, &indices));
|
| 585 |
+
Ok(indices)
|
| 586 |
+
},
|
| 587 |
+
)
|
| 588 |
+
.map_err(|e| format!("{e:?}"))?;
|
| 589 |
+
math.finish().map_err(|e| format!("{e:?}"))?;
|
| 590 |
+
let e = word_object(
|
| 591 |
+
&[4, 8, 92, 96, 100, 156, 160, 180, 460],
|
| 592 |
+
&[
|
| 593 |
+
entity.clear[0],
|
| 594 |
+
entity.clear[1],
|
| 595 |
+
entity.origin[0],
|
| 596 |
+
entity.origin[1],
|
| 597 |
+
entity.origin[2],
|
| 598 |
+
entity.clear[2],
|
| 599 |
+
entity.clear[3],
|
| 600 |
+
entity.clear[4],
|
| 601 |
+
entity.clear[5],
|
| 602 |
+
],
|
| 603 |
+
);
|
| 604 |
+
let clients = clients
|
| 605 |
+
.iter()
|
| 606 |
+
.map(|p| {
|
| 607 |
+
word_object(
|
| 608 |
+
&[4, 20, 24, 28, 32, 36, 40, 104, 152, 156, 160, 3108, 4324],
|
| 609 |
+
&[
|
| 610 |
+
p.movement,
|
| 611 |
+
p.origin[0],
|
| 612 |
+
p.origin[1],
|
| 613 |
+
p.origin[2],
|
| 614 |
+
p.velocity[0],
|
| 615 |
+
p.velocity[1],
|
| 616 |
+
p.velocity[2],
|
| 617 |
+
p.field104,
|
| 618 |
+
p.angles[0],
|
| 619 |
+
p.angles[1],
|
| 620 |
+
p.angles[2],
|
| 621 |
+
p.mode3108,
|
| 622 |
+
p.field4324,
|
| 623 |
+
],
|
| 624 |
+
)
|
| 625 |
+
})
|
| 626 |
+
.collect::<Vec<_>>()
|
| 627 |
+
.join(",");
|
| 628 |
+
let effects = out
|
| 629 |
+
.effects
|
| 630 |
+
.iter()
|
| 631 |
+
.map(|e| match e {
|
| 632 |
+
CameraEffect::FindClass => 276433,
|
| 633 |
+
CameraEffect::Fallback => 168114,
|
| 634 |
+
CameraEffect::PickTarget => 276586,
|
| 635 |
+
CameraEffect::Angles => 62944,
|
| 636 |
+
})
|
| 637 |
+
.collect::<Vec<_>>();
|
| 638 |
+
let matches = out.matches.iter().map(|j| *j as u32).collect::<Vec<_>>();
|
| 639 |
+
let diagnostic = match out.diagnostic {
|
| 640 |
+
None => "null",
|
| 641 |
+
Some(TargetDiagnostic::Null) => "\"null\"",
|
| 642 |
+
Some(TargetDiagnostic::Missing) => "\"missing\"",
|
| 643 |
+
};
|
| 644 |
+
let spawn_index = spawn.as_ref().map(|v| v.index as i32).unwrap_or(-1);
|
| 645 |
+
let spawn_candidates = array(
|
| 646 |
+
&spawn
|
| 647 |
+
.as_ref()
|
| 648 |
+
.map(|v| v.candidates.iter().map(|j| *j as u32).collect::<Vec<_>>())
|
| 649 |
+
.unwrap_or_default(),
|
| 650 |
+
);
|
| 651 |
+
let payload = format!(
|
| 652 |
+
"{{\"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}]}}",
|
| 653 |
+
out.class_index.map(|j| j as i32).unwrap_or(-1),
|
| 654 |
+
out.target_index.map(|j| j as i32).unwrap_or(-1),
|
| 655 |
+
array(&matches),
|
| 656 |
+
array(&effects),
|
| 657 |
+
array(&out.view.origin),
|
| 658 |
+
array(&out.view.angles)
|
| 659 |
+
);
|
| 660 |
+
Ok(format!(
|
| 661 |
+
"{{\"camera\":{payload},\"queries\":[{}]}}",
|
| 662 |
+
queries.join(",")
|
| 663 |
+
))
|
| 664 |
+
}
|
| 665 |
_ => Err("unknown command".into()),
|
| 666 |
}
|
| 667 |
}
|
crates/urt-gameplay/src/camera_world.rs
CHANGED
|
@@ -737,6 +737,19 @@ pub fn configure_camera_with_spawns<M: AngleMath>(
|
|
| 737 |
if blocked.len() != world.len() {
|
| 738 |
return Err(Error::InvalidCount);
|
| 739 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 740 |
let classes = world.iter().map(|e| e.class).collect::<Vec<_>>();
|
| 741 |
if find_class(&classes, world.len(), None, (-2104846030i32) as u32)?.is_some() {
|
| 742 |
return Ok((
|
|
@@ -756,15 +769,14 @@ pub fn configure_camera_with_spawns<M: AngleMath>(
|
|
| 756 |
}
|
| 757 |
let spawns = world
|
| 758 |
.iter()
|
| 759 |
-
.
|
| 760 |
-
.map(|(e, b)| SpawnEntity {
|
| 761 |
class: e.class,
|
| 762 |
-
blocked:
|
| 763 |
origin: e.origin,
|
| 764 |
angles: e.angles,
|
| 765 |
})
|
| 766 |
.collect::<Vec<_>>();
|
| 767 |
-
let selected =
|
| 768 |
let camera = CameraResolution {
|
| 769 |
class_index: None,
|
| 770 |
target_index: None,
|
|
@@ -952,3 +964,73 @@ mod query_spawn_tests {
|
|
| 952 |
assert_eq!(state, 7);
|
| 953 |
}
|
| 954 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 737 |
if blocked.len() != world.len() {
|
| 738 |
return Err(Error::InvalidCount);
|
| 739 |
}
|
| 740 |
+
configure_camera_with_spawn_check(world, state, math, entity, player, |j, _| Ok(blocked[j]))
|
| 741 |
+
}
|
| 742 |
+
pub fn configure_camera_with_spawn_check<
|
| 743 |
+
M: AngleMath,
|
| 744 |
+
F: FnMut(usize, &SpawnEntity) -> Result<bool, Error>,
|
| 745 |
+
>(
|
| 746 |
+
world: &[WorldEntity],
|
| 747 |
+
state: &mut u32,
|
| 748 |
+
math: &mut M,
|
| 749 |
+
entity: &mut crate::transition::CameraEntity,
|
| 750 |
+
player: &mut crate::transition::CameraPlayer,
|
| 751 |
+
mut check: F,
|
| 752 |
+
) -> Result<(CameraResolution, Option<SpawnSelection>), Error> {
|
| 753 |
let classes = world.iter().map(|e| e.class).collect::<Vec<_>>();
|
| 754 |
if find_class(&classes, world.len(), None, (-2104846030i32) as u32)?.is_some() {
|
| 755 |
return Ok((
|
|
|
|
| 769 |
}
|
| 770 |
let spawns = world
|
| 771 |
.iter()
|
| 772 |
+
.map(|e| SpawnEntity {
|
|
|
|
| 773 |
class: e.class,
|
| 774 |
+
blocked: false,
|
| 775 |
origin: e.origin,
|
| 776 |
angles: e.angles,
|
| 777 |
})
|
| 778 |
.collect::<Vec<_>>();
|
| 779 |
+
let selected = fallback_spawn_with_check(&spawns, [0; 3], state, math, &mut check)?;
|
| 780 |
let camera = CameraResolution {
|
| 781 |
class_index: None,
|
| 782 |
target_index: None,
|
|
|
|
| 964 |
assert_eq!(state, 7);
|
| 965 |
}
|
| 966 |
}
|
| 967 |
+
|
| 968 |
+
pub fn configure_camera_queried<
|
| 969 |
+
M: AngleMath,
|
| 970 |
+
F: FnMut(usize, crate::native_policy::SpawnBox) -> Result<Vec<usize>, Error>,
|
| 971 |
+
>(
|
| 972 |
+
world: &[WorldEntity],
|
| 973 |
+
state: &mut u32,
|
| 974 |
+
math: &mut M,
|
| 975 |
+
entity: &mut crate::transition::CameraEntity,
|
| 976 |
+
player: &mut crate::transition::CameraPlayer,
|
| 977 |
+
scene: SpawnQueryWorld<'_>,
|
| 978 |
+
mut query: F,
|
| 979 |
+
) -> Result<(CameraResolution, Option<SpawnSelection>), Error> {
|
| 980 |
+
configure_camera_with_spawn_check(world, state, math, entity, player, |j, e| {
|
| 981 |
+
overlap_check(j, e, scene, &mut query)
|
| 982 |
+
})
|
| 983 |
+
}
|
| 984 |
+
#[cfg(test)]
|
| 985 |
+
mod queried_camera_tests {
|
| 986 |
+
use super::*;
|
| 987 |
+
#[test]
|
| 988 |
+
fn primary_camera_never_queries() {
|
| 989 |
+
let world = [WorldEntity {
|
| 990 |
+
class: HashEntity {
|
| 991 |
+
in_use: 1,
|
| 992 |
+
class_present: true,
|
| 993 |
+
class_hash: (-2104846030i32) as u32,
|
| 994 |
+
},
|
| 995 |
+
origin: [0; 3],
|
| 996 |
+
angles: [0; 3],
|
| 997 |
+
aim: None,
|
| 998 |
+
label: None,
|
| 999 |
+
}];
|
| 1000 |
+
let mut rng = 7;
|
| 1001 |
+
let mut e = crate::transition::CameraEntity {
|
| 1002 |
+
origin: [8; 3],
|
| 1003 |
+
clear: [9; 6],
|
| 1004 |
+
};
|
| 1005 |
+
let mut p = crate::transition::CameraPlayer {
|
| 1006 |
+
movement: 7,
|
| 1007 |
+
origin: [7; 3],
|
| 1008 |
+
velocity: [7; 3],
|
| 1009 |
+
field104: 7,
|
| 1010 |
+
angles: [7; 3],
|
| 1011 |
+
mode3108: 7,
|
| 1012 |
+
field4324: 7,
|
| 1013 |
+
};
|
| 1014 |
+
let mut called = false;
|
| 1015 |
+
let (_, spawn) = configure_camera_queried(
|
| 1016 |
+
&world,
|
| 1017 |
+
&mut rng,
|
| 1018 |
+
&mut HostMath,
|
| 1019 |
+
&mut e,
|
| 1020 |
+
&mut p,
|
| 1021 |
+
SpawnQueryWorld {
|
| 1022 |
+
bounds: [[f32::NAN; 3]; 2],
|
| 1023 |
+
client_words: &[],
|
| 1024 |
+
},
|
| 1025 |
+
|_, _| {
|
| 1026 |
+
called = true;
|
| 1027 |
+
Err(Error::InvalidCount)
|
| 1028 |
+
},
|
| 1029 |
+
)
|
| 1030 |
+
.unwrap();
|
| 1031 |
+
assert!(!called);
|
| 1032 |
+
assert!(spawn.is_none());
|
| 1033 |
+
assert_eq!(rng, 7);
|
| 1034 |
+
assert_eq!(e.origin, [0; 3]);
|
| 1035 |
+
}
|
| 1036 |
+
}
|
docs/FINDINGS.md
CHANGED
|
@@ -1658,3 +1658,6 @@ RE-813–822 replaces the scoreboard decimal assumption with actual37914 executi
|
|
| 1658 |
|
| 1659 |
|
| 1660 |
**RE-886:** 128 coherent6717/168114/167638/6575 cases execute box construction and client filtering inside camera fallback. Existing primary cameras issue no spatial queries; fallback class matches issue ordered native -33 requests. Result/view/player fields and checked math match the prior boolean-boundary fixtures. Native broadphase and math remain supplied; respawn and complete intermission/runtime remain open. [Evidence](research/QVM_QUERIED_CAMERA.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1658 |
|
| 1659 |
|
| 1660 |
**RE-886:** 128 coherent6717/168114/167638/6575 cases execute box construction and client filtering inside camera fallback. Existing primary cameras issue no spatial queries; fallback class matches issue ordered native -33 requests. Result/view/player fields and checked math match the prior boolean-boundary fixtures. Native broadphase and math remain supplied; respawn and complete intermission/runtime remain open. [Evidence](research/QVM_QUERIED_CAMERA.md).
|
| 1661 |
+
|
| 1662 |
+
|
| 1663 |
+
**RE-887:** Rust matches128 original continuous camera/queried-fallback/placement cases, including exact query order and primary-camera bypass. Spatial callbacks use the same recovered box/filter policy as standalone fallback. Existing camera/spawn APIs retain their contracts; native broadphase/math and complete respawn/intermission remain open. [Evidence](research/RUST_QUERIED_CAMERA.md).
|
docs/PROVISIONAL_TODO.md
CHANGED
|
@@ -1689,3 +1689,6 @@ RE-885: Rust matches64 original168114/167638 compositions and their ordered box
|
|
| 1689 |
|
| 1690 |
|
| 1691 |
RE-886: 128 coherent6717/168114/167638/6575 cases execute box construction and client filtering inside camera fallback. Existing primary cameras issue no spatial queries; fallback class matches issue ordered native -33 requests. Result/view/player fields and checked math match the prior boolean-boundary fixtures. Native broadphase and math remain supplied; respawn and complete intermission/runtime remain open. Full runtime completion remains open. [Evidence](research/QVM_QUERIED_CAMERA.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1689 |
|
| 1690 |
|
| 1691 |
RE-886: 128 coherent6717/168114/167638/6575 cases execute box construction and client filtering inside camera fallback. Existing primary cameras issue no spatial queries; fallback class matches issue ordered native -33 requests. Result/view/player fields and checked math match the prior boolean-boundary fixtures. Native broadphase and math remain supplied; respawn and complete intermission/runtime remain open. Full runtime completion remains open. [Evidence](research/QVM_QUERIED_CAMERA.md).
|
| 1692 |
+
|
| 1693 |
+
|
| 1694 |
+
RE-887: Rust matches128 original continuous camera/queried-fallback/placement cases, including exact query order and primary-camera bypass. Spatial callbacks use the same recovered box/filter policy as standalone fallback. Existing camera/spawn APIs retain their contracts; native broadphase/math and complete respawn/intermission remain open. Full runtime completion remains open. [Evidence](research/RUST_QUERIED_CAMERA.md).
|
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -2140,3 +2140,8 @@ Rust matches64 original168114/167638 compositions and their ordered box query tr
|
|
| 2140 |
## 2026-10-28 — RE-886 Original camera placement with queried fallback
|
| 2141 |
|
| 2142 |
128 coherent6717/168114/167638/6575 cases execute box construction and client filtering inside camera fallback. Existing primary cameras issue no spatial queries; fallback class matches issue ordered native -33 requests. Result/view/player fields and checked math match the prior boolean-boundary fixtures. Native broadphase and math remain supplied; respawn and complete intermission/runtime remain open. Focused `cargo xtask test-group qvm-queried-camera` 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.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2140 |
## 2026-10-28 — RE-886 Original camera placement with queried fallback
|
| 2141 |
|
| 2142 |
128 coherent6717/168114/167638/6575 cases execute box construction and client filtering inside camera fallback. Existing primary cameras issue no spatial queries; fallback class matches issue ordered native -33 requests. Result/view/player fields and checked math match the prior boolean-boundary fixtures. Native broadphase and math remain supplied; respawn and complete intermission/runtime remain open. Focused `cargo xtask test-group qvm-queried-camera` 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.
|
| 2143 |
+
|
| 2144 |
+
|
| 2145 |
+
## 2026-10-28 — RE-887 Rust camera with lazy spatial queries
|
| 2146 |
+
|
| 2147 |
+
Rust matches128 original continuous camera/queried-fallback/placement cases, including exact query order and primary-camera bypass. Spatial callbacks use the same recovered box/filter policy as standalone fallback. Existing camera/spawn APIs retain their contracts; native broadphase/math and complete respawn/intermission remain open. Focused `cargo xtask test-group rust-queried-camera` 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.
|
docs/REVERSE_ENGINEERING_TODO.md
CHANGED
|
@@ -1726,3 +1726,6 @@ Purpose: turn the supplied installation into an evidence-backed specification of
|
|
| 1726 |
|
| 1727 |
|
| 1728 |
- [x] RE-886 Original camera placement with queried fallback ([evidence](research/QVM_QUERIED_CAMERA.md)).
|
|
|
|
|
|
|
|
|
|
|
|
| 1726 |
|
| 1727 |
|
| 1728 |
- [x] RE-886 Original camera placement with queried fallback ([evidence](research/QVM_QUERIED_CAMERA.md)).
|
| 1729 |
+
|
| 1730 |
+
|
| 1731 |
+
- [x] RE-887 Rust camera with lazy spatial queries ([evidence](research/RUST_QUERIED_CAMERA.md)).
|
docs/research/README.md
CHANGED
|
@@ -1261,3 +1261,6 @@ The extension collector adds five focused tests for native tail calls, command-r
|
|
| 1261 |
|
| 1262 |
|
| 1263 |
[RE-886: Original camera placement with queried fallback](QVM_QUERIED_CAMERA.md).
|
|
|
|
|
|
|
|
|
|
|
|
| 1261 |
|
| 1262 |
|
| 1263 |
[RE-886: Original camera placement with queried fallback](QVM_QUERIED_CAMERA.md).
|
| 1264 |
+
|
| 1265 |
+
|
| 1266 |
+
[RE-887: Rust camera with lazy spatial queries](RUST_QUERIED_CAMERA.md).
|
docs/research/RUST_QUERIED_CAMERA.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Rust camera with lazy spatial queries
|
| 2 |
+
|
| 3 |
+
RE-887. Reproduce with `python3 scripts/analyze_rust_queried_camera.py`; focused validation: `cargo xtask test-group rust-queried-camera`. [Machine report](rust-queried-camera.json).
|
| 4 |
+
|
| 5 |
+
Rust matches128 original continuous camera/queried-fallback/placement cases, including exact query order and primary-camera bypass. Spatial callbacks use the same recovered box/filter policy as standalone fallback. Existing camera/spawn APIs retain their contracts; native broadphase/math and complete respawn/intermission remain open.
|
| 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":
|
| 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": 143,
|
| 2989 |
"source_report": "docs/research/qvm-health-impulse.json",
|
| 2990 |
"source_report_sha256": "625fd8064004ea6dbdf398d51d0af844d9c635ff04c0a0790886dddb6ab81e71"
|
| 2991 |
}
|
docs/research/rust-queried-camera.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/analyze_rust_queried_camera.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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_rust_camera_spawn import encode as old_encode,project as old_project
|
| 7 |
+
def encode(s):
|
| 8 |
+
out=old_encode(s);pos=len(out)-1-4*len(s['math']);out[pos:pos]=s['bounds'];return out
|
| 9 |
+
def project(s):return {'camera':old_project(s),'queries':[{k:v for k,v in q.items() if k!='trace_sha256'} for q in s['queries']]}
|
| 10 |
+
|
| 11 |
+
def main():
|
| 12 |
+
b=binary();p=Path('docs/research/qvm-queried-camera.json');data=p.read_bytes();rows=[]
|
| 13 |
+
for s in json.loads(data)['probes']:rows.append({'source':project(s),'rust':result(b,'queried-camera',encode(s))})
|
| 14 |
+
write_json(Path('docs/research/rust-queried-camera.json'),{'source_report':str(p),'source_report_sha256':hashlib.sha256(data).hexdigest(),'probes':rows,'limits':'Rust matches128 original continuous camera/queried-fallback/placement cases, including exact query order and primary-camera bypass. Spatial callbacks use the same recovered box/filter policy as standalone fallback. Existing camera/spawn APIs retain their contracts; native broadphase/math and complete respawn/intermission remain open.'})
|
| 15 |
+
if __name__=='__main__':main()
|
scripts/test_rust_queried_camera.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-queried-camera.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-queried-camera.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
|
@@ -2632,3 +2632,19 @@ report scripts/analyze_qvm_queried_camera.py docs/research/qvm-queried-camera.js
|
|
| 2632 |
edge docs/research/qvm-queried-camera.json scripts/test_qvm_queried_camera.py
|
| 2633 |
group qvm-queried-camera scripts/analyze_qvm_queried_camera.py
|
| 2634 |
edge scripts/analyze_qvm_queried_camera.py scripts/analyze_qvm_resumer_audit.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2632 |
edge docs/research/qvm-queried-camera.json scripts/test_qvm_queried_camera.py
|
| 2633 |
group qvm-queried-camera scripts/analyze_qvm_queried_camera.py
|
| 2634 |
edge scripts/analyze_qvm_queried_camera.py scripts/analyze_qvm_resumer_audit.py
|
| 2635 |
+
edge Cargo.toml scripts/analyze_rust_queried_camera.py
|
| 2636 |
+
edge Cargo.lock scripts/analyze_rust_queried_camera.py
|
| 2637 |
+
edge crates/urt-gameplay/Cargo.toml scripts/analyze_rust_queried_camera.py
|
| 2638 |
+
edge crates/urt-gameplay/src/lib.rs scripts/analyze_rust_queried_camera.py
|
| 2639 |
+
edge crates/urt-gameplay/src/camera_world.rs scripts/analyze_rust_queried_camera.py
|
| 2640 |
+
edge crates/urt-gameplay/src/bin/urt-camera-world.rs scripts/analyze_rust_queried_camera.py
|
| 2641 |
+
edge docs/research/qvm-queried-camera.json scripts/analyze_rust_queried_camera.py
|
| 2642 |
+
group rust-queried-camera scripts/analyze_rust_health_impulse.py
|
| 2643 |
+
group gameplay scripts/analyze_rust_queried_camera.py
|
| 2644 |
+
edge crates/urt-gameplay/src/native_policy.rs scripts/analyze_rust_queried_camera.py
|
| 2645 |
+
group rust-queried-camera scripts/analyze_rust_camera_spawn.py
|
| 2646 |
+
group rust-queried-camera scripts/analyze_rust_camera_world.py
|
| 2647 |
+
report scripts/analyze_rust_queried_camera.py docs/research/rust-queried-camera.json
|
| 2648 |
+
edge docs/research/rust-queried-camera.json scripts/test_rust_queried_camera.py
|
| 2649 |
+
group rust-queried-camera scripts/analyze_rust_queried_camera.py
|
| 2650 |
+
edge scripts/analyze_rust_queried_camera.py scripts/analyze_qvm_resumer_audit.py
|