Recover scalar-band packed event metadata
This commit is contained in:
parent
87108f357b
commit
e2174713a9
8 changed files with 685 additions and 39 deletions
|
|
@ -54,7 +54,9 @@ through checked-in metadata into keyed `world_flags`, while the wider locomotive
|
|||
scalar bands are now split more cleanly: the boolean `0/1` availability subset can import through
|
||||
an overlay-backed `RuntimeState.locomotive_catalog` into
|
||||
`RuntimeState.named_locomotive_availability`, while non-boolean availability payloads plus the
|
||||
locomotive-cost/cargo-production/territory-access-cost families remain recovered-but-parity-only.
|
||||
locomotive-cost/cargo-production/territory-access-cost families now surface as recovered,
|
||||
metadata-rich parity rows with checked-in slot labels and locomotive ids where grounded, but they
|
||||
still remain non-executable.
|
||||
The runtime still carries the save-owned named locomotive availability table directly too:
|
||||
checked-in save-slice documents can populate `RuntimeState.named_locomotive_availability`, and
|
||||
imported runtime effects can mutate that map through the ordinary event-service path without
|
||||
|
|
|
|||
|
|
@ -4463,6 +4463,9 @@ mod tests {
|
|||
let overlay_locomotive_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
|
||||
"../../fixtures/runtime/packed-event-locomotive-availability-overlay-fixture.json",
|
||||
);
|
||||
let scalar_band_parity_fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(
|
||||
"../../fixtures/runtime/packed-event-world-scalar-band-parity-save-slice-fixture.json",
|
||||
);
|
||||
|
||||
run_runtime_summarize_fixture(&parity_fixture)
|
||||
.expect("save-slice-backed parity fixture should summarize");
|
||||
|
|
@ -4487,6 +4490,8 @@ mod tests {
|
|||
);
|
||||
run_runtime_summarize_fixture(&overlay_locomotive_fixture)
|
||||
.expect("overlay-backed locomotive availability fixture should summarize");
|
||||
run_runtime_summarize_fixture(&scalar_band_parity_fixture)
|
||||
.expect("save-slice-backed recovered scalar-band parity fixture should summarize");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -3070,11 +3070,19 @@ mod tests {
|
|||
descriptor_id: u32,
|
||||
value: i32,
|
||||
) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
let recovered_locomotive_id = match descriptor_id {
|
||||
352..=451 => Some(descriptor_id - 351),
|
||||
475..=500 => Some(descriptor_id - 374),
|
||||
_ => None,
|
||||
};
|
||||
let descriptor_label = recovered_locomotive_id
|
||||
.map(|loco_id| format!("Locomotive {loco_id} Cost"))
|
||||
.unwrap_or_else(|| "Locomotive Cost".to_string());
|
||||
crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
group_index: 0,
|
||||
row_index: 0,
|
||||
descriptor_id,
|
||||
descriptor_label: Some("Unknown Loco Cost".to_string()),
|
||||
descriptor_label: Some(descriptor_label.clone()),
|
||||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("locomotive_cost_scalar".to_string()),
|
||||
opcode: 3,
|
||||
|
|
@ -3087,12 +3095,65 @@ mod tests {
|
|||
value_word_0x16: 0,
|
||||
row_shape: "scalar_assignment".to_string(),
|
||||
semantic_family: Some("scalar_assignment".to_string()),
|
||||
semantic_preview: Some(format!("Set Unknown Loco Cost to {value}")),
|
||||
recovered_locomotive_id: match descriptor_id {
|
||||
352..=451 => Some(descriptor_id - 351),
|
||||
475..=500 => Some(descriptor_id - 374),
|
||||
_ => None,
|
||||
},
|
||||
semantic_preview: Some(format!("Set {descriptor_label} to {value}")),
|
||||
recovered_locomotive_id,
|
||||
locomotive_name: None,
|
||||
notes: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn real_cargo_production_row(
|
||||
descriptor_id: u32,
|
||||
value: i32,
|
||||
) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
let slot = descriptor_id.saturating_sub(229);
|
||||
let descriptor_label = format!("Cargo Production Slot {slot}");
|
||||
crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
group_index: 0,
|
||||
row_index: 0,
|
||||
descriptor_id,
|
||||
descriptor_label: Some(descriptor_label.clone()),
|
||||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("cargo_production_scalar".to_string()),
|
||||
opcode: 3,
|
||||
raw_scalar_value: value,
|
||||
value_byte_0x09: 0,
|
||||
value_dword_0x0d: 0,
|
||||
value_byte_0x11: 0,
|
||||
value_byte_0x12: 0,
|
||||
value_word_0x14: 0,
|
||||
value_word_0x16: 0,
|
||||
row_shape: "scalar_assignment".to_string(),
|
||||
semantic_family: Some("scalar_assignment".to_string()),
|
||||
semantic_preview: Some(format!("Set {descriptor_label} to {value}")),
|
||||
recovered_locomotive_id: None,
|
||||
locomotive_name: None,
|
||||
notes: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn real_territory_access_cost_row(
|
||||
value: i32,
|
||||
) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
crate::SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
group_index: 0,
|
||||
row_index: 0,
|
||||
descriptor_id: 453,
|
||||
descriptor_label: Some("Territory Access Cost".to_string()),
|
||||
target_mask_bits: Some(0x08),
|
||||
parameter_family: Some("territory_access_cost_scalar".to_string()),
|
||||
opcode: 3,
|
||||
raw_scalar_value: value,
|
||||
value_byte_0x09: 0,
|
||||
value_dword_0x0d: 0,
|
||||
value_byte_0x11: 0,
|
||||
value_byte_0x12: 0,
|
||||
value_word_0x14: 0,
|
||||
value_word_0x16: 0,
|
||||
row_shape: "scalar_assignment".to_string(),
|
||||
semantic_family: Some("scalar_assignment".to_string()),
|
||||
semantic_preview: Some(format!("Set Territory Access Cost to {value}")),
|
||||
recovered_locomotive_id: None,
|
||||
locomotive_name: None,
|
||||
notes: vec![],
|
||||
}
|
||||
|
|
@ -5155,6 +5216,152 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_recovered_cargo_production_rows_parity_only() {
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
file_extension_hint: Some("gms".to_string()),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
trailer_family: None,
|
||||
bridge_family: None,
|
||||
profile: None,
|
||||
candidate_availability_table: None,
|
||||
named_locomotive_availability_table: None,
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
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()),
|
||||
metadata_tag_offset: 0x7100,
|
||||
records_tag_offset: 0x7200,
|
||||
close_tag_offset: 0x7600,
|
||||
packed_state_version: 0x3e9,
|
||||
packed_state_version_hex: "0x000003e9".to_string(),
|
||||
live_id_bound: 35,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![35],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 0,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 35,
|
||||
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: Some(false),
|
||||
compact_control: Some(real_compact_control()),
|
||||
text_bands: vec![],
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
negative_sentinel_scope: None,
|
||||
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||
grouped_effect_rows: vec![real_cargo_production_row(230, 125)],
|
||||
decoded_conditions: Vec::new(),
|
||||
decoded_actions: vec![],
|
||||
executable_import_ready: false,
|
||||
notes: vec!["cargo production rows remain metadata-only".to_string()],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
let import = project_save_slice_to_runtime_state_import(
|
||||
&save_slice,
|
||||
"packed-events-cargo-production-frontier",
|
||||
None,
|
||||
)
|
||||
.expect("save slice should project");
|
||||
|
||||
assert!(import.state.event_runtime_records.is_empty());
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.packed_event_collection
|
||||
.as_ref()
|
||||
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
|
||||
Some("blocked_unmapped_world_descriptor")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keeps_recovered_territory_access_cost_rows_parity_only() {
|
||||
let save_slice = SmpLoadedSaveSlice {
|
||||
file_extension_hint: Some("gms".to_string()),
|
||||
container_profile_family: Some("rt3-classic-save-container-v1".to_string()),
|
||||
mechanism_family: "classic-save-rehydrate-v1".to_string(),
|
||||
mechanism_confidence: "grounded".to_string(),
|
||||
trailer_family: None,
|
||||
bridge_family: None,
|
||||
profile: None,
|
||||
candidate_availability_table: None,
|
||||
named_locomotive_availability_table: None,
|
||||
special_conditions_table: None,
|
||||
event_runtime_collection: Some(crate::SmpLoadedEventRuntimeCollectionSummary {
|
||||
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()),
|
||||
metadata_tag_offset: 0x7100,
|
||||
records_tag_offset: 0x7200,
|
||||
close_tag_offset: 0x7600,
|
||||
packed_state_version: 0x3e9,
|
||||
packed_state_version_hex: "0x000003e9".to_string(),
|
||||
live_id_bound: 36,
|
||||
live_record_count: 1,
|
||||
live_entry_ids: vec![36],
|
||||
decoded_record_count: 1,
|
||||
imported_runtime_record_count: 0,
|
||||
records: vec![crate::SmpLoadedPackedEventRecordSummary {
|
||||
record_index: 0,
|
||||
live_entry_id: 36,
|
||||
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: Some(false),
|
||||
compact_control: Some(real_compact_control()),
|
||||
text_bands: vec![],
|
||||
standalone_condition_row_count: 0,
|
||||
standalone_condition_rows: vec![],
|
||||
negative_sentinel_scope: None,
|
||||
grouped_effect_row_counts: vec![1, 0, 0, 0],
|
||||
grouped_effect_rows: vec![real_territory_access_cost_row(750000)],
|
||||
decoded_conditions: Vec::new(),
|
||||
decoded_actions: vec![],
|
||||
executable_import_ready: false,
|
||||
notes: vec!["territory access cost rows remain metadata-only".to_string()],
|
||||
}],
|
||||
}),
|
||||
notes: vec![],
|
||||
};
|
||||
|
||||
let import = project_save_slice_to_runtime_state_import(
|
||||
&save_slice,
|
||||
"packed-events-territory-access-cost-frontier",
|
||||
None,
|
||||
)
|
||||
.expect("save slice should project");
|
||||
|
||||
assert!(import.state.event_runtime_records.is_empty());
|
||||
assert_eq!(
|
||||
import
|
||||
.state
|
||||
.packed_event_collection
|
||||
.as_ref()
|
||||
.and_then(|summary| summary.records[0].import_outcome.as_deref()),
|
||||
Some("blocked_unmapped_world_descriptor")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlays_real_company_cash_descriptor_into_executable_runtime_record() {
|
||||
let base_state = RuntimeState {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
|
@ -2684,6 +2686,11 @@ fn parse_real_grouped_effect_row_summary(
|
|||
"locomotive availability descriptor maps to live locomotive id {loco_id}"
|
||||
));
|
||||
}
|
||||
if let Some(loco_id) = recovered_locomotive_cost_loco_id(descriptor_id) {
|
||||
notes.push(format!(
|
||||
"locomotive cost descriptor maps to live locomotive id {loco_id}"
|
||||
));
|
||||
}
|
||||
|
||||
Some(SmpLoadedPackedEventGroupedEffectRowSummary {
|
||||
group_index,
|
||||
|
|
@ -2711,7 +2718,8 @@ fn parse_real_grouped_effect_row_summary(
|
|||
value_word_0x14,
|
||||
value_word_0x16,
|
||||
)),
|
||||
recovered_locomotive_id: recovered_locomotive_availability_loco_id(descriptor_id),
|
||||
recovered_locomotive_id: recovered_locomotive_availability_loco_id(descriptor_id)
|
||||
.or_else(|| recovered_locomotive_cost_loco_id(descriptor_id)),
|
||||
locomotive_name,
|
||||
notes,
|
||||
})
|
||||
|
|
@ -2823,16 +2831,16 @@ fn real_grouped_effect_descriptor_metadata(
|
|||
fn recovered_cargo_production_descriptor_metadata(
|
||||
descriptor_id: u32,
|
||||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
(230..=240)
|
||||
.contains(&descriptor_id)
|
||||
.then_some(RealGroupedEffectDescriptorMetadata {
|
||||
recovered_cargo_production_label(descriptor_id).map(|label| {
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "Unknown Cargo Production",
|
||||
label,
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "unknown_cargo_production_scalar",
|
||||
parameter_family: "cargo_production_scalar",
|
||||
runtime_key: None,
|
||||
executable_in_runtime: false,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn recovered_locomotive_availability_descriptor_metadata(
|
||||
|
|
@ -2872,31 +2880,66 @@ fn recovered_locomotive_availability_loco_id(descriptor_id: u32) -> Option<u32>
|
|||
None
|
||||
}
|
||||
|
||||
fn recovered_cargo_production_label(descriptor_id: u32) -> Option<&'static str> {
|
||||
static LABELS: OnceLock<BTreeMap<u32, &'static str>> = OnceLock::new();
|
||||
LABELS
|
||||
.get_or_init(|| {
|
||||
(230..=240)
|
||||
.enumerate()
|
||||
.map(|(slot_index, descriptor_id)| {
|
||||
let label = Box::leak(
|
||||
format!("Cargo Production Slot {}", slot_index + 1).into_boxed_str(),
|
||||
) as &'static str;
|
||||
(descriptor_id, label)
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.get(&descriptor_id)
|
||||
.copied()
|
||||
}
|
||||
|
||||
fn recovered_locomotive_cost_loco_id(descriptor_id: u32) -> Option<u32> {
|
||||
if (352..=451).contains(&descriptor_id) {
|
||||
return Some(descriptor_id - 351);
|
||||
}
|
||||
if (475..=500).contains(&descriptor_id) {
|
||||
return Some(descriptor_id - 374);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn recovered_locomotive_cost_label(descriptor_id: u32) -> Option<&'static str> {
|
||||
static LABELS: OnceLock<BTreeMap<u32, &'static str>> = OnceLock::new();
|
||||
LABELS
|
||||
.get_or_init(|| {
|
||||
(352..=451)
|
||||
.chain(475..=500)
|
||||
.filter_map(|descriptor_id| {
|
||||
recovered_locomotive_cost_loco_id(descriptor_id).map(|loco_id| {
|
||||
let label = Box::leak(format!("Locomotive {loco_id} Cost").into_boxed_str())
|
||||
as &'static str;
|
||||
(descriptor_id, label)
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.get(&descriptor_id)
|
||||
.copied()
|
||||
}
|
||||
|
||||
fn recovered_locomotive_cost_descriptor_metadata(
|
||||
descriptor_id: u32,
|
||||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
(352..=451)
|
||||
.contains(&descriptor_id)
|
||||
.then_some(RealGroupedEffectDescriptorMetadata {
|
||||
recovered_locomotive_cost_label(descriptor_id).map(|label| {
|
||||
RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "Unknown Loco Cost",
|
||||
label,
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "locomotive_cost_scalar",
|
||||
runtime_key: None,
|
||||
executable_in_runtime: false,
|
||||
})
|
||||
.or_else(|| {
|
||||
(475..=500)
|
||||
.contains(&descriptor_id)
|
||||
.then_some(RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "Unknown Loco Cost",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "locomotive_cost_scalar",
|
||||
runtime_key: None,
|
||||
executable_in_runtime: false,
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn recovered_territory_access_cost_descriptor_metadata(
|
||||
|
|
@ -2904,7 +2947,7 @@ fn recovered_territory_access_cost_descriptor_metadata(
|
|||
) -> Option<RealGroupedEffectDescriptorMetadata> {
|
||||
(descriptor_id == 453).then_some(RealGroupedEffectDescriptorMetadata {
|
||||
descriptor_id,
|
||||
label: "Territory access cost",
|
||||
label: "Territory Access Cost",
|
||||
target_mask_bits: 0x08,
|
||||
parameter_family: "territory_access_cost_scalar",
|
||||
runtime_key: None,
|
||||
|
|
@ -9292,6 +9335,81 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looks_up_recovered_cargo_production_descriptor_metadata() {
|
||||
let metadata =
|
||||
real_grouped_effect_descriptor_metadata(230).expect("descriptor metadata should exist");
|
||||
|
||||
assert_eq!(metadata.label, "Cargo Production Slot 1");
|
||||
assert_eq!(metadata.target_mask_bits, 0x08);
|
||||
assert_eq!(metadata.parameter_family, "cargo_production_scalar");
|
||||
assert_eq!(metadata.runtime_key, None);
|
||||
assert!(!metadata.executable_in_runtime);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looks_up_recovered_lower_band_locomotive_cost_descriptor_metadata() {
|
||||
let metadata =
|
||||
real_grouped_effect_descriptor_metadata(352).expect("descriptor metadata should exist");
|
||||
|
||||
assert_eq!(metadata.label, "Locomotive 1 Cost");
|
||||
assert_eq!(metadata.target_mask_bits, 0x08);
|
||||
assert_eq!(metadata.parameter_family, "locomotive_cost_scalar");
|
||||
assert_eq!(metadata.runtime_key, None);
|
||||
assert_eq!(recovered_locomotive_cost_loco_id(352), Some(1));
|
||||
assert!(!metadata.executable_in_runtime);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looks_up_recovered_upper_band_locomotive_cost_descriptor_metadata() {
|
||||
let metadata =
|
||||
real_grouped_effect_descriptor_metadata(475).expect("descriptor metadata should exist");
|
||||
|
||||
assert_eq!(metadata.label, "Locomotive 101 Cost");
|
||||
assert_eq!(metadata.parameter_family, "locomotive_cost_scalar");
|
||||
assert_eq!(recovered_locomotive_cost_loco_id(475), Some(101));
|
||||
assert!(!metadata.executable_in_runtime);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looks_up_recovered_territory_access_cost_descriptor_metadata() {
|
||||
let metadata =
|
||||
real_grouped_effect_descriptor_metadata(453).expect("descriptor metadata should exist");
|
||||
|
||||
assert_eq!(metadata.label, "Territory Access Cost");
|
||||
assert_eq!(metadata.target_mask_bits, 0x08);
|
||||
assert_eq!(metadata.parameter_family, "territory_access_cost_scalar");
|
||||
assert_eq!(metadata.runtime_key, None);
|
||||
assert!(!metadata.executable_in_runtime);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_recovered_locomotive_cost_row_with_structured_locomotive_id() {
|
||||
let row_bytes = build_real_grouped_effect_row(RealGroupedEffectRowSpec {
|
||||
descriptor_id: 352,
|
||||
raw_scalar_value: 250000,
|
||||
opcode: 3,
|
||||
value_byte_0x09: 0,
|
||||
value_dword_0x0d: 0,
|
||||
value_byte_0x11: 0,
|
||||
value_byte_0x12: 0,
|
||||
value_word_0x14: 0,
|
||||
value_word_0x16: 0,
|
||||
locomotive_name: None,
|
||||
});
|
||||
|
||||
let row = parse_real_grouped_effect_row_summary(&row_bytes, 0, 0, None)
|
||||
.expect("row should parse");
|
||||
|
||||
assert_eq!(row.descriptor_id, 352);
|
||||
assert_eq!(row.descriptor_label.as_deref(), Some("Locomotive 1 Cost"));
|
||||
assert_eq!(row.recovered_locomotive_id, Some(1));
|
||||
assert_eq!(
|
||||
row.parameter_family.as_deref(),
|
||||
Some("locomotive_cost_scalar")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn looks_up_recovered_locomotive_policy_descriptor_metadata() {
|
||||
let metadata =
|
||||
|
|
|
|||
|
|
@ -125,8 +125,9 @@ The highest-value next passes are now:
|
|||
`blocked_unmapped_world_descriptor` frontier
|
||||
- the next recovered locomotives-page descriptor batch is partially executable too:
|
||||
descriptors `454..456` (`All Steam/Diesel/Electric Locos Avail.`) now lower through checked-in
|
||||
metadata into keyed `world_flags`, while the wider locomotive availability/cost scalar bands
|
||||
remain recovered-but-parity-only until per-locomotive identity is grounded
|
||||
metadata into keyed `world_flags`, while the wider locomotive availability/cost scalar bands now
|
||||
split cleanly between executable boolean availability rows and recovered metadata-rich parity
|
||||
rows for the remaining cargo-production, locomotive-cost, and territory-access-cost families
|
||||
- the runtime now also carries both the save-owned named locomotive availability table and an
|
||||
overlay-backed locomotive catalog context: checked-in save-slice documents can populate
|
||||
`RuntimeState.named_locomotive_availability`, and boolean `0/1` availability descriptors can
|
||||
|
|
|
|||
|
|
@ -88,12 +88,15 @@ Implemented today:
|
|||
through the ordinary event-service path without requiring Trainbuy or live locomotive-pool parity
|
||||
- the boolean `0/1` subset of the recovered locomotives-page availability bands can now import
|
||||
through an overlay-backed `RuntimeState.locomotive_catalog`; non-boolean availability payloads
|
||||
and the adjacent locomotive-cost/cargo-production/access-cost families remain parity-only
|
||||
and the adjacent locomotive-cost/cargo-production/access-cost families now surface as recovered
|
||||
metadata-rich parity rows with checked-in slot labels and locomotive ids where grounded, but
|
||||
execution for those scalar families remains deferred
|
||||
|
||||
That means the next implementation work is breadth, not bootstrap. The recommended next slice is
|
||||
broader real grouped-descriptor and ordinary condition-id coverage beyond the current access,
|
||||
whole-game toggle, train, player, numeric-threshold, named locomotive availability, and
|
||||
overlay-resolved locomotive availability batches.
|
||||
honest landing surfaces for one or more of those recovered scalar families, plus broader real
|
||||
grouped-descriptor and ordinary condition-id coverage beyond the current access, whole-game
|
||||
toggle, train, player, numeric-threshold, named locomotive availability, and overlay-resolved
|
||||
locomotive availability batches.
|
||||
Richer runtime ownership should still be added only where a later descriptor or condition family
|
||||
needs more than the current event-owned roster.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"fixture_id": "packed-event-world-scalar-band-parity-save-slice-fixture",
|
||||
"source": {
|
||||
"kind": "captured-runtime",
|
||||
"description": "Fixture backed by a tracked save-slice document that pins recovered scalar-band world descriptors as parity-only."
|
||||
},
|
||||
"state_save_slice_path": "packed-event-world-scalar-band-parity-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,
|
||||
"packed_event_collection_present": true,
|
||||
"packed_event_record_count": 3,
|
||||
"packed_event_decoded_record_count": 3,
|
||||
"packed_event_imported_runtime_record_count": 0,
|
||||
"packed_event_parity_only_record_count": 3,
|
||||
"packed_event_unsupported_record_count": 0,
|
||||
"packed_event_blocked_missing_condition_context_count": 0,
|
||||
"packed_event_blocked_territory_condition_scope_count": 0,
|
||||
"packed_event_blocked_missing_compact_control_count": 0,
|
||||
"packed_event_blocked_unmapped_real_descriptor_count": 0,
|
||||
"packed_event_blocked_unmapped_world_descriptor_count": 3,
|
||||
"packed_event_blocked_structural_only_count": 0,
|
||||
"event_runtime_record_count": 0,
|
||||
"total_company_cash": 0
|
||||
},
|
||||
"expected_state_fragment": {
|
||||
"metadata": {
|
||||
"save_slice.import_projection": "partial-runtime-restore-v1"
|
||||
},
|
||||
"packed_event_collection": {
|
||||
"live_entry_ids": [11, 12, 13],
|
||||
"records": [
|
||||
{
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"import_outcome": "blocked_unmapped_world_descriptor",
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"descriptor_id": 230,
|
||||
"descriptor_label": "Cargo Production Slot 1",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "cargo_production_scalar",
|
||||
"semantic_family": "scalar_assignment",
|
||||
"semantic_preview": "Set Cargo Production Slot 1 to 125",
|
||||
"row_shape": "scalar_assignment"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"import_outcome": "blocked_unmapped_world_descriptor",
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"descriptor_id": 352,
|
||||
"descriptor_label": "Locomotive 1 Cost",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "locomotive_cost_scalar",
|
||||
"semantic_family": "scalar_assignment",
|
||||
"semantic_preview": "Set Locomotive 1 Cost to 250000",
|
||||
"recovered_locomotive_id": 1,
|
||||
"row_shape": "scalar_assignment"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"import_outcome": "blocked_unmapped_world_descriptor",
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"descriptor_id": 453,
|
||||
"descriptor_label": "Territory Access Cost",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "territory_access_cost_scalar",
|
||||
"semantic_family": "scalar_assignment",
|
||||
"semantic_preview": "Set Territory Access Cost to 750000",
|
||||
"row_shape": "scalar_assignment"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
{
|
||||
"format_version": 1,
|
||||
"save_slice_id": "packed-event-world-scalar-band-parity-save-slice",
|
||||
"source": {
|
||||
"description": "Tracked save-slice document pinning recovered locomotives-page scalar families that are still parity-only.",
|
||||
"original_save_filename": "captured-world-scalar-band-parity.gms",
|
||||
"original_save_sha256": "world-scalar-band-parity-sample-sha256",
|
||||
"notes": [
|
||||
"tracked as JSON save-slice document rather than raw .smp",
|
||||
"covers recovered cargo production, locomotive cost, and territory access cost families without claiming executable runtime semantics"
|
||||
]
|
||||
},
|
||||
"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,
|
||||
"special_conditions_table": null,
|
||||
"event_runtime_collection": {
|
||||
"source_kind": "packed-event-runtime-collection",
|
||||
"mechanism_family": "classic-save-rehydrate-v1",
|
||||
"mechanism_confidence": "grounded",
|
||||
"container_profile_family": "rt3-classic-save-container-v1",
|
||||
"metadata_tag_offset": 29696,
|
||||
"records_tag_offset": 29952,
|
||||
"close_tag_offset": 30720,
|
||||
"packed_state_version": 1001,
|
||||
"packed_state_version_hex": "0x000003e9",
|
||||
"live_id_bound": 13,
|
||||
"live_record_count": 3,
|
||||
"live_entry_ids": [11, 12, 13],
|
||||
"decoded_record_count": 3,
|
||||
"imported_runtime_record_count": 0,
|
||||
"records": [
|
||||
{
|
||||
"record_index": 0,
|
||||
"live_entry_id": 11,
|
||||
"payload_offset": 29984,
|
||||
"payload_len": 96,
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 6,
|
||||
"one_shot": false,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 6,
|
||||
"primary_selector_0x7f0": 0,
|
||||
"grouped_mode_0x7f4": 2,
|
||||
"one_shot_header_0x7f5": 0,
|
||||
"modifier_flag_0x7f9": 0,
|
||||
"modifier_flag_0x7fa": 0,
|
||||
"grouped_target_scope_ordinals_0x7fb": [0, 0, 0, 0],
|
||||
"grouped_scope_checkboxes_0x7ff": [1, 0, 0, 0],
|
||||
"summary_toggle_0x800": 1,
|
||||
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
|
||||
},
|
||||
"text_bands": [],
|
||||
"standalone_condition_row_count": 0,
|
||||
"standalone_condition_rows": [],
|
||||
"grouped_effect_row_counts": [1, 0, 0, 0],
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 0,
|
||||
"descriptor_id": 230,
|
||||
"descriptor_label": "Cargo Production Slot 1",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "cargo_production_scalar",
|
||||
"opcode": 3,
|
||||
"raw_scalar_value": 125,
|
||||
"value_byte_0x09": 0,
|
||||
"value_dword_0x0d": 0,
|
||||
"value_byte_0x11": 0,
|
||||
"value_byte_0x12": 0,
|
||||
"value_word_0x14": 0,
|
||||
"value_word_0x16": 0,
|
||||
"row_shape": "scalar_assignment",
|
||||
"semantic_family": "scalar_assignment",
|
||||
"semantic_preview": "Set Cargo Production Slot 1 to 125",
|
||||
"recovered_locomotive_id": null,
|
||||
"locomotive_name": null,
|
||||
"notes": []
|
||||
}
|
||||
],
|
||||
"decoded_actions": [],
|
||||
"executable_import_ready": false,
|
||||
"notes": [
|
||||
"recovered cargo production metadata is now checked in, but no runtime landing surface exists yet"
|
||||
]
|
||||
},
|
||||
{
|
||||
"record_index": 1,
|
||||
"live_entry_id": 12,
|
||||
"payload_offset": 30096,
|
||||
"payload_len": 96,
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 6,
|
||||
"one_shot": false,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 6,
|
||||
"primary_selector_0x7f0": 0,
|
||||
"grouped_mode_0x7f4": 2,
|
||||
"one_shot_header_0x7f5": 0,
|
||||
"modifier_flag_0x7f9": 0,
|
||||
"modifier_flag_0x7fa": 0,
|
||||
"grouped_target_scope_ordinals_0x7fb": [0, 0, 0, 0],
|
||||
"grouped_scope_checkboxes_0x7ff": [1, 0, 0, 0],
|
||||
"summary_toggle_0x800": 1,
|
||||
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
|
||||
},
|
||||
"text_bands": [],
|
||||
"standalone_condition_row_count": 0,
|
||||
"standalone_condition_rows": [],
|
||||
"grouped_effect_row_counts": [1, 0, 0, 0],
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 0,
|
||||
"descriptor_id": 352,
|
||||
"descriptor_label": "Locomotive 1 Cost",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "locomotive_cost_scalar",
|
||||
"opcode": 3,
|
||||
"raw_scalar_value": 250000,
|
||||
"value_byte_0x09": 0,
|
||||
"value_dword_0x0d": 0,
|
||||
"value_byte_0x11": 0,
|
||||
"value_byte_0x12": 0,
|
||||
"value_word_0x14": 0,
|
||||
"value_word_0x16": 0,
|
||||
"row_shape": "scalar_assignment",
|
||||
"semantic_family": "scalar_assignment",
|
||||
"semantic_preview": "Set Locomotive 1 Cost to 250000",
|
||||
"recovered_locomotive_id": 1,
|
||||
"locomotive_name": null,
|
||||
"notes": [
|
||||
"locomotive cost descriptor maps to live locomotive id 1"
|
||||
]
|
||||
}
|
||||
],
|
||||
"decoded_actions": [],
|
||||
"executable_import_ready": false,
|
||||
"notes": [
|
||||
"recovered locomotive cost metadata is now checked in, but execution is still deferred"
|
||||
]
|
||||
},
|
||||
{
|
||||
"record_index": 2,
|
||||
"live_entry_id": 13,
|
||||
"payload_offset": 30208,
|
||||
"payload_len": 96,
|
||||
"decode_status": "parity_only",
|
||||
"payload_family": "real_packed_v1",
|
||||
"trigger_kind": 6,
|
||||
"one_shot": false,
|
||||
"compact_control": {
|
||||
"mode_byte_0x7ef": 6,
|
||||
"primary_selector_0x7f0": 0,
|
||||
"grouped_mode_0x7f4": 2,
|
||||
"one_shot_header_0x7f5": 0,
|
||||
"modifier_flag_0x7f9": 0,
|
||||
"modifier_flag_0x7fa": 0,
|
||||
"grouped_target_scope_ordinals_0x7fb": [0, 0, 0, 0],
|
||||
"grouped_scope_checkboxes_0x7ff": [1, 0, 0, 0],
|
||||
"summary_toggle_0x800": 1,
|
||||
"grouped_territory_selectors_0x80f": [-1, -1, -1, -1]
|
||||
},
|
||||
"text_bands": [],
|
||||
"standalone_condition_row_count": 0,
|
||||
"standalone_condition_rows": [],
|
||||
"grouped_effect_row_counts": [1, 0, 0, 0],
|
||||
"grouped_effect_rows": [
|
||||
{
|
||||
"group_index": 0,
|
||||
"row_index": 0,
|
||||
"descriptor_id": 453,
|
||||
"descriptor_label": "Territory Access Cost",
|
||||
"target_mask_bits": 8,
|
||||
"parameter_family": "territory_access_cost_scalar",
|
||||
"opcode": 3,
|
||||
"raw_scalar_value": 750000,
|
||||
"value_byte_0x09": 0,
|
||||
"value_dword_0x0d": 0,
|
||||
"value_byte_0x11": 0,
|
||||
"value_byte_0x12": 0,
|
||||
"value_word_0x14": 0,
|
||||
"value_word_0x16": 0,
|
||||
"row_shape": "scalar_assignment",
|
||||
"semantic_family": "scalar_assignment",
|
||||
"semantic_preview": "Set Territory Access Cost to 750000",
|
||||
"recovered_locomotive_id": null,
|
||||
"locomotive_name": null,
|
||||
"notes": []
|
||||
}
|
||||
],
|
||||
"decoded_actions": [],
|
||||
"executable_import_ready": false,
|
||||
"notes": [
|
||||
"recovered territory access cost metadata is now checked in, but execution is still deferred"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"notes": [
|
||||
"world scalar parity sample"
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue