2026-04-10 01:22:47 -07:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
|
|
use crate::{CalendarPoint, RuntimeState};
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
|
|
|
|
pub struct RuntimeSummary {
|
|
|
|
|
pub calendar: CalendarPoint,
|
2026-04-11 18:12:25 -07:00
|
|
|
pub calendar_projection_source: Option<String>,
|
|
|
|
|
pub calendar_projection_is_placeholder: bool,
|
2026-04-10 01:22:47 -07:00
|
|
|
pub world_flag_count: usize,
|
2026-04-11 18:12:25 -07:00
|
|
|
pub world_restore_selected_year_profile_lane: Option<u8>,
|
|
|
|
|
pub world_restore_campaign_scenario_enabled: Option<bool>,
|
|
|
|
|
pub world_restore_sandbox_enabled: Option<bool>,
|
|
|
|
|
pub world_restore_seed_tuple_written_from_raw_lane: Option<bool>,
|
|
|
|
|
pub world_restore_absolute_counter_requires_shell_context: Option<bool>,
|
|
|
|
|
pub world_restore_absolute_counter_reconstructible_from_save: Option<bool>,
|
|
|
|
|
pub world_restore_disable_cargo_economy_special_condition_slot: Option<u8>,
|
|
|
|
|
pub world_restore_disable_cargo_economy_special_condition_reconstructible_from_save:
|
|
|
|
|
Option<bool>,
|
|
|
|
|
pub world_restore_disable_cargo_economy_special_condition_write_side_grounded: Option<bool>,
|
|
|
|
|
pub world_restore_disable_cargo_economy_special_condition_enabled: Option<bool>,
|
|
|
|
|
pub world_restore_use_bio_accelerator_cars_enabled: Option<bool>,
|
|
|
|
|
pub world_restore_use_wartime_cargos_enabled: Option<bool>,
|
|
|
|
|
pub world_restore_disable_train_crashes_enabled: Option<bool>,
|
|
|
|
|
pub world_restore_disable_train_crashes_and_breakdowns_enabled: Option<bool>,
|
|
|
|
|
pub world_restore_ai_ignore_territories_at_startup_enabled: Option<bool>,
|
2026-04-16 09:20:49 -07:00
|
|
|
pub world_restore_limited_track_building_amount: Option<i32>,
|
2026-04-15 20:20:25 -07:00
|
|
|
pub world_restore_economic_status_code: Option<i32>,
|
2026-04-11 18:12:25 -07:00
|
|
|
pub world_restore_absolute_counter_restore_kind: Option<String>,
|
|
|
|
|
pub world_restore_absolute_counter_adjustment_context: Option<String>,
|
|
|
|
|
pub metadata_count: usize,
|
2026-04-10 01:22:47 -07:00
|
|
|
pub company_count: usize,
|
2026-04-15 12:11:29 -07:00
|
|
|
pub active_company_count: usize,
|
2026-04-15 19:15:47 -07:00
|
|
|
pub player_count: usize,
|
2026-04-15 20:20:25 -07:00
|
|
|
pub train_count: usize,
|
|
|
|
|
pub active_train_count: usize,
|
|
|
|
|
pub retired_train_count: usize,
|
2026-04-16 10:50:13 -07:00
|
|
|
pub locomotive_catalog_count: usize,
|
2026-04-15 18:27:04 -07:00
|
|
|
pub territory_count: usize,
|
|
|
|
|
pub company_territory_track_count: usize,
|
2026-04-14 20:01:43 -07:00
|
|
|
pub packed_event_collection_present: bool,
|
|
|
|
|
pub packed_event_record_count: usize,
|
2026-04-14 20:35:07 -07:00
|
|
|
pub packed_event_decoded_record_count: usize,
|
|
|
|
|
pub packed_event_imported_runtime_record_count: usize,
|
2026-04-14 20:51:27 -07:00
|
|
|
pub packed_event_parity_only_record_count: usize,
|
|
|
|
|
pub packed_event_unsupported_record_count: usize,
|
2026-04-14 21:19:08 -07:00
|
|
|
pub packed_event_blocked_missing_company_context_count: usize,
|
2026-04-15 09:13:51 -07:00
|
|
|
pub packed_event_blocked_missing_selection_context_count: usize,
|
|
|
|
|
pub packed_event_blocked_missing_company_role_context_count: usize,
|
2026-04-15 19:15:47 -07:00
|
|
|
pub packed_event_blocked_missing_player_context_count: usize,
|
|
|
|
|
pub packed_event_blocked_missing_player_selection_context_count: usize,
|
|
|
|
|
pub packed_event_blocked_missing_player_role_context_count: usize,
|
2026-04-15 09:13:51 -07:00
|
|
|
pub packed_event_blocked_missing_condition_context_count: usize,
|
2026-04-15 19:15:47 -07:00
|
|
|
pub packed_event_blocked_missing_player_condition_context_count: usize,
|
2026-04-15 14:21:12 -07:00
|
|
|
pub packed_event_blocked_company_condition_scope_disabled_count: usize,
|
|
|
|
|
pub packed_event_blocked_player_condition_scope_count: usize,
|
|
|
|
|
pub packed_event_blocked_territory_condition_scope_count: usize,
|
2026-04-15 18:27:04 -07:00
|
|
|
pub packed_event_blocked_missing_territory_context_count: usize,
|
|
|
|
|
pub packed_event_blocked_named_territory_binding_count: usize,
|
|
|
|
|
pub packed_event_blocked_unmapped_ordinary_condition_count: usize,
|
2026-04-15 21:41:40 -07:00
|
|
|
pub packed_event_blocked_unmapped_world_condition_count: usize,
|
2026-04-14 23:01:18 -07:00
|
|
|
pub packed_event_blocked_missing_compact_control_count: usize,
|
|
|
|
|
pub packed_event_blocked_unmapped_real_descriptor_count: usize,
|
2026-04-15 21:41:40 -07:00
|
|
|
pub packed_event_blocked_unmapped_world_descriptor_count: usize,
|
2026-04-15 20:53:35 -07:00
|
|
|
pub packed_event_blocked_territory_access_variant_count: usize,
|
|
|
|
|
pub packed_event_blocked_territory_access_scope_count: usize,
|
2026-04-15 20:20:25 -07:00
|
|
|
pub packed_event_blocked_missing_train_context_count: usize,
|
|
|
|
|
pub packed_event_blocked_missing_train_territory_context_count: usize,
|
2026-04-16 10:50:13 -07:00
|
|
|
pub packed_event_blocked_missing_locomotive_catalog_context_count: usize,
|
2026-04-15 20:20:25 -07:00
|
|
|
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,
|
2026-04-14 22:09:09 -07:00
|
|
|
pub packed_event_blocked_structural_only_count: usize,
|
2026-04-10 01:22:47 -07:00
|
|
|
pub event_runtime_record_count: usize,
|
2026-04-11 18:12:25 -07:00
|
|
|
pub candidate_availability_count: usize,
|
|
|
|
|
pub zero_candidate_availability_count: usize,
|
2026-04-16 10:23:29 -07:00
|
|
|
pub named_locomotive_availability_count: usize,
|
|
|
|
|
pub zero_named_locomotive_availability_count: usize,
|
2026-04-16 11:19:53 -07:00
|
|
|
pub named_locomotive_cost_count: usize,
|
2026-04-11 18:12:25 -07:00
|
|
|
pub special_condition_count: usize,
|
|
|
|
|
pub enabled_special_condition_count: usize,
|
|
|
|
|
pub save_profile_kind: Option<String>,
|
|
|
|
|
pub save_profile_family: Option<String>,
|
|
|
|
|
pub save_profile_map_path: Option<String>,
|
|
|
|
|
pub save_profile_display_name: Option<String>,
|
|
|
|
|
pub save_profile_selected_year_profile_lane: Option<u8>,
|
|
|
|
|
pub save_profile_sandbox_enabled: Option<bool>,
|
|
|
|
|
pub save_profile_campaign_scenario_enabled: Option<bool>,
|
|
|
|
|
pub save_profile_staged_profile_copy_on_restore: Option<bool>,
|
2026-04-10 01:22:47 -07:00
|
|
|
pub total_event_record_service_count: u64,
|
|
|
|
|
pub periodic_boundary_call_count: u64,
|
|
|
|
|
pub total_trigger_dispatch_count: u64,
|
|
|
|
|
pub dirty_rerun_count: u64,
|
|
|
|
|
pub total_company_cash: i64,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RuntimeSummary {
|
|
|
|
|
pub fn from_state(state: &RuntimeState) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
calendar: state.calendar,
|
2026-04-11 18:12:25 -07:00
|
|
|
calendar_projection_source: state.metadata.get("save_slice.calendar_source").cloned(),
|
|
|
|
|
calendar_projection_is_placeholder: state
|
|
|
|
|
.metadata
|
|
|
|
|
.get("save_slice.calendar_source")
|
|
|
|
|
.is_some_and(|value| value == "default-1830-placeholder"),
|
2026-04-10 01:22:47 -07:00
|
|
|
world_flag_count: state.world_flags.len(),
|
2026-04-11 18:12:25 -07:00
|
|
|
world_restore_selected_year_profile_lane: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.selected_year_profile_lane,
|
|
|
|
|
world_restore_campaign_scenario_enabled: state.world_restore.campaign_scenario_enabled,
|
|
|
|
|
world_restore_sandbox_enabled: state.world_restore.sandbox_enabled,
|
|
|
|
|
world_restore_seed_tuple_written_from_raw_lane: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.seed_tuple_written_from_raw_lane,
|
|
|
|
|
world_restore_absolute_counter_requires_shell_context: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.absolute_counter_requires_shell_context,
|
|
|
|
|
world_restore_absolute_counter_reconstructible_from_save: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.absolute_counter_reconstructible_from_save,
|
|
|
|
|
world_restore_disable_cargo_economy_special_condition_slot: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.disable_cargo_economy_special_condition_slot,
|
|
|
|
|
world_restore_disable_cargo_economy_special_condition_reconstructible_from_save: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.disable_cargo_economy_special_condition_reconstructible_from_save,
|
|
|
|
|
world_restore_disable_cargo_economy_special_condition_write_side_grounded: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.disable_cargo_economy_special_condition_write_side_grounded,
|
|
|
|
|
world_restore_disable_cargo_economy_special_condition_enabled: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.disable_cargo_economy_special_condition_enabled,
|
|
|
|
|
world_restore_use_bio_accelerator_cars_enabled: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.use_bio_accelerator_cars_enabled,
|
|
|
|
|
world_restore_use_wartime_cargos_enabled: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.use_wartime_cargos_enabled,
|
|
|
|
|
world_restore_disable_train_crashes_enabled: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.disable_train_crashes_enabled,
|
|
|
|
|
world_restore_disable_train_crashes_and_breakdowns_enabled: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.disable_train_crashes_and_breakdowns_enabled,
|
|
|
|
|
world_restore_ai_ignore_territories_at_startup_enabled: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.ai_ignore_territories_at_startup_enabled,
|
2026-04-16 09:20:49 -07:00
|
|
|
world_restore_limited_track_building_amount: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.limited_track_building_amount,
|
2026-04-15 20:20:25 -07:00
|
|
|
world_restore_economic_status_code: state.world_restore.economic_status_code,
|
2026-04-11 18:12:25 -07:00
|
|
|
world_restore_absolute_counter_restore_kind: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.absolute_counter_restore_kind
|
|
|
|
|
.clone(),
|
|
|
|
|
world_restore_absolute_counter_adjustment_context: state
|
|
|
|
|
.world_restore
|
|
|
|
|
.absolute_counter_adjustment_context
|
|
|
|
|
.clone(),
|
|
|
|
|
metadata_count: state.metadata.len(),
|
2026-04-10 01:22:47 -07:00
|
|
|
company_count: state.companies.len(),
|
2026-04-15 14:21:12 -07:00
|
|
|
active_company_count: state
|
|
|
|
|
.companies
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|company| company.active)
|
|
|
|
|
.count(),
|
2026-04-15 19:15:47 -07:00
|
|
|
player_count: state.players.len(),
|
2026-04-15 20:20:25 -07:00
|
|
|
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(),
|
2026-04-16 10:50:13 -07:00
|
|
|
locomotive_catalog_count: state.locomotive_catalog.len(),
|
2026-04-15 18:27:04 -07:00
|
|
|
territory_count: state.territories.len(),
|
|
|
|
|
company_territory_track_count: state.company_territory_track_piece_counts.len(),
|
2026-04-14 20:01:43 -07:00
|
|
|
packed_event_collection_present: state.packed_event_collection.is_some(),
|
|
|
|
|
packed_event_record_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| summary.live_record_count)
|
|
|
|
|
.unwrap_or(0),
|
2026-04-14 20:35:07 -07:00
|
|
|
packed_event_decoded_record_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| summary.decoded_record_count)
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_imported_runtime_record_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| summary.imported_runtime_record_count)
|
|
|
|
|
.unwrap_or(0),
|
2026-04-14 20:51:27 -07:00
|
|
|
packed_event_parity_only_record_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| record.decode_status == "parity_only")
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_unsupported_record_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| record.decode_status == "unsupported_framing")
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-14 21:19:08 -07:00
|
|
|
packed_event_blocked_missing_company_context_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_company_context")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-15 09:13:51 -07:00
|
|
|
packed_event_blocked_missing_selection_context_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_selection_context")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_missing_company_role_context_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_company_role_context")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-15 19:15:47 -07:00
|
|
|
packed_event_blocked_missing_player_context_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_player_context")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_missing_player_selection_context_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_player_selection_context")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_missing_player_role_context_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_player_role_context")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-15 09:13:51 -07:00
|
|
|
packed_event_blocked_missing_condition_context_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_condition_context")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-15 19:15:47 -07:00
|
|
|
packed_event_blocked_missing_player_condition_context_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_player_condition_context")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-15 14:21:12 -07:00
|
|
|
packed_event_blocked_company_condition_scope_disabled_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_company_condition_scope_disabled")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_player_condition_scope_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_player_condition_scope")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_territory_condition_scope_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_territory_condition_scope")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-15 18:27:04 -07:00
|
|
|
packed_event_blocked_missing_territory_context_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_territory_context")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_named_territory_binding_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_named_territory_binding")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_unmapped_ordinary_condition_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_unmapped_ordinary_condition")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-15 21:41:40 -07:00
|
|
|
packed_event_blocked_unmapped_world_condition_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_unmapped_world_condition")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-14 23:01:18 -07:00
|
|
|
packed_event_blocked_missing_compact_control_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_compact_control")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_unmapped_real_descriptor_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_unmapped_real_descriptor")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-15 21:41:40 -07:00
|
|
|
packed_event_blocked_unmapped_world_descriptor_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_unmapped_world_descriptor")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-15 20:53:35 -07:00
|
|
|
packed_event_blocked_territory_access_variant_count: state
|
2026-04-15 19:15:47 -07:00
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
2026-04-15 20:53:35 -07:00
|
|
|
== Some("blocked_territory_access_variant")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_territory_access_scope_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_territory_access_scope")
|
2026-04-15 19:15:47 -07:00
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-15 20:20:25 -07:00
|
|
|
packed_event_blocked_missing_train_context_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_train_context")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_missing_train_territory_context_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref()
|
|
|
|
|
== Some("blocked_missing_train_territory_context")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-16 10:50:13 -07:00
|
|
|
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),
|
2026-04-15 20:20:25 -07:00
|
|
|
packed_event_blocked_confiscation_variant_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref() == Some("blocked_confiscation_variant")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_retire_train_variant_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref() == Some("blocked_retire_train_variant")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
|
|
|
|
packed_event_blocked_retire_train_scope_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref() == Some("blocked_retire_train_scope")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-14 22:09:09 -07:00
|
|
|
packed_event_blocked_structural_only_count: state
|
|
|
|
|
.packed_event_collection
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|summary| {
|
|
|
|
|
summary
|
|
|
|
|
.records
|
|
|
|
|
.iter()
|
|
|
|
|
.filter(|record| {
|
|
|
|
|
record.import_outcome.as_deref() == Some("blocked_structural_only")
|
|
|
|
|
})
|
|
|
|
|
.count()
|
|
|
|
|
})
|
|
|
|
|
.unwrap_or(0),
|
2026-04-10 01:22:47 -07:00
|
|
|
event_runtime_record_count: state.event_runtime_records.len(),
|
2026-04-11 18:12:25 -07:00
|
|
|
candidate_availability_count: state.candidate_availability.len(),
|
|
|
|
|
zero_candidate_availability_count: state
|
|
|
|
|
.candidate_availability
|
|
|
|
|
.values()
|
|
|
|
|
.filter(|value| **value == 0)
|
|
|
|
|
.count(),
|
2026-04-16 10:23:29 -07:00
|
|
|
named_locomotive_availability_count: state.named_locomotive_availability.len(),
|
|
|
|
|
zero_named_locomotive_availability_count: state
|
|
|
|
|
.named_locomotive_availability
|
|
|
|
|
.values()
|
|
|
|
|
.filter(|value| **value == 0)
|
|
|
|
|
.count(),
|
2026-04-16 11:19:53 -07:00
|
|
|
named_locomotive_cost_count: state.named_locomotive_cost.len(),
|
2026-04-11 18:12:25 -07:00
|
|
|
special_condition_count: state.special_conditions.len(),
|
|
|
|
|
enabled_special_condition_count: state
|
|
|
|
|
.special_conditions
|
|
|
|
|
.values()
|
|
|
|
|
.filter(|value| **value != 0)
|
|
|
|
|
.count(),
|
|
|
|
|
save_profile_kind: state.save_profile.profile_kind.clone(),
|
|
|
|
|
save_profile_family: state.save_profile.profile_family.clone(),
|
|
|
|
|
save_profile_map_path: state.save_profile.map_path.clone(),
|
|
|
|
|
save_profile_display_name: state.save_profile.display_name.clone(),
|
|
|
|
|
save_profile_selected_year_profile_lane: state.save_profile.selected_year_profile_lane,
|
|
|
|
|
save_profile_sandbox_enabled: state.save_profile.sandbox_enabled,
|
|
|
|
|
save_profile_campaign_scenario_enabled: state.save_profile.campaign_scenario_enabled,
|
|
|
|
|
save_profile_staged_profile_copy_on_restore: state
|
|
|
|
|
.save_profile
|
|
|
|
|
.staged_profile_copy_on_restore,
|
2026-04-10 01:22:47 -07:00
|
|
|
total_event_record_service_count: state.service_state.total_event_record_services,
|
|
|
|
|
periodic_boundary_call_count: state.service_state.periodic_boundary_calls,
|
|
|
|
|
total_trigger_dispatch_count: state
|
|
|
|
|
.service_state
|
|
|
|
|
.trigger_dispatch_counts
|
|
|
|
|
.values()
|
|
|
|
|
.sum(),
|
|
|
|
|
dirty_rerun_count: state.service_state.dirty_rerun_count,
|
|
|
|
|
total_company_cash: state
|
|
|
|
|
.companies
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|company| company.current_cash)
|
|
|
|
|
.sum(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-14 22:09:09 -07:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use std::collections::BTreeMap;
|
|
|
|
|
|
|
|
|
|
use crate::{
|
2026-04-15 12:11:29 -07:00
|
|
|
CalendarPoint, RuntimeCompany, RuntimeCompanyControllerKind,
|
|
|
|
|
RuntimePackedEventCollectionSummary, RuntimePackedEventRecordSummary,
|
2026-04-15 20:20:25 -07:00
|
|
|
RuntimeSaveProfileState, RuntimeServiceState, RuntimeState, RuntimeTrackPieceCounts,
|
|
|
|
|
RuntimeWorldRestoreState,
|
2026-04-14 22:09:09 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use super::RuntimeSummary;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn counts_structural_only_and_missing_context_frontiers() {
|
|
|
|
|
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(),
|
2026-04-15 09:13:51 -07:00
|
|
|
selected_company_id: None,
|
2026-04-15 19:15:47 -07:00
|
|
|
players: Vec::new(),
|
|
|
|
|
selected_player_id: None,
|
2026-04-15 20:20:25 -07:00
|
|
|
trains: Vec::new(),
|
2026-04-16 10:50:13 -07:00
|
|
|
locomotive_catalog: vec![
|
|
|
|
|
crate::RuntimeLocomotiveCatalogEntry {
|
|
|
|
|
locomotive_id: 10,
|
|
|
|
|
name: "Locomotive 10".to_string(),
|
|
|
|
|
},
|
|
|
|
|
crate::RuntimeLocomotiveCatalogEntry {
|
|
|
|
|
locomotive_id: 112,
|
|
|
|
|
name: "Locomotive 112".to_string(),
|
|
|
|
|
},
|
|
|
|
|
],
|
2026-04-15 18:27:04 -07:00
|
|
|
territories: Vec::new(),
|
|
|
|
|
company_territory_track_piece_counts: Vec::new(),
|
2026-04-15 20:53:35 -07:00
|
|
|
company_territory_access: Vec::new(),
|
2026-04-14 22:09:09 -07:00
|
|
|
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(),
|
2026-04-15 14:21:12 -07:00
|
|
|
live_id_bound: 11,
|
|
|
|
|
live_record_count: 5,
|
|
|
|
|
live_entry_ids: vec![3, 7, 9, 10, 11],
|
|
|
|
|
decoded_record_count: 5,
|
2026-04-14 22:09:09 -07:00
|
|
|
imported_runtime_record_count: 0,
|
|
|
|
|
records: vec![
|
|
|
|
|
RuntimePackedEventRecordSummary {
|
|
|
|
|
record_index: 0,
|
|
|
|
|
live_entry_id: 3,
|
|
|
|
|
payload_offset: Some(0x7202),
|
|
|
|
|
payload_len: Some(96),
|
|
|
|
|
decode_status: "parity_only".to_string(),
|
|
|
|
|
payload_family: "real_packed_v1".to_string(),
|
|
|
|
|
trigger_kind: None,
|
|
|
|
|
active: None,
|
|
|
|
|
marks_collection_dirty: None,
|
|
|
|
|
one_shot: None,
|
2026-04-14 23:01:18 -07:00
|
|
|
compact_control: None,
|
2026-04-14 22:09:09 -07:00
|
|
|
text_bands: Vec::new(),
|
|
|
|
|
standalone_condition_row_count: 0,
|
|
|
|
|
standalone_condition_rows: Vec::new(),
|
2026-04-15 14:21:12 -07:00
|
|
|
negative_sentinel_scope: None,
|
2026-04-14 22:09:09 -07:00
|
|
|
grouped_effect_row_counts: vec![0, 0, 0, 0],
|
|
|
|
|
grouped_effect_rows: Vec::new(),
|
2026-04-15 09:13:51 -07:00
|
|
|
grouped_company_targets: Vec::new(),
|
2026-04-15 18:27:04 -07:00
|
|
|
decoded_conditions: Vec::new(),
|
2026-04-14 22:09:09 -07:00
|
|
|
decoded_actions: Vec::new(),
|
|
|
|
|
executable_import_ready: false,
|
2026-04-14 23:01:18 -07:00
|
|
|
import_outcome: Some("blocked_missing_compact_control".to_string()),
|
2026-04-14 22:09:09 -07:00
|
|
|
notes: Vec::new(),
|
|
|
|
|
},
|
|
|
|
|
RuntimePackedEventRecordSummary {
|
|
|
|
|
record_index: 1,
|
|
|
|
|
live_entry_id: 7,
|
|
|
|
|
payload_offset: Some(0x7262),
|
|
|
|
|
payload_len: Some(48),
|
|
|
|
|
decode_status: "parity_only".to_string(),
|
|
|
|
|
payload_family: "synthetic_harness".to_string(),
|
|
|
|
|
trigger_kind: Some(7),
|
|
|
|
|
active: Some(true),
|
|
|
|
|
marks_collection_dirty: Some(false),
|
|
|
|
|
one_shot: Some(false),
|
2026-04-14 23:01:18 -07:00
|
|
|
compact_control: None,
|
2026-04-14 22:09:09 -07:00
|
|
|
text_bands: Vec::new(),
|
|
|
|
|
standalone_condition_row_count: 0,
|
|
|
|
|
standalone_condition_rows: Vec::new(),
|
2026-04-15 14:21:12 -07:00
|
|
|
negative_sentinel_scope: None,
|
2026-04-14 22:09:09 -07:00
|
|
|
grouped_effect_row_counts: vec![0, 0, 0, 0],
|
|
|
|
|
grouped_effect_rows: Vec::new(),
|
2026-04-15 09:13:51 -07:00
|
|
|
grouped_company_targets: Vec::new(),
|
2026-04-15 18:27:04 -07:00
|
|
|
decoded_conditions: Vec::new(),
|
2026-04-14 22:09:09 -07:00
|
|
|
decoded_actions: Vec::new(),
|
|
|
|
|
executable_import_ready: false,
|
|
|
|
|
import_outcome: Some("blocked_missing_company_context".to_string()),
|
|
|
|
|
notes: Vec::new(),
|
|
|
|
|
},
|
2026-04-15 14:21:12 -07:00
|
|
|
RuntimePackedEventRecordSummary {
|
|
|
|
|
record_index: 2,
|
|
|
|
|
live_entry_id: 9,
|
|
|
|
|
payload_offset: Some(0x7292),
|
|
|
|
|
payload_len: Some(48),
|
|
|
|
|
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![0, 0, 0, 0],
|
|
|
|
|
grouped_effect_rows: Vec::new(),
|
|
|
|
|
grouped_company_targets: Vec::new(),
|
2026-04-15 18:27:04 -07:00
|
|
|
decoded_conditions: Vec::new(),
|
2026-04-15 14:21:12 -07:00
|
|
|
decoded_actions: Vec::new(),
|
|
|
|
|
executable_import_ready: false,
|
|
|
|
|
import_outcome: Some(
|
|
|
|
|
"blocked_company_condition_scope_disabled".to_string(),
|
|
|
|
|
),
|
|
|
|
|
notes: Vec::new(),
|
|
|
|
|
},
|
|
|
|
|
RuntimePackedEventRecordSummary {
|
|
|
|
|
record_index: 3,
|
|
|
|
|
live_entry_id: 10,
|
|
|
|
|
payload_offset: Some(0x72c2),
|
|
|
|
|
payload_len: Some(48),
|
|
|
|
|
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![0, 0, 0, 0],
|
|
|
|
|
grouped_effect_rows: Vec::new(),
|
|
|
|
|
grouped_company_targets: Vec::new(),
|
2026-04-15 18:27:04 -07:00
|
|
|
decoded_conditions: Vec::new(),
|
2026-04-15 14:21:12 -07:00
|
|
|
decoded_actions: Vec::new(),
|
|
|
|
|
executable_import_ready: false,
|
|
|
|
|
import_outcome: Some("blocked_player_condition_scope".to_string()),
|
|
|
|
|
notes: Vec::new(),
|
|
|
|
|
},
|
|
|
|
|
RuntimePackedEventRecordSummary {
|
|
|
|
|
record_index: 4,
|
|
|
|
|
live_entry_id: 11,
|
|
|
|
|
payload_offset: Some(0x72f2),
|
|
|
|
|
payload_len: Some(48),
|
|
|
|
|
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![0, 0, 0, 0],
|
|
|
|
|
grouped_effect_rows: Vec::new(),
|
|
|
|
|
grouped_company_targets: Vec::new(),
|
2026-04-15 18:27:04 -07:00
|
|
|
decoded_conditions: Vec::new(),
|
2026-04-15 14:21:12 -07:00
|
|
|
decoded_actions: Vec::new(),
|
|
|
|
|
executable_import_ready: false,
|
|
|
|
|
import_outcome: Some("blocked_territory_condition_scope".to_string()),
|
|
|
|
|
notes: Vec::new(),
|
|
|
|
|
},
|
2026-04-14 22:09:09 -07:00
|
|
|
],
|
|
|
|
|
}),
|
|
|
|
|
event_runtime_records: Vec::new(),
|
|
|
|
|
candidate_availability: BTreeMap::new(),
|
2026-04-16 10:23:29 -07:00
|
|
|
named_locomotive_availability: BTreeMap::new(),
|
2026-04-16 11:19:53 -07:00
|
|
|
named_locomotive_cost: BTreeMap::new(),
|
2026-04-14 22:09:09 -07:00
|
|
|
special_conditions: BTreeMap::new(),
|
|
|
|
|
service_state: RuntimeServiceState::default(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let summary = RuntimeSummary::from_state(&state);
|
2026-04-15 14:21:12 -07:00
|
|
|
assert_eq!(
|
|
|
|
|
summary.packed_event_blocked_missing_compact_control_count,
|
|
|
|
|
1
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
summary.packed_event_blocked_unmapped_real_descriptor_count,
|
|
|
|
|
0
|
|
|
|
|
);
|
2026-04-14 23:01:18 -07:00
|
|
|
assert_eq!(summary.packed_event_blocked_structural_only_count, 0);
|
2026-04-15 14:21:12 -07:00
|
|
|
assert_eq!(
|
|
|
|
|
summary.packed_event_blocked_missing_company_context_count,
|
|
|
|
|
1
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
summary.packed_event_blocked_missing_selection_context_count,
|
|
|
|
|
0
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
summary.packed_event_blocked_missing_company_role_context_count,
|
|
|
|
|
0
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
summary.packed_event_blocked_missing_condition_context_count,
|
|
|
|
|
0
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
summary.packed_event_blocked_company_condition_scope_disabled_count,
|
|
|
|
|
1
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(summary.packed_event_blocked_player_condition_scope_count, 1);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
summary.packed_event_blocked_territory_condition_scope_count,
|
|
|
|
|
1
|
|
|
|
|
);
|
2026-04-14 22:09:09 -07:00
|
|
|
}
|
2026-04-15 12:11:29 -07:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn counts_active_companies_separately_from_total_companies() {
|
|
|
|
|
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![
|
|
|
|
|
RuntimeCompany {
|
|
|
|
|
company_id: 1,
|
|
|
|
|
current_cash: 10,
|
|
|
|
|
debt: 0,
|
2026-04-15 18:27:04 -07:00
|
|
|
credit_rating_score: None,
|
|
|
|
|
prime_rate: None,
|
|
|
|
|
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
2026-04-15 12:11:29 -07:00
|
|
|
active: true,
|
|
|
|
|
available_track_laying_capacity: None,
|
|
|
|
|
controller_kind: RuntimeCompanyControllerKind::Human,
|
|
|
|
|
},
|
|
|
|
|
RuntimeCompany {
|
|
|
|
|
company_id: 2,
|
|
|
|
|
current_cash: 20,
|
|
|
|
|
debt: 0,
|
2026-04-15 18:27:04 -07:00
|
|
|
credit_rating_score: None,
|
|
|
|
|
prime_rate: None,
|
|
|
|
|
track_piece_counts: RuntimeTrackPieceCounts::default(),
|
2026-04-15 12:11:29 -07:00
|
|
|
active: false,
|
|
|
|
|
available_track_laying_capacity: Some(7),
|
|
|
|
|
controller_kind: RuntimeCompanyControllerKind::Ai,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
selected_company_id: None,
|
2026-04-15 19:15:47 -07:00
|
|
|
players: Vec::new(),
|
|
|
|
|
selected_player_id: None,
|
2026-04-15 20:20:25 -07:00
|
|
|
trains: Vec::new(),
|
2026-04-16 10:50:13 -07:00
|
|
|
locomotive_catalog: vec![
|
|
|
|
|
crate::RuntimeLocomotiveCatalogEntry {
|
|
|
|
|
locomotive_id: 10,
|
|
|
|
|
name: "Locomotive 10".to_string(),
|
|
|
|
|
},
|
|
|
|
|
crate::RuntimeLocomotiveCatalogEntry {
|
|
|
|
|
locomotive_id: 112,
|
|
|
|
|
name: "Locomotive 112".to_string(),
|
|
|
|
|
},
|
|
|
|
|
],
|
2026-04-15 18:27:04 -07:00
|
|
|
territories: Vec::new(),
|
|
|
|
|
company_territory_track_piece_counts: Vec::new(),
|
2026-04-15 20:53:35 -07:00
|
|
|
company_territory_access: Vec::new(),
|
2026-04-15 12:11:29 -07:00
|
|
|
packed_event_collection: None,
|
|
|
|
|
event_runtime_records: Vec::new(),
|
|
|
|
|
candidate_availability: BTreeMap::new(),
|
2026-04-16 10:23:29 -07:00
|
|
|
named_locomotive_availability: BTreeMap::new(),
|
2026-04-16 11:19:53 -07:00
|
|
|
named_locomotive_cost: BTreeMap::new(),
|
2026-04-15 12:11:29 -07:00
|
|
|
special_conditions: BTreeMap::new(),
|
|
|
|
|
service_state: RuntimeServiceState::default(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let summary = RuntimeSummary::from_state(&state);
|
|
|
|
|
assert_eq!(summary.company_count, 2);
|
|
|
|
|
assert_eq!(summary.active_company_count, 1);
|
|
|
|
|
}
|
2026-04-15 21:41:40 -07:00
|
|
|
|
2026-04-16 10:23:29 -07:00
|
|
|
#[test]
|
|
|
|
|
fn counts_named_locomotive_availability_entries_and_zero_values() {
|
|
|
|
|
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(),
|
2026-04-16 10:50:13 -07:00
|
|
|
locomotive_catalog: vec![
|
|
|
|
|
crate::RuntimeLocomotiveCatalogEntry {
|
|
|
|
|
locomotive_id: 10,
|
|
|
|
|
name: "Locomotive 10".to_string(),
|
|
|
|
|
},
|
|
|
|
|
crate::RuntimeLocomotiveCatalogEntry {
|
|
|
|
|
locomotive_id: 112,
|
|
|
|
|
name: "Locomotive 112".to_string(),
|
|
|
|
|
},
|
|
|
|
|
],
|
2026-04-16 10:23:29 -07:00
|
|
|
territories: Vec::new(),
|
|
|
|
|
company_territory_track_piece_counts: Vec::new(),
|
|
|
|
|
company_territory_access: Vec::new(),
|
|
|
|
|
packed_event_collection: None,
|
|
|
|
|
event_runtime_records: Vec::new(),
|
|
|
|
|
candidate_availability: BTreeMap::new(),
|
|
|
|
|
named_locomotive_availability: BTreeMap::from([
|
|
|
|
|
("Big Boy".to_string(), 0),
|
|
|
|
|
("GP7".to_string(), 1),
|
|
|
|
|
("Mikado".to_string(), 0),
|
|
|
|
|
]),
|
2026-04-16 11:19:53 -07:00
|
|
|
named_locomotive_cost: BTreeMap::new(),
|
2026-04-16 10:23:29 -07:00
|
|
|
special_conditions: BTreeMap::new(),
|
|
|
|
|
service_state: RuntimeServiceState::default(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let summary = RuntimeSummary::from_state(&state);
|
2026-04-16 10:50:13 -07:00
|
|
|
assert_eq!(summary.locomotive_catalog_count, 2);
|
2026-04-16 10:23:29 -07:00
|
|
|
assert_eq!(summary.named_locomotive_availability_count, 3);
|
|
|
|
|
assert_eq!(summary.zero_named_locomotive_availability_count, 2);
|
2026-04-16 11:19:53 -07:00
|
|
|
assert_eq!(summary.named_locomotive_cost_count, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn counts_named_locomotive_cost_entries() {
|
|
|
|
|
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: None,
|
|
|
|
|
event_runtime_records: Vec::new(),
|
|
|
|
|
candidate_availability: BTreeMap::new(),
|
|
|
|
|
named_locomotive_availability: BTreeMap::new(),
|
|
|
|
|
named_locomotive_cost: BTreeMap::from([
|
|
|
|
|
("Big Boy".to_string(), 250000),
|
|
|
|
|
("GP7".to_string(), 175000),
|
|
|
|
|
]),
|
|
|
|
|
special_conditions: BTreeMap::new(),
|
|
|
|
|
service_state: RuntimeServiceState::default(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let summary = RuntimeSummary::from_state(&state);
|
|
|
|
|
|
|
|
|
|
assert_eq!(summary.named_locomotive_cost_count, 2);
|
2026-04-16 10:23:29 -07:00
|
|
|
}
|
|
|
|
|
|
2026-04-15 21:41:40 -07:00
|
|
|
#[test]
|
|
|
|
|
fn counts_world_frontier_buckets_separately() {
|
|
|
|
|
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(),
|
2026-04-16 10:50:13 -07:00
|
|
|
locomotive_catalog: Vec::new(),
|
2026-04-15 21:41:40 -07:00
|
|
|
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: 2,
|
|
|
|
|
live_record_count: 2,
|
|
|
|
|
live_entry_ids: vec![21, 22],
|
|
|
|
|
decoded_record_count: 2,
|
|
|
|
|
imported_runtime_record_count: 0,
|
|
|
|
|
records: vec![
|
|
|
|
|
RuntimePackedEventRecordSummary {
|
|
|
|
|
record_index: 0,
|
|
|
|
|
live_entry_id: 21,
|
|
|
|
|
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_unmapped_world_descriptor".to_string()),
|
|
|
|
|
notes: Vec::new(),
|
|
|
|
|
},
|
|
|
|
|
RuntimePackedEventRecordSummary {
|
|
|
|
|
record_index: 1,
|
|
|
|
|
live_entry_id: 22,
|
|
|
|
|
payload_offset: Some(0x7242),
|
|
|
|
|
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: 1,
|
|
|
|
|
standalone_condition_rows: Vec::new(),
|
|
|
|
|
negative_sentinel_scope: None,
|
|
|
|
|
grouped_effect_row_counts: vec![0, 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_unmapped_world_condition".to_string()),
|
|
|
|
|
notes: Vec::new(),
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}),
|
|
|
|
|
event_runtime_records: Vec::new(),
|
|
|
|
|
candidate_availability: BTreeMap::new(),
|
2026-04-16 10:23:29 -07:00
|
|
|
named_locomotive_availability: BTreeMap::new(),
|
2026-04-16 11:19:53 -07:00
|
|
|
named_locomotive_cost: BTreeMap::new(),
|
2026-04-15 21:41:40 -07:00
|
|
|
special_conditions: BTreeMap::new(),
|
|
|
|
|
service_state: RuntimeServiceState::default(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let summary = RuntimeSummary::from_state(&state);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
summary.packed_event_blocked_unmapped_world_descriptor_count,
|
|
|
|
|
1
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
summary.packed_event_blocked_unmapped_world_condition_count,
|
|
|
|
|
1
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-04-16 10:50:13 -07:00
|
|
|
|
|
|
|
|
#[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(),
|
2026-04-16 11:19:53 -07:00
|
|
|
named_locomotive_cost: BTreeMap::new(),
|
2026-04-16 10:50:13 -07:00
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-04-14 22:09:09 -07:00
|
|
|
}
|