Import locomotive availability descriptors with overlay context

This commit is contained in:
Jan Petykiewicz 2026-04-16 10:50:13 -07:00
commit 87108f357b
21 changed files with 1154 additions and 27 deletions

View file

@ -35,6 +35,7 @@ pub struct RuntimeSummary {
pub train_count: usize,
pub active_train_count: usize,
pub retired_train_count: usize,
pub locomotive_catalog_count: usize,
pub territory_count: usize,
pub company_territory_track_count: usize,
pub packed_event_collection_present: bool,
@ -65,6 +66,7 @@ pub struct RuntimeSummary {
pub packed_event_blocked_territory_access_scope_count: usize,
pub packed_event_blocked_missing_train_context_count: usize,
pub packed_event_blocked_missing_train_territory_context_count: usize,
pub packed_event_blocked_missing_locomotive_catalog_context_count: usize,
pub packed_event_blocked_confiscation_variant_count: usize,
pub packed_event_blocked_retire_train_variant_count: usize,
pub packed_event_blocked_retire_train_scope_count: usize,
@ -165,6 +167,7 @@ impl RuntimeSummary {
train_count: state.trains.len(),
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(),
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(),
@ -513,6 +516,20 @@ impl RuntimeSummary {
.count()
})
.unwrap_or(0),
packed_event_blocked_missing_locomotive_catalog_context_count: state
.packed_event_collection
.as_ref()
.map(|summary| {
summary
.records
.iter()
.filter(|record| {
record.import_outcome.as_deref()
== Some("blocked_missing_locomotive_catalog_context")
})
.count()
})
.unwrap_or(0),
packed_event_blocked_confiscation_variant_count: state
.packed_event_collection
.as_ref()
@ -642,6 +659,16 @@ mod tests {
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
locomotive_catalog: vec![
crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 10,
name: "Locomotive 10".to_string(),
},
crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 112,
name: "Locomotive 112".to_string(),
},
],
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
company_territory_access: Vec::new(),
@ -872,6 +899,16 @@ mod tests {
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
locomotive_catalog: vec![
crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 10,
name: "Locomotive 10".to_string(),
},
crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 112,
name: "Locomotive 112".to_string(),
},
],
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
company_territory_access: Vec::new(),
@ -906,6 +943,16 @@ mod tests {
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
locomotive_catalog: vec![
crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 10,
name: "Locomotive 10".to_string(),
},
crate::RuntimeLocomotiveCatalogEntry {
locomotive_id: 112,
name: "Locomotive 112".to_string(),
},
],
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
company_territory_access: Vec::new(),
@ -922,6 +969,7 @@ mod tests {
};
let summary = RuntimeSummary::from_state(&state);
assert_eq!(summary.locomotive_catalog_count, 2);
assert_eq!(summary.named_locomotive_availability_count, 3);
assert_eq!(summary.zero_named_locomotive_availability_count, 2);
}
@ -944,6 +992,7 @@ mod tests {
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
locomotive_catalog: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
company_territory_access: Vec::new(),
@ -1029,4 +1078,78 @@ mod tests {
1
);
}
#[test]
fn counts_missing_locomotive_catalog_context_frontier() {
let state = RuntimeState {
calendar: CalendarPoint {
year: 1830,
month_slot: 0,
phase_slot: 0,
tick_slot: 0,
},
world_flags: BTreeMap::new(),
save_profile: RuntimeSaveProfileState::default(),
world_restore: RuntimeWorldRestoreState::default(),
metadata: BTreeMap::new(),
companies: Vec::new(),
selected_company_id: None,
players: Vec::new(),
selected_player_id: None,
trains: Vec::new(),
locomotive_catalog: Vec::new(),
territories: Vec::new(),
company_territory_track_piece_counts: Vec::new(),
company_territory_access: Vec::new(),
packed_event_collection: Some(RuntimePackedEventCollectionSummary {
source_kind: "packed-event-runtime-collection".to_string(),
mechanism_family: "classic-save-rehydrate-v1".to_string(),
mechanism_confidence: "grounded".to_string(),
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
packed_state_version: 0x3e9,
packed_state_version_hex: "0x000003e9".to_string(),
live_id_bound: 1,
live_record_count: 1,
live_entry_ids: vec![1],
decoded_record_count: 1,
imported_runtime_record_count: 0,
records: vec![RuntimePackedEventRecordSummary {
record_index: 0,
live_entry_id: 1,
payload_offset: Some(0x7202),
payload_len: Some(96),
decode_status: "parity_only".to_string(),
payload_family: "real_packed_v1".to_string(),
trigger_kind: Some(7),
active: None,
marks_collection_dirty: None,
one_shot: None,
compact_control: None,
text_bands: Vec::new(),
standalone_condition_row_count: 0,
standalone_condition_rows: Vec::new(),
negative_sentinel_scope: None,
grouped_effect_row_counts: vec![1, 0, 0, 0],
grouped_effect_rows: Vec::new(),
grouped_company_targets: Vec::new(),
decoded_conditions: Vec::new(),
decoded_actions: Vec::new(),
executable_import_ready: false,
import_outcome: Some("blocked_missing_locomotive_catalog_context".to_string()),
notes: Vec::new(),
}],
}),
event_runtime_records: Vec::new(),
candidate_availability: BTreeMap::new(),
named_locomotive_availability: BTreeMap::new(),
special_conditions: BTreeMap::new(),
service_state: RuntimeServiceState::default(),
};
let summary = RuntimeSummary::from_state(&state);
assert_eq!(
summary.packed_event_blocked_missing_locomotive_catalog_context_count,
1
);
}
}