From c17b9f55f73c5f7c01230f2cec621b246cd21823 Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Thu, 16 Apr 2026 14:47:38 -0700 Subject: [PATCH] Make cargo catalog save-native --- README.md | 5 +- crates/rrt-cli/src/main.rs | 5 + crates/rrt-fixtures/src/load.rs | 4 + crates/rrt-fixtures/src/schema.rs | 10 + crates/rrt-runtime/src/import.rs | 198 +++++++++++++++--- crates/rrt-runtime/src/lib.rs | 35 ++-- crates/rrt-runtime/src/persistence.rs | 1 + crates/rrt-runtime/src/runtime.rs | 68 ++++++ crates/rrt-runtime/src/smp.rs | 85 ++++++++ crates/rrt-runtime/src/step.rs | 1 + crates/rrt-runtime/src/summary.rs | 9 + docs/README.md | 6 +- docs/runtime-rehost-plan.md | 6 +- ...vent-cargo-catalog-save-slice-fixture.json | 44 ++++ ...packed-event-cargo-catalog-save-slice.json | 48 +++++ ...vent-selective-import-overlay-fixture.json | 2 +- ...scalar-band-parity-save-slice-fixture.json | 3 + ...t-world-scalar-band-parity-save-slice.json | 19 ++ ...r-condition-parity-save-slice-fixture.json | 13 +- ...ld-scalar-condition-parity-save-slice.json | 19 ++ ...d-scalar-condition-save-slice-fixture.json | 9 +- ...ent-world-scalar-condition-save-slice.json | 19 ++ ...-scalar-executable-save-slice-fixture.json | 1 + ...nt-world-scalar-executable-save-slice.json | 19 ++ 24 files changed, 576 insertions(+), 53 deletions(-) create mode 100644 fixtures/runtime/packed-event-cargo-catalog-save-slice-fixture.json create mode 100644 fixtures/runtime/packed-event-cargo-catalog-save-slice.json diff --git a/README.md b/README.md index adb36e0..4107edf 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,10 @@ metadata still does not ground, such as `All Factory Production`, now remain exp unmapped world-condition and world-descriptor frontier buckets still remain where current checked-in metadata stops, and `blocked_missing_locomotive_catalog_context` is now reserved for intentionally incomplete save-side -catalog context instead of the normal save-slice path. Shell purchase-flow, Trainbuy refresh, +catalog context instead of the normal save-slice path. Cargo slot identity is now save-native too: +the recipe-book probe lowers into `RuntimeState.cargo_catalog`, so save-slice documents can carry +slot labels and token-stem evidence alongside the executable `cargo_production_overrides` surface +without introducing a live cargo-economy model. Shell purchase-flow, Trainbuy refresh, cached locomotive-rating recomputation, and selected-profile parity remain out of scope. Mixed supported/unsupported real rows still stay parity-only. The PE32 hook remains useful as capture and integration tooling, but it is no longer the main execution milestone. diff --git a/crates/rrt-cli/src/main.rs b/crates/rrt-cli/src/main.rs index 204d8be..938e61a 100644 --- a/crates/rrt-cli/src/main.rs +++ b/crates/rrt-cli/src/main.rs @@ -4483,6 +4483,8 @@ mod tests { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join( "../../fixtures/runtime/packed-event-world-scalar-condition-parity-save-slice-fixture.json", ); + let cargo_catalog_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("../../fixtures/runtime/packed-event-cargo-catalog-save-slice-fixture.json"); run_runtime_summarize_fixture(&parity_fixture) .expect("save-slice-backed parity fixture should summarize"); @@ -4522,6 +4524,8 @@ mod tests { .expect("save-slice-backed executable world-scalar condition fixture should summarize"); run_runtime_summarize_fixture(&world_scalar_condition_parity_fixture) .expect("save-slice-backed parity world-scalar condition fixture should summarize"); + run_runtime_summarize_fixture(&cargo_catalog_fixture) + .expect("save-slice-backed cargo catalog fixture should summarize"); } #[test] @@ -4548,6 +4552,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: None, notes: vec!["exported for test".to_string()], diff --git a/crates/rrt-fixtures/src/load.rs b/crates/rrt-fixtures/src/load.rs index 53f13e1..e73c95a 100644 --- a/crates/rrt-fixtures/src/load.rs +++ b/crates/rrt-fixtures/src/load.rs @@ -178,6 +178,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -265,6 +266,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: None, notes: vec![], @@ -353,6 +355,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -383,6 +386,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some( rrt_runtime::SmpLoadedEventRuntimeCollectionSummary { diff --git a/crates/rrt-fixtures/src/schema.rs b/crates/rrt-fixtures/src/schema.rs index a29ce67..f1bf091 100644 --- a/crates/rrt-fixtures/src/schema.rs +++ b/crates/rrt-fixtures/src/schema.rs @@ -80,6 +80,8 @@ pub struct ExpectedRuntimeSummary { #[serde(default)] pub locomotive_catalog_count: Option, #[serde(default)] + pub cargo_catalog_count: Option, + #[serde(default)] pub territory_count: Option, #[serde(default)] pub company_territory_track_count: Option, @@ -469,6 +471,14 @@ impl ExpectedRuntimeSummary { )); } } + if let Some(count) = self.cargo_catalog_count { + if actual.cargo_catalog_count != count { + mismatches.push(format!( + "cargo_catalog_count mismatch: expected {count}, got {}", + actual.cargo_catalog_count + )); + } + } if let Some(count) = self.territory_count { if actual.territory_count != count { mismatches.push(format!( diff --git a/crates/rrt-runtime/src/import.rs b/crates/rrt-runtime/src/import.rs index 2db4dc3..e720a62 100644 --- a/crates/rrt-runtime/src/import.rs +++ b/crates/rrt-runtime/src/import.rs @@ -5,17 +5,17 @@ use serde::{Deserialize, Serialize}; use crate::persistence::{load_runtime_snapshot_document, validate_runtime_snapshot_document}; use crate::{ - CalendarPoint, RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind, - RuntimeCompanyTarget, RuntimeCondition, RuntimeEffect, RuntimeEventRecord, - RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary, - RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary, - RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary, - RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, - RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeSaveProfileState, - RuntimeServiceState, RuntimeState, RuntimeTerritoryTarget, RuntimeWorldRestoreState, - SmpLoadedPackedEventConditionRowSummary, SmpLoadedPackedEventGroupedEffectRowSummary, - SmpLoadedPackedEventNegativeSentinelScopeSummary, SmpLoadedPackedEventRecordSummary, - SmpLoadedPackedEventTextBandSummary, SmpLoadedSaveSlice, + CalendarPoint, RuntimeCargoCatalogEntry, RuntimeCompanyConditionTestScope, + RuntimeCompanyControllerKind, RuntimeCompanyTarget, RuntimeCondition, RuntimeEffect, + RuntimeEventRecord, RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry, + RuntimePackedEventCollectionSummary, RuntimePackedEventCompactControlSummary, + RuntimePackedEventConditionRowSummary, RuntimePackedEventGroupedEffectRowSummary, + RuntimePackedEventNegativeSentinelScopeSummary, RuntimePackedEventRecordSummary, + RuntimePackedEventTextBandSummary, RuntimePlayerConditionTestScope, RuntimePlayerTarget, + RuntimeSaveProfileState, RuntimeServiceState, RuntimeState, RuntimeTerritoryTarget, + RuntimeWorldRestoreState, SmpLoadedPackedEventConditionRowSummary, + SmpLoadedPackedEventGroupedEffectRowSummary, SmpLoadedPackedEventNegativeSentinelScopeSummary, + SmpLoadedPackedEventRecordSummary, SmpLoadedPackedEventTextBandSummary, SmpLoadedSaveSlice, }; pub const STATE_DUMP_FORMAT_VERSION: u32 = 1; @@ -96,6 +96,7 @@ struct SaveSliceProjection { candidate_availability: BTreeMap, named_locomotive_availability: BTreeMap, locomotive_catalog: Option>, + cargo_catalog: Option>, named_locomotive_cost: BTreeMap, cargo_production_overrides: BTreeMap, special_conditions: BTreeMap, @@ -245,6 +246,7 @@ pub fn project_save_slice_to_runtime_state_import( selected_player_id: None, trains: Vec::new(), locomotive_catalog: projection.locomotive_catalog.unwrap_or_default(), + cargo_catalog: projection.cargo_catalog.unwrap_or_default(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -309,6 +311,9 @@ pub fn project_save_slice_overlay_to_runtime_state_import( locomotive_catalog: projection .locomotive_catalog .unwrap_or_else(|| base_state.locomotive_catalog.clone()), + cargo_catalog: projection + .cargo_catalog + .unwrap_or_else(|| base_state.cargo_catalog.clone()), territories: base_state.territories.clone(), company_territory_track_piece_counts: base_state .company_territory_track_piece_counts @@ -359,6 +364,10 @@ fn project_save_slice_components( save_slice.locomotive_catalog.is_some() || save_slice.named_locomotive_availability_table.is_some(), ); + world_flags.insert( + "save_slice.cargo_catalog_present".to_string(), + save_slice.cargo_catalog.is_some(), + ); world_flags.insert( "save_slice.event_runtime_collection_present".to_string(), save_slice.event_runtime_collection.is_some(), @@ -690,6 +699,44 @@ fn project_save_slice_components( } else { None }; + let cargo_catalog = if let Some(catalog) = &save_slice.cargo_catalog { + metadata.insert( + "save_slice.cargo_catalog_source_kind".to_string(), + catalog.source_kind.clone(), + ); + metadata.insert( + "save_slice.cargo_catalog_semantic_family".to_string(), + catalog.semantic_family.clone(), + ); + metadata.insert( + "save_slice.cargo_catalog_entry_count".to_string(), + catalog.observed_entry_count.to_string(), + ); + if let Some(root_offset) = catalog.root_offset { + metadata.insert( + "save_slice.cargo_catalog_root_offset".to_string(), + root_offset.to_string(), + ); + } + Some( + catalog + .entries + .iter() + .map(|entry| RuntimeCargoCatalogEntry { + slot_id: entry.slot_id, + label: entry.label.clone(), + supplied_token_stem: entry + .supplied_cargo_token_probable_high16_ascii_stem + .clone(), + demanded_token_stem: entry + .demanded_cargo_token_probable_high16_ascii_stem + .clone(), + }) + .collect::>(), + ) + } else { + None + }; let named_locomotive_cost = BTreeMap::new(); let cargo_production_overrides = BTreeMap::new(); @@ -702,8 +749,11 @@ fn project_save_slice_components( .collect(); } - let (packed_event_collection, event_runtime_records) = - project_packed_event_collection(save_slice, &packed_event_context)?; + let (packed_event_collection, event_runtime_records) = project_packed_event_collection( + save_slice, + &packed_event_context, + cargo_catalog.as_deref().unwrap_or(&[]), + )?; if let Some(summary) = &save_slice.event_runtime_collection { metadata.insert( "save_slice.event_runtime_collection_source_kind".to_string(), @@ -741,6 +791,7 @@ fn project_save_slice_components( candidate_availability, named_locomotive_availability, locomotive_catalog, + cargo_catalog, named_locomotive_cost, cargo_production_overrides, special_conditions, @@ -750,6 +801,7 @@ fn project_save_slice_components( fn project_packed_event_collection( save_slice: &SmpLoadedSaveSlice, company_context: &ImportRuntimeContext, + cargo_catalog: &[RuntimeCargoCatalogEntry], ) -> Result< ( Option, @@ -780,6 +832,7 @@ fn project_packed_event_collection( runtime_packed_event_record_summary_from_smp( record, company_context, + cargo_catalog, imported_record_ids.contains(&record.live_entry_id), ) }) @@ -810,6 +863,7 @@ fn project_packed_event_collection( fn runtime_packed_event_record_summary_from_smp( record: &SmpLoadedPackedEventRecordSummary, company_context: &ImportRuntimeContext, + cargo_catalog: &[RuntimeCargoCatalogEntry], imported: bool, ) -> RuntimePackedEventRecordSummary { let lowered_decoded_conditions = lowered_record_decoded_conditions(record, company_context) @@ -840,7 +894,7 @@ fn runtime_packed_event_record_summary_from_smp( standalone_condition_rows: record .standalone_condition_rows .iter() - .map(runtime_packed_event_condition_row_summary_from_smp) + .map(|row| runtime_packed_event_condition_row_summary_from_smp(row, cargo_catalog)) .collect(), negative_sentinel_scope: record .negative_sentinel_scope @@ -850,7 +904,7 @@ fn runtime_packed_event_record_summary_from_smp( grouped_effect_rows: record .grouped_effect_rows .iter() - .map(runtime_packed_event_grouped_effect_row_summary_from_smp) + .map(|row| runtime_packed_event_grouped_effect_row_summary_from_smp(row, cargo_catalog)) .collect(), grouped_company_targets: classify_real_grouped_company_targets(record), decoded_conditions: lowered_decoded_conditions, @@ -906,7 +960,11 @@ fn runtime_packed_event_text_band_summary_from_smp( fn runtime_packed_event_condition_row_summary_from_smp( row: &crate::SmpLoadedPackedEventConditionRowSummary, + cargo_catalog: &[RuntimeCargoCatalogEntry], ) -> RuntimePackedEventConditionRowSummary { + let cargo_entry = row + .recovered_cargo_slot + .and_then(|slot| cargo_catalog.iter().find(|entry| entry.slot_id == slot)); RuntimePackedEventConditionRowSummary { row_index: row.row_index, raw_condition_id: row.raw_condition_id, @@ -918,13 +976,26 @@ fn runtime_packed_event_condition_row_summary_from_smp( semantic_family: row.semantic_family.clone(), semantic_preview: row.semantic_preview.clone(), requires_candidate_name_binding: row.requires_candidate_name_binding, + recovered_cargo_slot: row.recovered_cargo_slot, + recovered_cargo_class: row.recovered_cargo_class.clone(), + recovered_cargo_label: cargo_entry + .map(|entry| entry.label.clone()) + .or_else(|| row.recovered_cargo_slot.map(default_cargo_slot_label)), + recovered_cargo_supplied_token_stem: cargo_entry + .and_then(|entry| entry.supplied_token_stem.clone()), + recovered_cargo_demanded_token_stem: cargo_entry + .and_then(|entry| entry.demanded_token_stem.clone()), notes: row.notes.clone(), } } fn runtime_packed_event_grouped_effect_row_summary_from_smp( row: &crate::SmpLoadedPackedEventGroupedEffectRowSummary, + cargo_catalog: &[RuntimeCargoCatalogEntry], ) -> RuntimePackedEventGroupedEffectRowSummary { + let cargo_entry = row + .recovered_cargo_slot + .and_then(|slot| cargo_catalog.iter().find(|entry| entry.slot_id == slot)); RuntimePackedEventGroupedEffectRowSummary { group_index: row.group_index, row_index: row.row_index, @@ -943,12 +1014,25 @@ fn runtime_packed_event_grouped_effect_row_summary_from_smp( row_shape: row.row_shape.clone(), semantic_family: row.semantic_family.clone(), semantic_preview: row.semantic_preview.clone(), + recovered_cargo_slot: row.recovered_cargo_slot, + recovered_cargo_class: row.recovered_cargo_class.clone(), + recovered_cargo_label: cargo_entry + .map(|entry| entry.label.clone()) + .or_else(|| row.recovered_cargo_slot.map(default_cargo_slot_label)), + recovered_cargo_supplied_token_stem: cargo_entry + .and_then(|entry| entry.supplied_token_stem.clone()), + recovered_cargo_demanded_token_stem: cargo_entry + .and_then(|entry| entry.demanded_token_stem.clone()), recovered_locomotive_id: row.recovered_locomotive_id, locomotive_name: row.locomotive_name.clone(), notes: row.notes.clone(), } } +fn default_cargo_slot_label(slot: u32) -> String { + format!("Cargo Production Slot {slot}") +} + fn smp_packed_record_to_runtime_event_record( record: &SmpLoadedPackedEventRecordSummary, company_context: &ImportRuntimeContext, @@ -2251,6 +2335,17 @@ fn determine_packed_event_import_outcome( { return "blocked_retire_train_variant".to_string(); } + if record + .standalone_condition_rows + .iter() + .any(|row| row.raw_condition_id >= 0) + { + if record_has_world_state_condition_rows(record) { + return "blocked_unmapped_world_condition".to_string(); + } else { + return "blocked_unmapped_ordinary_condition".to_string(); + } + } if record .grouped_effect_rows .iter() @@ -2258,19 +2353,7 @@ fn determine_packed_event_import_outcome( { return "blocked_unmapped_world_descriptor".to_string(); } - return if record - .standalone_condition_rows - .iter() - .any(|row| row.raw_condition_id >= 0) - { - if record_has_world_state_condition_rows(record) { - "blocked_unmapped_world_condition".to_string() - } else { - "blocked_unmapped_ordinary_condition".to_string() - } - } else { - "blocked_unmapped_real_descriptor".to_string() - }; + return "blocked_unmapped_real_descriptor".to_string(); } if let Some(blocker) = packed_record_condition_scope_import_blocker(record, company_context) { @@ -2952,6 +3035,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -3717,6 +3801,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: None, notes: vec![], @@ -3757,6 +3842,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: None, notes: vec![], @@ -3871,6 +3957,7 @@ mod tests { }, ), locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: Some(crate::SmpLoadedSpecialConditionsTable { source_kind: "save-fixed-special-conditions-range".to_string(), table_offset: 0x0d64, @@ -4196,6 +4283,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -4311,6 +4399,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -4404,6 +4493,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -4515,6 +4605,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -4599,6 +4690,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -4733,6 +4825,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -4975,6 +5068,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -5052,6 +5146,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -5153,6 +5248,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -5227,6 +5323,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -5328,6 +5425,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -5405,6 +5503,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: Some(save_named_locomotive_table(112)), locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -5522,6 +5621,7 @@ mod tests { name: "Locomotive 112".to_string(), }, ], + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -5548,6 +5648,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -5650,6 +5751,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -5726,6 +5828,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -5802,6 +5905,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: Some(save_named_locomotive_table(112)), locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -5912,6 +6016,7 @@ mod tests { name: "Locomotive 101".to_string(), }, ], + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -5938,6 +6043,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -6033,6 +6139,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -6107,6 +6214,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -6191,6 +6299,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -6294,6 +6403,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -6317,6 +6427,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -6464,6 +6575,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -6568,6 +6680,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -6653,6 +6766,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -6759,6 +6873,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -6865,6 +6980,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -6961,6 +7077,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -7053,6 +7170,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -7153,6 +7271,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -7244,6 +7363,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -7317,6 +7437,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -7395,6 +7516,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -7478,6 +7600,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -7561,6 +7684,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -7660,6 +7784,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -7750,6 +7875,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -7866,6 +7992,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -7995,6 +8122,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -8292,6 +8420,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -8402,6 +8531,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -8575,6 +8705,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -8668,6 +8799,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -8757,6 +8889,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -8911,6 +9044,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -9054,6 +9188,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -9144,6 +9279,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -9261,6 +9397,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -9366,6 +9503,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -9474,6 +9612,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -9512,6 +9651,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), @@ -9656,6 +9796,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -9687,6 +9828,7 @@ mod tests { candidate_availability_table: None, named_locomotive_availability_table: None, locomotive_catalog: None, + cargo_catalog: None, special_conditions_table: None, event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary { source_kind: "packed-event-runtime-collection".to_string(), diff --git a/crates/rrt-runtime/src/lib.rs b/crates/rrt-runtime/src/lib.rs index a9e4f89..6c4ecb2 100644 --- a/crates/rrt-runtime/src/lib.rs +++ b/crates/rrt-runtime/src/lib.rs @@ -35,29 +35,30 @@ pub use pk4::{ extract_pk4_entry_bytes, extract_pk4_entry_file, inspect_pk4_bytes, inspect_pk4_file, }; pub use runtime::{ - RuntimeCompany, RuntimeCompanyConditionTestScope, RuntimeCompanyControllerKind, - RuntimeCompanyMetric, RuntimeCompanyTarget, RuntimeCompanyTerritoryAccess, - RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, RuntimeConditionComparator, - RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate, RuntimeLocomotiveCatalogEntry, - RuntimePackedEventCollectionSummary, RuntimePackedEventCompactControlSummary, - RuntimePackedEventConditionRowSummary, RuntimePackedEventGroupedEffectRowSummary, - RuntimePackedEventNegativeSentinelScopeSummary, RuntimePackedEventRecordSummary, - RuntimePackedEventTextBandSummary, RuntimePlayer, RuntimePlayerConditionTestScope, - RuntimePlayerTarget, RuntimeSaveProfileState, RuntimeServiceState, RuntimeState, - RuntimeTerritory, RuntimeTerritoryMetric, RuntimeTerritoryTarget, RuntimeTrackMetric, - RuntimeTrackPieceCounts, RuntimeTrain, RuntimeWorldRestoreState, + RuntimeCargoCatalogEntry, RuntimeCompany, RuntimeCompanyConditionTestScope, + RuntimeCompanyControllerKind, RuntimeCompanyMetric, RuntimeCompanyTarget, + RuntimeCompanyTerritoryAccess, RuntimeCompanyTerritoryTrackPieceCount, RuntimeCondition, + RuntimeConditionComparator, RuntimeEffect, RuntimeEventRecord, RuntimeEventRecordTemplate, + RuntimeLocomotiveCatalogEntry, RuntimePackedEventCollectionSummary, + RuntimePackedEventCompactControlSummary, RuntimePackedEventConditionRowSummary, + RuntimePackedEventGroupedEffectRowSummary, RuntimePackedEventNegativeSentinelScopeSummary, + RuntimePackedEventRecordSummary, RuntimePackedEventTextBandSummary, RuntimePlayer, + RuntimePlayerConditionTestScope, RuntimePlayerTarget, RuntimeSaveProfileState, + RuntimeServiceState, RuntimeState, RuntimeTerritory, RuntimeTerritoryMetric, + RuntimeTerritoryTarget, RuntimeTrackMetric, RuntimeTrackPieceCounts, RuntimeTrain, + RuntimeWorldRestoreState, }; pub use smp::{ SMP_FOUR_SIDECAR_BYTE_PLANES_MIN_BUNDLE_VERSION, SmpAlignedRuntimeRuleBandLane, SmpAlignedRuntimeRuleBandProbe, SmpAsciiPreview, SmpClassicPackedProfileBlock, SmpClassicRehydrateProfileProbe, SmpContainerProfile, SmpEarlyContentProbe, SmpHeaderVariantProbe, SmpInspectionReport, SmpKnownTagHit, - SmpLoadedCandidateAvailabilityTable, SmpLoadedEventRuntimeCollectionSummary, - SmpLoadedNamedLocomotiveAvailabilityTable, SmpLoadedPackedEventCompactControlSummary, - SmpLoadedPackedEventConditionRowSummary, SmpLoadedPackedEventGroupedEffectRowSummary, - SmpLoadedPackedEventNegativeSentinelScopeSummary, SmpLoadedPackedEventRecordSummary, - SmpLoadedPackedEventTextBandSummary, SmpLoadedProfile, SmpLoadedSaveSlice, - SmpLoadedSpecialConditionsTable, SmpLocomotivePolicyFieldObservation, + SmpLoadedCandidateAvailabilityTable, SmpLoadedCargoCatalog, SmpLoadedCargoCatalogEntry, + SmpLoadedEventRuntimeCollectionSummary, SmpLoadedNamedLocomotiveAvailabilityTable, + SmpLoadedPackedEventCompactControlSummary, SmpLoadedPackedEventConditionRowSummary, + SmpLoadedPackedEventGroupedEffectRowSummary, SmpLoadedPackedEventNegativeSentinelScopeSummary, + SmpLoadedPackedEventRecordSummary, SmpLoadedPackedEventTextBandSummary, SmpLoadedProfile, + SmpLoadedSaveSlice, SmpLoadedSpecialConditionsTable, SmpLocomotivePolicyFieldObservation, SmpLocomotivePolicyFloatAlignmentCandidate, SmpLocomotivePolicyNeighborhoodProbe, SmpPackedProfileWordLane, SmpPostSpecialConditionsScalarLane, SmpPostSpecialConditionsScalarProbe, SmpPostTextFieldNeighborhoodProbe, diff --git a/crates/rrt-runtime/src/persistence.rs b/crates/rrt-runtime/src/persistence.rs index 6d0b68e..8bff5ad 100644 --- a/crates/rrt-runtime/src/persistence.rs +++ b/crates/rrt-runtime/src/persistence.rs @@ -98,6 +98,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), diff --git a/crates/rrt-runtime/src/runtime.rs b/crates/rrt-runtime/src/runtime.rs index 849199e..79cf3c6 100644 --- a/crates/rrt-runtime/src/runtime.rs +++ b/crates/rrt-runtime/src/runtime.rs @@ -113,6 +113,16 @@ pub struct RuntimeLocomotiveCatalogEntry { pub name: String, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct RuntimeCargoCatalogEntry { + pub slot_id: u32, + pub label: String, + #[serde(default)] + pub supplied_token_stem: Option, + #[serde(default)] + pub demanded_token_stem: Option, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(tag = "kind", rename_all = "snake_case")] pub enum RuntimeCompanyTarget { @@ -533,6 +543,16 @@ pub struct RuntimePackedEventConditionRowSummary { #[serde(default)] pub requires_candidate_name_binding: bool, #[serde(default)] + pub recovered_cargo_slot: Option, + #[serde(default)] + pub recovered_cargo_class: Option, + #[serde(default)] + pub recovered_cargo_label: Option, + #[serde(default)] + pub recovered_cargo_supplied_token_stem: Option, + #[serde(default)] + pub recovered_cargo_demanded_token_stem: Option, + #[serde(default)] pub notes: Vec, } @@ -561,6 +581,16 @@ pub struct RuntimePackedEventGroupedEffectRowSummary { #[serde(default)] pub semantic_preview: Option, #[serde(default)] + pub recovered_cargo_slot: Option, + #[serde(default)] + pub recovered_cargo_class: Option, + #[serde(default)] + pub recovered_cargo_label: Option, + #[serde(default)] + pub recovered_cargo_supplied_token_stem: Option, + #[serde(default)] + pub recovered_cargo_demanded_token_stem: Option, + #[serde(default)] pub recovered_locomotive_id: Option, #[serde(default)] pub locomotive_name: Option, @@ -684,6 +714,8 @@ pub struct RuntimeState { #[serde(default)] pub locomotive_catalog: Vec, #[serde(default)] + pub cargo_catalog: Vec, + #[serde(default)] pub territories: Vec, #[serde(default)] pub company_territory_track_piece_counts: Vec, @@ -837,6 +869,28 @@ impl RuntimeState { )); } } + let mut seen_cargo_slots = BTreeSet::new(); + let mut seen_cargo_labels = BTreeSet::new(); + for entry in &self.cargo_catalog { + if !(1..=11).contains(&entry.slot_id) { + return Err(format!( + "cargo_catalog entry has out-of-range slot_id {}", + entry.slot_id + )); + } + if !seen_cargo_slots.insert(entry.slot_id) { + return Err(format!("duplicate cargo_catalog.slot_id {}", entry.slot_id)); + } + if entry.label.trim().is_empty() { + return Err(format!( + "cargo_catalog entry {} has an empty label", + entry.slot_id + )); + } + if !seen_cargo_labels.insert(entry.label.clone()) { + return Err(format!("duplicate cargo_catalog.label {:?}", entry.label)); + } + } for entry in &self.company_territory_track_piece_counts { if !seen_company_ids.contains(&entry.company_id) { return Err(format!( @@ -1550,6 +1604,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -1610,6 +1665,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -1655,6 +1711,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -1713,6 +1770,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -1771,6 +1829,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -1880,6 +1939,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -1925,6 +1985,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -1987,6 +2048,7 @@ mod tests { }, ], locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -2039,6 +2101,7 @@ mod tests { retired: false, }], locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -2091,6 +2154,7 @@ mod tests { retired: false, }], locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: vec![RuntimeTerritory { territory_id: 1, name: Some("Appalachia".to_string()), @@ -2147,6 +2211,7 @@ mod tests { retired: true, }], locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -2192,6 +2257,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: vec![RuntimeTerritory { territory_id: 7, name: Some("Appalachia".to_string()), @@ -2250,6 +2316,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: vec![RuntimeTerritory { territory_id: 7, name: Some("Appalachia".to_string()), @@ -2302,6 +2369,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: vec![RuntimeTerritory { territory_id: 7, name: Some("Appalachia".to_string()), diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index 590e5cd..0f423eb 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -1625,6 +1625,33 @@ pub struct SmpLoadedLocomotiveCatalog { pub entries: Vec, } +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SmpLoadedCargoCatalogEntry { + pub slot_id: u32, + pub label: String, + pub book_index: usize, + pub max_annual_production_word: u32, + pub mode_word: u32, + pub runtime_import_branch_kind: String, + pub annual_amount_word: u32, + pub supplied_cargo_token_word: u32, + #[serde(default)] + pub supplied_cargo_token_probable_high16_ascii_stem: Option, + pub demanded_cargo_token_word: u32, + #[serde(default)] + pub demanded_cargo_token_probable_high16_ascii_stem: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct SmpLoadedCargoCatalog { + pub source_kind: String, + pub semantic_family: String, + #[serde(default)] + pub root_offset: Option, + pub observed_entry_count: usize, + pub entries: Vec, +} + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct SmpLoadedSpecialConditionsTable { pub source_kind: String, @@ -1808,6 +1835,8 @@ pub struct SmpLoadedSaveSlice { pub named_locomotive_availability_table: Option, #[serde(default)] pub locomotive_catalog: Option, + #[serde(default)] + pub cargo_catalog: Option, pub special_conditions_table: Option, pub event_runtime_collection: Option, pub notes: Vec, @@ -1972,6 +2001,10 @@ pub fn load_save_slice_from_report( let locomotive_catalog = named_locomotive_availability_table .as_ref() .and_then(derive_locomotive_catalog_from_named_availability_table); + let cargo_catalog = report + .recipe_book_summary_probe + .as_ref() + .and_then(derive_cargo_catalog_from_recipe_book_probe); let special_conditions_table = report .special_conditions_probe @@ -1996,6 +2029,7 @@ pub fn load_save_slice_from_report( candidate_availability_table, named_locomotive_availability_table, locomotive_catalog, + cargo_catalog, special_conditions_table, event_runtime_collection: report.event_runtime_collection_summary.clone(), notes: summary.notes.clone(), @@ -2028,6 +2062,57 @@ fn derive_locomotive_catalog_from_named_availability_table( }) } +fn derive_cargo_catalog_from_recipe_book_probe( + probe: &SmpRecipeBookSummaryProbe, +) -> Option { + if probe.books.is_empty() { + return None; + } + + let entries = probe + .books + .iter() + .filter(|book| book.book_index < 11) + .filter_map(|book| { + let line = book + .lines + .iter() + .find(|line| line.imports_to_runtime_descriptor) + .or_else(|| book.lines.first())?; + let slot_id = (book.book_index + 1) as u32; + Some(SmpLoadedCargoCatalogEntry { + slot_id, + label: format!("Cargo Production Slot {slot_id}"), + book_index: book.book_index, + max_annual_production_word: book.max_annual_production_word, + mode_word: line.mode_word, + runtime_import_branch_kind: line.runtime_import_branch_kind.clone(), + annual_amount_word: line.annual_amount_word, + supplied_cargo_token_word: line.supplied_cargo_token_word, + supplied_cargo_token_probable_high16_ascii_stem: line + .supplied_cargo_token_probable_high16_ascii_stem + .clone(), + demanded_cargo_token_word: line.demanded_cargo_token_word, + demanded_cargo_token_probable_high16_ascii_stem: line + .demanded_cargo_token_probable_high16_ascii_stem + .clone(), + }) + }) + .collect::>(); + + if entries.is_empty() { + return None; + } + + Some(SmpLoadedCargoCatalog { + source_kind: format!("{}-slot-catalog", probe.source_kind), + semantic_family: "scenario-save-derived-cargo-catalog".to_string(), + root_offset: Some(probe.root_offset), + observed_entry_count: entries.len(), + entries, + }) +} + fn parse_event_runtime_collection_summary( bytes: &[u8], container_profile: Option<&SmpContainerProfile>, diff --git a/crates/rrt-runtime/src/step.rs b/crates/rrt-runtime/src/step.rs index d5d5892..001d9e3 100644 --- a/crates/rrt-runtime/src/step.rs +++ b/crates/rrt-runtime/src/step.rs @@ -1249,6 +1249,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), diff --git a/crates/rrt-runtime/src/summary.rs b/crates/rrt-runtime/src/summary.rs index a4d1cc6..f72e22a 100644 --- a/crates/rrt-runtime/src/summary.rs +++ b/crates/rrt-runtime/src/summary.rs @@ -37,6 +37,7 @@ pub struct RuntimeSummary { pub active_train_count: usize, pub retired_train_count: usize, pub locomotive_catalog_count: usize, + pub cargo_catalog_count: usize, pub territory_count: usize, pub company_territory_track_count: usize, pub packed_event_collection_present: bool, @@ -172,6 +173,7 @@ impl RuntimeSummary { active_train_count: state.trains.iter().filter(|train| train.active).count(), retired_train_count: state.trains.iter().filter(|train| train.retired).count(), locomotive_catalog_count: state.locomotive_catalog.len(), + cargo_catalog_count: state.cargo_catalog.len(), territory_count: state.territories.len(), company_territory_track_count: state.company_territory_track_piece_counts.len(), packed_event_collection_present: state.packed_event_collection.is_some(), @@ -675,6 +677,7 @@ mod tests { name: "Locomotive 112".to_string(), }, ], + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -917,6 +920,7 @@ mod tests { name: "Locomotive 112".to_string(), }, ], + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -963,6 +967,7 @@ mod tests { name: "Locomotive 112".to_string(), }, ], + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -1006,6 +1011,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -1049,6 +1055,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -1087,6 +1094,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), @@ -1194,6 +1202,7 @@ mod tests { selected_player_id: None, trains: Vec::new(), locomotive_catalog: Vec::new(), + cargo_catalog: Vec::new(), territories: Vec::new(), company_territory_track_piece_counts: Vec::new(), company_territory_access: Vec::new(), diff --git a/docs/README.md b/docs/README.md index 20c20fd..e530e56 100644 --- a/docs/README.md +++ b/docs/README.md @@ -138,12 +138,16 @@ The highest-value next passes are now: - cargo-production `230..240` and territory-access-cost `453` now execute too through minimal world-side scalar landing surfaces: slot-indexed `cargo_production_overrides` and `world_restore.territory_access_cost` +- recipe-book probing now derives a save-native `cargo_catalog` too, so save-slice documents carry + stable cargo slot labels and token-stem evidence into runtime state without requiring a separate + cargo simulation layer - world-scalar ordinary-condition coverage now matches those runtime surfaces too: checked-in metadata lowers named locomotive availability, named locomotive cost, named cargo-production slot thresholds, aggregate cargo production, limited-track-building-amount, and territory-access-cost rows into explicit runtime condition gates - the remaining world-scalar condition frontier is now narrow and explicit: unsupported families - such as `All Factory Production` stay parity-only on `blocked_unmapped_world_condition` + such as `All Factory Production` stay parity-only on `blocked_unmapped_world_condition`; the + remaining cargo frontier is slot-to-class classification, not save/import plumbing - keep in mind that the current local `.gms` corpus still exports with no packed event collection, so real descriptor mapping needs to stay plumbing-first until better captures exist - use `rrt-hook` primarily as optional capture or integration tooling, not as the first execution diff --git a/docs/runtime-rehost-plan.md b/docs/runtime-rehost-plan.md index 481ffba..4f630ae 100644 --- a/docs/runtime-rehost-plan.md +++ b/docs/runtime-rehost-plan.md @@ -97,12 +97,16 @@ Implemented today: - the remaining recovered scalar world families now execute as well: cargo-production `230..240` rows lower into slot-indexed `cargo_production_overrides`, and territory-access-cost descriptor `453` lowers into `world_restore.territory_access_cost` +- cargo slot identity is now save-native as well: the recipe-book probe derives a bounded + `cargo_catalog` into save-slice documents and runtime state, so cargo descriptors and + `%1 Production` conditions can expose stable slot metadata without a live cargo simulation layer - world-scalar ordinary-condition coverage now aligns with those runtime surfaces too: checked-in metadata lowers named locomotive availability, named locomotive cost, named cargo-production slot thresholds, aggregate cargo production, limited-track-building-amount, and territory-access-cost rows into explicit runtime condition gates - the remaining world-side condition frontier is now narrower and more honest: unsupported scalar - families such as `All Factory Production` stay visible on `blocked_unmapped_world_condition` + families such as `All Factory Production` stay visible on `blocked_unmapped_world_condition`; + the remaining cargo frontier is slot classification rather than missing save/import context instead of falling back to generic placeholder ids That means the next implementation work is breadth, not bootstrap. The recommended next slice is diff --git a/fixtures/runtime/packed-event-cargo-catalog-save-slice-fixture.json b/fixtures/runtime/packed-event-cargo-catalog-save-slice-fixture.json new file mode 100644 index 0000000..44a618f --- /dev/null +++ b/fixtures/runtime/packed-event-cargo-catalog-save-slice-fixture.json @@ -0,0 +1,44 @@ +{ + "format_version": 1, + "fixture_id": "packed-event-cargo-catalog-save-slice-fixture", + "source": { + "kind": "captured-runtime", + "description": "Fixture proving recipe-book-derived cargo catalog context imports into runtime state even without executable event records." + }, + "state_save_slice_path": "packed-event-cargo-catalog-save-slice.json", + "commands": [ + { + "kind": "step_count", + "steps": 1 + } + ], + "expected_summary": { + "calendar": { + "year": 1830, + "month_slot": 0, + "phase_slot": 0, + "tick_slot": 1 + }, + "calendar_projection_is_placeholder": true, + "world_flag_count": 8, + "cargo_catalog_count": 11, + "packed_event_collection_present": false, + "event_runtime_record_count": 0 + }, + "expected_state_fragment": { + "cargo_catalog": [ + { + "slot_id": 1, + "label": "Cargo Production Slot 1" + }, + { + "slot_id": 2, + "label": "Cargo Production Slot 2" + } + ], + "metadata": { + "save_slice.cargo_catalog_source_kind": "recipe-book-summary-slot-catalog", + "save_slice.cargo_catalog_entry_count": "11" + } + } +} diff --git a/fixtures/runtime/packed-event-cargo-catalog-save-slice.json b/fixtures/runtime/packed-event-cargo-catalog-save-slice.json new file mode 100644 index 0000000..dcc1093 --- /dev/null +++ b/fixtures/runtime/packed-event-cargo-catalog-save-slice.json @@ -0,0 +1,48 @@ +{ + "format_version": 1, + "save_slice_id": "packed-event-cargo-catalog-save-slice", + "source": { + "description": "Tracked save-slice document proving save-native cargo catalog export/import without executable packed-event commands.", + "original_save_filename": "captured-cargo-catalog.gms", + "original_save_sha256": "cargo-catalog-sample-sha256", + "notes": [ + "tracked as JSON save-slice document rather than raw .smp", + "pins the save-native recipe-book-derived cargo catalog surface independently of event execution" + ] + }, + "save_slice": { + "file_extension_hint": "gms", + "container_profile_family": "rt3-classic-save-container-v1", + "mechanism_family": "classic-save-rehydrate-v1", + "mechanism_confidence": "grounded", + "trailer_family": null, + "bridge_family": null, + "profile": null, + "candidate_availability_table": null, + "named_locomotive_availability_table": null, + "cargo_catalog": { + "source_kind": "recipe-book-summary-slot-catalog", + "semantic_family": "scenario-save-derived-cargo-catalog", + "root_offset": 4071, + "observed_entry_count": 11, + "entries": [ + { "slot_id": 1, "label": "Cargo Production Slot 1", "book_index": 0, "max_annual_production_word": 0, "mode_word": 1114112, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 2, "label": "Cargo Production Slot 2", "book_index": 1, "max_annual_production_word": 0, "mode_word": 720896, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 3, "label": "Cargo Production Slot 3", "book_index": 2, "max_annual_production_word": 0, "mode_word": 720896, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 4, "label": "Cargo Production Slot 4", "book_index": 3, "max_annual_production_word": 0, "mode_word": 1245184, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 5, "label": "Cargo Production Slot 5", "book_index": 4, "max_annual_production_word": 0, "mode_word": 1572864, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 6, "label": "Cargo Production Slot 6", "book_index": 5, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 7, "label": "Cargo Production Slot 7", "book_index": 6, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 8, "label": "Cargo Production Slot 8", "book_index": 7, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 9, "label": "Cargo Production Slot 9", "book_index": 8, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 10, "label": "Cargo Production Slot 10", "book_index": 9, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 11, "label": "Cargo Production Slot 11", "book_index": 10, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null } + ] + }, + "special_conditions_table": null, + "event_runtime_collection": null, + "notes": [ + "cargo catalog only sample" + ] + } +} diff --git a/fixtures/runtime/packed-event-selective-import-overlay-fixture.json b/fixtures/runtime/packed-event-selective-import-overlay-fixture.json index dbdcbe7..1ed4dc0 100644 --- a/fixtures/runtime/packed-event-selective-import-overlay-fixture.json +++ b/fixtures/runtime/packed-event-selective-import-overlay-fixture.json @@ -21,7 +21,7 @@ }, "calendar_projection_source": "base-snapshot-preserved", "calendar_projection_is_placeholder": false, - "world_flag_count": 9, + "world_flag_count": 10, "company_count": 1, "packed_event_collection_present": true, "packed_event_record_count": 2, diff --git a/fixtures/runtime/packed-event-world-scalar-band-parity-save-slice-fixture.json b/fixtures/runtime/packed-event-world-scalar-band-parity-save-slice-fixture.json index 7092571..21328a5 100644 --- a/fixtures/runtime/packed-event-world-scalar-band-parity-save-slice-fixture.json +++ b/fixtures/runtime/packed-event-world-scalar-band-parity-save-slice-fixture.json @@ -26,6 +26,7 @@ "packed_event_imported_runtime_record_count": 2, "packed_event_parity_only_record_count": 3, "packed_event_unsupported_record_count": 0, + "cargo_catalog_count": 11, "packed_event_blocked_missing_locomotive_catalog_context_count": 0, "packed_event_blocked_missing_condition_context_count": 0, "packed_event_blocked_territory_condition_scope_count": 0, @@ -52,6 +53,8 @@ { "descriptor_id": 230, "descriptor_label": "Cargo Production Slot 1", + "recovered_cargo_slot": 1, + "recovered_cargo_label": "Cargo Production Slot 1", "target_mask_bits": 8, "parameter_family": "cargo_production_scalar", "semantic_family": "scalar_assignment", diff --git a/fixtures/runtime/packed-event-world-scalar-band-parity-save-slice.json b/fixtures/runtime/packed-event-world-scalar-band-parity-save-slice.json index b65616b..cd43fe8 100644 --- a/fixtures/runtime/packed-event-world-scalar-band-parity-save-slice.json +++ b/fixtures/runtime/packed-event-world-scalar-band-parity-save-slice.json @@ -20,6 +20,25 @@ "profile": null, "candidate_availability_table": null, "named_locomotive_availability_table": null, + "cargo_catalog": { + "source_kind": "recipe-book-summary-slot-catalog", + "semantic_family": "scenario-save-derived-cargo-catalog", + "root_offset": 4071, + "observed_entry_count": 11, + "entries": [ + { "slot_id": 1, "label": "Cargo Production Slot 1", "book_index": 0, "max_annual_production_word": 0, "mode_word": 1114112, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 2, "label": "Cargo Production Slot 2", "book_index": 1, "max_annual_production_word": 0, "mode_word": 720896, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 3, "label": "Cargo Production Slot 3", "book_index": 2, "max_annual_production_word": 0, "mode_word": 720896, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 4, "label": "Cargo Production Slot 4", "book_index": 3, "max_annual_production_word": 0, "mode_word": 1245184, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 5, "label": "Cargo Production Slot 5", "book_index": 4, "max_annual_production_word": 0, "mode_word": 1572864, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 6, "label": "Cargo Production Slot 6", "book_index": 5, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 7, "label": "Cargo Production Slot 7", "book_index": 6, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 8, "label": "Cargo Production Slot 8", "book_index": 7, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 9, "label": "Cargo Production Slot 9", "book_index": 8, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 10, "label": "Cargo Production Slot 10", "book_index": 9, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 11, "label": "Cargo Production Slot 11", "book_index": 10, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null } + ] + }, "special_conditions_table": null, "event_runtime_collection": { "source_kind": "packed-event-runtime-collection", diff --git a/fixtures/runtime/packed-event-world-scalar-condition-parity-save-slice-fixture.json b/fixtures/runtime/packed-event-world-scalar-condition-parity-save-slice-fixture.json index b323c99..3ead76f 100644 --- a/fixtures/runtime/packed-event-world-scalar-condition-parity-save-slice-fixture.json +++ b/fixtures/runtime/packed-event-world-scalar-condition-parity-save-slice-fixture.json @@ -18,17 +18,24 @@ "packed_event_decoded_record_count": 1, "packed_event_imported_runtime_record_count": 0, "packed_event_parity_only_record_count": 1, - "packed_event_blocked_unmapped_world_descriptor_count": 1, + "packed_event_blocked_unmapped_world_condition_count": 1, + "cargo_catalog_count": 11, "event_runtime_record_count": 0, - "world_flag_count": 7, + "world_flag_count": 8, "total_event_record_service_count": 0, "total_trigger_dispatch_count": 1 }, "expected_state_fragment": { + "cargo_catalog": [ + { + "slot_id": 1, + "label": "Cargo Production Slot 1" + } + ], "packed_event_collection": { "records": [ { - "import_outcome": "blocked_unmapped_world_descriptor", + "import_outcome": "blocked_unmapped_world_condition", "standalone_condition_rows": [ { "raw_condition_id": 2419, diff --git a/fixtures/runtime/packed-event-world-scalar-condition-parity-save-slice.json b/fixtures/runtime/packed-event-world-scalar-condition-parity-save-slice.json index 90307a3..bef90fa 100644 --- a/fixtures/runtime/packed-event-world-scalar-condition-parity-save-slice.json +++ b/fixtures/runtime/packed-event-world-scalar-condition-parity-save-slice.json @@ -20,6 +20,25 @@ "profile": null, "candidate_availability_table": null, "named_locomotive_availability_table": null, + "cargo_catalog": { + "source_kind": "recipe-book-summary-slot-catalog", + "semantic_family": "scenario-save-derived-cargo-catalog", + "root_offset": 4071, + "observed_entry_count": 11, + "entries": [ + { "slot_id": 1, "label": "Cargo Production Slot 1", "book_index": 0, "max_annual_production_word": 0, "mode_word": 1114112, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 2, "label": "Cargo Production Slot 2", "book_index": 1, "max_annual_production_word": 0, "mode_word": 720896, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 3, "label": "Cargo Production Slot 3", "book_index": 2, "max_annual_production_word": 0, "mode_word": 720896, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 4, "label": "Cargo Production Slot 4", "book_index": 3, "max_annual_production_word": 0, "mode_word": 1245184, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 5, "label": "Cargo Production Slot 5", "book_index": 4, "max_annual_production_word": 0, "mode_word": 1572864, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 6, "label": "Cargo Production Slot 6", "book_index": 5, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 7, "label": "Cargo Production Slot 7", "book_index": 6, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 8, "label": "Cargo Production Slot 8", "book_index": 7, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 9, "label": "Cargo Production Slot 9", "book_index": 8, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 10, "label": "Cargo Production Slot 10", "book_index": 9, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 11, "label": "Cargo Production Slot 11", "book_index": 10, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null } + ] + }, "special_conditions_table": null, "event_runtime_collection": { "source_kind": "packed-event-runtime-collection", diff --git a/fixtures/runtime/packed-event-world-scalar-condition-save-slice-fixture.json b/fixtures/runtime/packed-event-world-scalar-condition-save-slice-fixture.json index 26d96d2..2275e12 100644 --- a/fixtures/runtime/packed-event-world-scalar-condition-save-slice-fixture.json +++ b/fixtures/runtime/packed-event-world-scalar-condition-save-slice-fixture.json @@ -22,13 +22,14 @@ "packed_event_decoded_record_count": 2, "packed_event_imported_runtime_record_count": 2, "event_runtime_record_count": 2, + "cargo_catalog_count": 11, "named_locomotive_availability_count": 1, "zero_named_locomotive_availability_count": 0, "named_locomotive_cost_count": 1, "cargo_production_override_count": 1, "world_restore_limited_track_building_amount": 18, "world_restore_territory_access_cost": 750000, - "world_flag_count": 8, + "world_flag_count": 9, "total_event_record_service_count": 2, "total_trigger_dispatch_count": 2 }, @@ -49,6 +50,12 @@ "cargo_production_overrides": { "1": 125 }, + "cargo_catalog": [ + { + "slot_id": 1, + "label": "Cargo Production Slot 1" + } + ], "packed_event_collection": { "records": [ { diff --git a/fixtures/runtime/packed-event-world-scalar-condition-save-slice.json b/fixtures/runtime/packed-event-world-scalar-condition-save-slice.json index 65b28f5..f9bf4a0 100644 --- a/fixtures/runtime/packed-event-world-scalar-condition-save-slice.json +++ b/fixtures/runtime/packed-event-world-scalar-condition-save-slice.json @@ -20,6 +20,25 @@ "profile": null, "candidate_availability_table": null, "named_locomotive_availability_table": null, + "cargo_catalog": { + "source_kind": "recipe-book-summary-slot-catalog", + "semantic_family": "scenario-save-derived-cargo-catalog", + "root_offset": 4071, + "observed_entry_count": 11, + "entries": [ + { "slot_id": 1, "label": "Cargo Production Slot 1", "book_index": 0, "max_annual_production_word": 0, "mode_word": 1114112, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 2, "label": "Cargo Production Slot 2", "book_index": 1, "max_annual_production_word": 0, "mode_word": 720896, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 3, "label": "Cargo Production Slot 3", "book_index": 2, "max_annual_production_word": 0, "mode_word": 720896, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 4, "label": "Cargo Production Slot 4", "book_index": 3, "max_annual_production_word": 0, "mode_word": 1245184, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 5, "label": "Cargo Production Slot 5", "book_index": 4, "max_annual_production_word": 0, "mode_word": 1572864, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 6, "label": "Cargo Production Slot 6", "book_index": 5, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 7, "label": "Cargo Production Slot 7", "book_index": 6, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 8, "label": "Cargo Production Slot 8", "book_index": 7, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 9, "label": "Cargo Production Slot 9", "book_index": 8, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 10, "label": "Cargo Production Slot 10", "book_index": 9, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 11, "label": "Cargo Production Slot 11", "book_index": 10, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null } + ] + }, "special_conditions_table": null, "event_runtime_collection": { "source_kind": "packed-event-runtime-collection", diff --git a/fixtures/runtime/packed-event-world-scalar-executable-save-slice-fixture.json b/fixtures/runtime/packed-event-world-scalar-executable-save-slice-fixture.json index 24e3227..ca4c34a 100644 --- a/fixtures/runtime/packed-event-world-scalar-executable-save-slice-fixture.json +++ b/fixtures/runtime/packed-event-world-scalar-executable-save-slice-fixture.json @@ -25,6 +25,7 @@ "packed_event_decoded_record_count": 1, "packed_event_imported_runtime_record_count": 1, "packed_event_parity_only_record_count": 1, + "cargo_catalog_count": 11, "packed_event_blocked_unmapped_world_descriptor_count": 0, "event_runtime_record_count": 1, "cargo_production_override_count": 1, diff --git a/fixtures/runtime/packed-event-world-scalar-executable-save-slice.json b/fixtures/runtime/packed-event-world-scalar-executable-save-slice.json index f871272..02b3bb5 100644 --- a/fixtures/runtime/packed-event-world-scalar-executable-save-slice.json +++ b/fixtures/runtime/packed-event-world-scalar-executable-save-slice.json @@ -20,6 +20,25 @@ "profile": null, "candidate_availability_table": null, "named_locomotive_availability_table": null, + "cargo_catalog": { + "source_kind": "recipe-book-summary-slot-catalog", + "semantic_family": "scenario-save-derived-cargo-catalog", + "root_offset": 4071, + "observed_entry_count": 11, + "entries": [ + { "slot_id": 1, "label": "Cargo Production Slot 1", "book_index": 0, "max_annual_production_word": 0, "mode_word": 1114112, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 2, "label": "Cargo Production Slot 2", "book_index": 1, "max_annual_production_word": 0, "mode_word": 720896, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 3, "label": "Cargo Production Slot 3", "book_index": 2, "max_annual_production_word": 0, "mode_word": 720896, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 4, "label": "Cargo Production Slot 4", "book_index": 3, "max_annual_production_word": 0, "mode_word": 1245184, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 5, "label": "Cargo Production Slot 5", "book_index": 4, "max_annual_production_word": 0, "mode_word": 1572864, "runtime_import_branch_kind": "nonzero-supply-branch", "annual_amount_word": 0, "supplied_cargo_token_word": 16544, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 6, "label": "Cargo Production Slot 6", "book_index": 5, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 7, "label": "Cargo Production Slot 7", "book_index": 6, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 8, "label": "Cargo Production Slot 8", "book_index": 7, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 9, "label": "Cargo Production Slot 9", "book_index": 8, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 10, "label": "Cargo Production Slot 10", "book_index": 9, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null }, + { "slot_id": 11, "label": "Cargo Production Slot 11", "book_index": 10, "max_annual_production_word": 0, "mode_word": 0, "runtime_import_branch_kind": "zero-mode-skipped", "annual_amount_word": 0, "supplied_cargo_token_word": 0, "supplied_cargo_token_probable_high16_ascii_stem": null, "demanded_cargo_token_word": 0, "demanded_cargo_token_probable_high16_ascii_stem": null } + ] + }, "special_conditions_table": null, "event_runtime_collection": { "source_kind": "packed-event-runtime-collection",