aboutsummaryrefslogtreecommitdiff
path: root/crates/fparkan-runtime/src
diff options
context:
space:
mode:
authorValentin Popov <valentin@popov.link>2026-10-29 15:26:54 +0300
committerValentin Popov <valentin@popov.link>2026-10-29 15:26:54 +0300
commit38ff0ca6828ce83dea376f288959a9ab2242d662 (patch)
tree14e6560c68827ffcd761627b742681bca31c0919 /crates/fparkan-runtime/src
parent888a331a6dfae8568ea4389e937cf09f2760d490 (diff)
downloadfparkan-38ff0ca6828ce83dea376f288959a9ab2242d662.tar.xz
fparkan-38ff0ca6828ce83dea376f288959a9ab2242d662.zip
fix(runtime): load robot Control device tables
Diffstat (limited to 'crates/fparkan-runtime/src')
-rw-r--r--crates/fparkan-runtime/src/lib.rs93
1 files changed, 90 insertions, 3 deletions
diff --git a/crates/fparkan-runtime/src/lib.rs b/crates/fparkan-runtime/src/lib.rs
index 180533a..d15d7ea 100644
--- a/crates/fparkan-runtime/src/lib.rs
+++ b/crates/fparkan-runtime/src/lib.rs
@@ -3154,16 +3154,17 @@ fn apply_effective_control_override(
fn control_factory_uses_common_setter(factory_tag: u32) -> bool {
matches!(
factory_tag,
- 1 | 2 | 4 | 5 | 8 | 9 | 10 | 15 | 17 | 19 | 25 | 26 | 30
+ 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 15 | 17 | 19 | 21 | 25 | 26 | 27 | 30
)
}
fn native_control_factory_defaults(factory_tag: u32) -> (Option<u32>, Option<u32>) {
match factory_tag {
1 | 4 => (Some(0x3f80_0000), Some(0x200)),
- 5 | 8 | 26 => (Some(0x3f80_0000), Some(0x5)),
+ 3 | 5 | 8 | 21 | 26 => (Some(0x3f80_0000), Some(0x5)),
10 => (Some(0x3f80_0000), Some(0x2000)),
17 | 19 => (Some(0x3f80_0000), Some(0x20)),
+ 2 | 27 => (Some(0x3f80_0000), Some(0)),
9 | 15 | 25 => (Some(0x3f80_0000), None),
_ => (None, None),
}
@@ -3534,7 +3535,10 @@ fn build_control_device_projection(
target_index,
},
)?;
- if !matches!(device.factory_tag, 9 | 15 | 19 | 25) {
+ if !matches!(
+ device.factory_tag,
+ 2 | 3 | 4 | 5 | 8 | 9 | 10 | 15 | 19 | 21 | 25 | 27
+ ) {
return Err(Unavailable::UnsupportedOverrideReceiver {
factory_tag: device.factory_tag,
});
@@ -4961,6 +4965,57 @@ mod tests {
}
#[test]
+ fn common_factory_into_overrides_preserve_native_constructor_defaults() {
+ const INTO: u32 = u32::from_le_bytes(*b"INTO");
+ for (factory_tag, word_50) in [
+ (2, 0),
+ (3, 5),
+ (4, 0x200),
+ (5, 5),
+ (8, 5),
+ (10, 0x2000),
+ (21, 5),
+ (27, 0),
+ ] {
+ let mut root = synthetic_control_record(factory_tag, 2, 0x20, None);
+ root.fixed_words[6] = u32::MAX;
+ let mut replacement = synthetic_control_record(19, 99, 0x30, None);
+ replacement.fixed_words[6] = u32::MAX;
+ let status = synthetic_control_table(
+ &[None, Some(INTO)],
+ &[1, 1],
+ &[1, 0],
+ &[0, 0],
+ &[1, 1],
+ vec![
+ Some(synthetic_component_control(
+ 0,
+ vec![root],
+ PreparedControlEffectSection::Parsed(Vec::new()),
+ )),
+ Some(synthetic_component_control(
+ 1,
+ vec![replacement],
+ PreparedControlEffectSection::Parsed(Vec::new()),
+ )),
+ ],
+ );
+ let MissionControlDeviceTableStatus::Available(devices) = status else {
+ panic!("{status:?}")
+ };
+ assert_eq!(devices.len(), 1);
+ let device = &devices[0];
+ assert_eq!(
+ (device.factory_tag, device.descriptor.tag, device.key),
+ (factory_tag, 19, 2)
+ );
+ assert_eq!(device.multiplier_bits, Some(1.0_f32.to_bits()));
+ assert_eq!(device.native_word_50_bits, Some(word_50));
+ assert_eq!(device.coefficient_bits.unwrap()[0], 0x30);
+ }
+ }
+
+ #[test]
fn control_factory_multiplier_defaults_preserve_unknown_word_50() {
for factory_tag in [9, 15, 25] {
assert_eq!(
@@ -6972,6 +7027,38 @@ mod tests {
#[test]
#[ignore = "requires licensed corpus"]
+ fn full_load_builds_control_tables_for_autodemo_robots_and_buildings() {
+ let mut engine = create(EngineServices::new(Arc::new(DirectoryVfs::new(
+ licensed_root("IS"),
+ ))))
+ .unwrap();
+ load_mission(
+ &mut engine,
+ MissionRequest {
+ key: "MISSIONS/Autodemo.00/data.tma".into(),
+ mode: MissionMode::AutoDemo,
+ },
+ 100_000,
+ )
+ .unwrap();
+ let tables = loaded_mission_control_device_tables(&engine).unwrap();
+ let expected_counts = [14, 12, 15, 13, 12, 12, 11, 11];
+ assert_eq!(tables.len(), expected_counts.len());
+ for (table, expected_count) in tables.iter().zip(expected_counts) {
+ let MissionControlDeviceTableStatus::Available(devices) = &table.status else {
+ panic!("object {:?}: {:?}", table.original_id, table.status);
+ };
+ assert_eq!(
+ devices.len(),
+ expected_count,
+ "object {:?}",
+ table.original_id
+ );
+ }
+ }
+
+ #[test]
+ #[ignore = "requires licensed corpus"]
fn headless_step_runs_resource_schedule_and_rolls_back_both_error_paths() {
let mut engine = create(EngineServices::new(Arc::new(DirectoryVfs::new(
licensed_root("IS"),