From e44e0d5ac5a33f5bd6ef1ffa7334f39cad89393c Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Wed, 15 Apr 2026 23:03:18 -0700 Subject: [PATCH] Execute world flag packed event descriptor --- README.md | 13 +- crates/rrt-runtime/src/import.rs | 122 ++++++++++++++++-- crates/rrt-runtime/src/smp.rs | 54 +++++++- docs/README.md | 6 +- docs/runtime-rehost-plan.md | 14 +- ...d-event-world-flag-save-slice-fixture.json | 44 +++++++ ...> packed-event-world-flag-save-slice.json} | 32 +++-- ...event-world-parity-save-slice-fixture.json | 33 ----- 8 files changed, 242 insertions(+), 76 deletions(-) create mode 100644 fixtures/runtime/packed-event-world-flag-save-slice-fixture.json rename fixtures/runtime/{packed-event-world-parity-save-slice.json => packed-event-world-flag-save-slice.json} (75%) delete mode 100644 fixtures/runtime/packed-event-world-parity-save-slice-fixture.json diff --git a/README.md b/README.md index 947ba0c..ff62fa6 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,13 @@ condition batch now decodes from checked-in metadata instead of fixture-only ids special-condition label ids, real economic-status ids, and the recovered `%1 Avail.` candidate template plus candidate-name side strings all lower into the runtime condition model. Checked-in whole-game descriptor metadata now drives the first real world-side effect batch too: -special-condition and candidate-availability setters import natively while world-flag rows remain -parity-only until keyed mapping is grounded. Explicit unmapped world-condition and -world-descriptor frontier buckets remain where current checked-in metadata still stops. Shell -purchase-flow 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. +special-condition and candidate-availability setters import natively, and descriptor `110` +`Disable Stock Buying and Selling` now lowers into the keyed runtime flag +`world.disable_stock_buying_and_selling`. Explicit unmapped world-condition and world-descriptor +frontier buckets still remain where current checked-in metadata stops. Shell purchase-flow 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. ## Project Docs diff --git a/crates/rrt-runtime/src/import.rs b/crates/rrt-runtime/src/import.rs index 39af7a9..3535c88 100644 --- a/crates/rrt-runtime/src/import.rs +++ b/crates/rrt-runtime/src/import.rs @@ -5359,7 +5359,7 @@ mod tests { } #[test] - fn leaves_real_world_flag_descriptor_parity_only_until_key_mapping_is_grounded() { + fn overlays_real_world_flag_descriptor_into_executable_runtime_record() { let base_state = state(); let save_slice = SmpLoadedSaveSlice { file_extension_hint: Some("gms".to_string()), @@ -5385,13 +5385,13 @@ mod tests { live_record_count: 1, live_entry_ids: vec![23], decoded_record_count: 1, - imported_runtime_record_count: 0, + imported_runtime_record_count: 1, records: vec![crate::SmpLoadedPackedEventRecordSummary { record_index: 0, live_entry_id: 23, - payload_offset: Some(0x7202), + payload_offset: Some(0x7200), payload_len: Some(120), - decode_status: "parity_only".to_string(), + decode_status: "executable".to_string(), payload_family: "real_packed_v1".to_string(), trigger_kind: Some(7), active: None, @@ -5405,33 +5405,133 @@ mod tests { grouped_effect_row_counts: vec![1, 0, 0, 0], grouped_effect_rows: vec![real_world_flag_row(true)], decoded_conditions: Vec::new(), - decoded_actions: vec![], - executable_import_ready: false, + decoded_actions: vec![RuntimeEffect::SetWorldFlag { + key: "world.disable_stock_buying_and_selling".to_string(), + value: true, + }], + executable_import_ready: true, notes: vec![ "decoded from grounded real 0x4e9a row framing".to_string(), - "world-flag descriptor identity is checked in, but keyed runtime mapping remains parity-only".to_string(), + "world-flag descriptor identity and keyed runtime mapping are checked in" + .to_string(), ], }], }), notes: vec![], }; - let import = project_save_slice_overlay_to_runtime_state_import( + let mut import = project_save_slice_overlay_to_runtime_state_import( &base_state, &save_slice, - "real-world-flag-parity-overlay", + "real-world-flag-overlay", None, ) .expect("overlay import should project"); - assert!(import.state.event_runtime_records.is_empty()); + assert_eq!(import.state.event_runtime_records.len(), 1); assert_eq!( import .state .packed_event_collection .as_ref() .and_then(|summary| summary.records[0].import_outcome.as_deref()), - Some("blocked_unmapped_world_descriptor") + Some("imported") + ); + crate::execute_step_command( + &mut import.state, + &crate::StepCommand::ServiceTriggerKind { trigger_kind: 7 }, + ) + .expect("trigger service should execute"); + assert_eq!( + import + .state + .world_flags + .get("world.disable_stock_buying_and_selling"), + Some(&true) + ); + } + + #[test] + fn overlays_real_world_flag_false_variant_into_executable_runtime_record() { + let base_state = state(); + 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, + 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: 24, + live_record_count: 1, + live_entry_ids: vec![24], + decoded_record_count: 1, + imported_runtime_record_count: 1, + records: vec![crate::SmpLoadedPackedEventRecordSummary { + record_index: 0, + live_entry_id: 24, + payload_offset: Some(0x7200), + payload_len: Some(120), + decode_status: "executable".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: packed_text_bands(), + 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_world_flag_row(false)], + decoded_conditions: Vec::new(), + decoded_actions: vec![RuntimeEffect::SetWorldFlag { + key: "world.disable_stock_buying_and_selling".to_string(), + value: false, + }], + executable_import_ready: true, + notes: vec![ + "decoded from grounded real 0x4e9a row framing".to_string(), + "world-flag descriptor identity and keyed runtime mapping are checked in" + .to_string(), + ], + }], + }), + notes: vec![], + }; + + let mut import = project_save_slice_overlay_to_runtime_state_import( + &base_state, + &save_slice, + "real-world-flag-false-overlay", + None, + ) + .expect("overlay import should project"); + + crate::execute_step_command( + &mut import.state, + &crate::StepCommand::ServiceTriggerKind { trigger_kind: 7 }, + ) + .expect("trigger service should execute"); + assert_eq!( + import + .state + .world_flags + .get("world.disable_stock_buying_and_selling"), + Some(&false) ); } diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index 14bc768..805d3d7 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -124,6 +124,7 @@ struct RealGroupedEffectDescriptorMetadata { label: &'static str, target_mask_bits: u8, parameter_family: &'static str, + runtime_key: Option<&'static str>, executable_in_runtime: bool, } @@ -133,6 +134,7 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad label: "Player Cash", target_mask_bits: 0x02, parameter_family: "player_finance_scalar", + runtime_key: None, executable_in_runtime: true, }, RealGroupedEffectDescriptorMetadata { @@ -140,6 +142,7 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad label: "Company Cash", target_mask_bits: 0x01, parameter_family: "company_finance_scalar", + runtime_key: None, executable_in_runtime: true, }, RealGroupedEffectDescriptorMetadata { @@ -147,6 +150,7 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad label: "Territory - Allow All", target_mask_bits: 0x05, parameter_family: "territory_access_toggle", + runtime_key: None, executable_in_runtime: true, }, RealGroupedEffectDescriptorMetadata { @@ -154,6 +158,7 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad label: "Economic Status", target_mask_bits: 0x08, parameter_family: "whole_game_state_enum", + runtime_key: None, executable_in_runtime: true, }, RealGroupedEffectDescriptorMetadata { @@ -161,6 +166,7 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad label: "Use Wartime Cargos", target_mask_bits: 0x08, parameter_family: "special_condition_scalar", + runtime_key: None, executable_in_runtime: true, }, RealGroupedEffectDescriptorMetadata { @@ -168,6 +174,7 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad label: "Turbo Diesel Availability", target_mask_bits: 0x08, parameter_family: "candidate_availability_scalar", + runtime_key: None, executable_in_runtime: true, }, RealGroupedEffectDescriptorMetadata { @@ -175,13 +182,15 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad label: "Disable Stock Buying and Selling", target_mask_bits: 0x08, parameter_family: "world_flag_toggle", - executable_in_runtime: false, + runtime_key: Some("world.disable_stock_buying_and_selling"), + executable_in_runtime: true, }, RealGroupedEffectDescriptorMetadata { descriptor_id: 9, label: "Confiscate All", target_mask_bits: 0x01, parameter_family: "company_confiscation_variant", + runtime_key: None, executable_in_runtime: true, }, RealGroupedEffectDescriptorMetadata { @@ -189,6 +198,7 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad label: "Deactivate Company", target_mask_bits: 0x01, parameter_family: "company_lifecycle_toggle", + runtime_key: None, executable_in_runtime: true, }, RealGroupedEffectDescriptorMetadata { @@ -196,6 +206,7 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad label: "Retire Train", target_mask_bits: 0x0d, parameter_family: "company_or_territory_asset_toggle", + runtime_key: None, executable_in_runtime: true, }, RealGroupedEffectDescriptorMetadata { @@ -203,6 +214,7 @@ const REAL_GROUPED_EFFECT_DESCRIPTOR_METADATA: [RealGroupedEffectDescriptorMetad label: "Company Track Pieces Buildable", target_mask_bits: 0x01, parameter_family: "company_build_limit_scalar", + runtime_key: None, executable_in_runtime: true, }, ]; @@ -2801,6 +2813,10 @@ fn runtime_candidate_availability_name(label: &str) -> String { .to_string() } +fn runtime_world_flag_key(descriptor_metadata: RealGroupedEffectDescriptorMetadata) -> Option { + descriptor_metadata.runtime_key.map(str::to_string) +} + fn decode_real_grouped_effect_actions( grouped_effect_rows: &[SmpLoadedPackedEventGroupedEffectRowSummary], compact_control: &SmpLoadedPackedEventCompactControlSummary, @@ -2895,6 +2911,16 @@ fn decode_real_grouped_effect_action( }); } + if descriptor_metadata.executable_in_runtime + && descriptor_metadata.descriptor_id == 110 + && row.row_shape == "bool_toggle" + { + return Some(RuntimeEffect::SetWorldFlag { + key: runtime_world_flag_key(descriptor_metadata)?, + value: row.raw_scalar_value != 0, + }); + } + if descriptor_metadata.executable_in_runtime && descriptor_metadata.descriptor_id == 9 && row.row_shape == "bool_toggle" @@ -8800,7 +8826,21 @@ mod tests { } #[test] - fn keeps_real_world_flag_descriptor_parity_only_with_checked_in_metadata() { + fn looks_up_checked_in_world_flag_descriptor_metadata() { + let metadata = + real_grouped_effect_descriptor_metadata(110).expect("descriptor metadata should exist"); + + assert_eq!(metadata.label, "Disable Stock Buying and Selling"); + assert_eq!(metadata.parameter_family, "world_flag_toggle"); + assert_eq!( + metadata.runtime_key, + Some("world.disable_stock_buying_and_selling") + ); + assert!(metadata.executable_in_runtime); + } + + #[test] + fn decodes_real_world_flag_descriptor_with_checked_in_runtime_key() { let grouped_row = build_real_grouped_effect_row(RealGroupedEffectRowSpec { descriptor_id: 110, opcode: 0, @@ -8863,8 +8903,14 @@ mod tests { .as_deref(), Some("world_flag_toggle") ); - assert!(summary.records[0].decoded_actions.is_empty()); - assert!(!summary.records[0].executable_import_ready); + assert_eq!( + summary.records[0].decoded_actions, + vec![RuntimeEffect::SetWorldFlag { + key: "world.disable_stock_buying_and_selling".to_string(), + value: true, + }] + ); + assert!(summary.records[0].executable_import_ready); } #[test] diff --git a/docs/README.md b/docs/README.md index 7dd65d1..c5bc96c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -105,9 +105,9 @@ The highest-value next passes are now: economic-status, and the generic `%1 Avail.` candidate-availability template plus candidate-name side strings all decode through checked-in world-condition metadata instead of fixture-only ids - the first real whole-game grouped-descriptor batch is now metadata-driven too: checked-in - descriptor metadata covers special-condition and candidate-availability setters, while the - current world-flag family stays parity-only until keyed flag identity is grounded well enough - for execution + descriptor metadata covers special-condition and candidate-availability setters, and descriptor + `110` `Disable Stock Buying and Selling` now executes too through the checked-in keyed runtime + flag `world.disable_stock_buying_and_selling` - 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 b4adbef..27d1e64 100644 --- a/docs/runtime-rehost-plan.md +++ b/docs/runtime-rehost-plan.md @@ -59,15 +59,17 @@ Implemented today: placeholder ids - checked-in whole-game grouped-descriptor metadata now drives the first real world-side effect batch too: real special-condition and candidate-availability setter rows now decode and import - through the ordinary runtime path, while world-flag rows remain parity-only until keyed flag - identity is grounded strongly enough for execution + through the ordinary runtime path, and the first real world-flag family now executes too: + descriptor `110` `Disable Stock Buying and Selling` lowers through checked-in keyed runtime + metadata into `world.disable_stock_buying_and_selling` 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, train, player, and numeric-threshold batches, with the world-side frontier now -centered primarily on still-unmapped world-flag families and any later state families that need -stronger checked-in descriptor or key recovery. Richer runtime ownership should still be added only -where a later descriptor or condition family needs more than the current event-owned roster. +whole-game, train, player, and numeric-threshold batches, with the world-side frontier now shifted +away from the first world-flag unlock and onto broader descriptor and condition recovery for later +state families that still need stronger checked-in metadata. Richer runtime ownership should still +be added only where a later descriptor or condition family needs more than the current event-owned +roster. ## Why This Boundary diff --git a/fixtures/runtime/packed-event-world-flag-save-slice-fixture.json b/fixtures/runtime/packed-event-world-flag-save-slice-fixture.json new file mode 100644 index 0000000..516c604 --- /dev/null +++ b/fixtures/runtime/packed-event-world-flag-save-slice-fixture.json @@ -0,0 +1,44 @@ +{ + "format_version": 1, + "fixture_id": "packed-event-world-flag-save-slice-fixture", + "source": { + "kind": "captured-runtime", + "description": "Fixture proving the first real whole-game world-flag descriptor imports and executes through the ordinary runtime path." + }, + "state_save_slice_path": "packed-event-world-flag-save-slice.json", + "commands": [ + { + "kind": "service_trigger_kind", + "trigger_kind": 6 + } + ], + "expected_summary": { + "packed_event_collection_present": true, + "packed_event_record_count": 1, + "packed_event_decoded_record_count": 1, + "packed_event_imported_runtime_record_count": 1, + "event_runtime_record_count": 1, + "packed_event_blocked_unmapped_world_descriptor_count": 0, + "total_event_record_service_count": 1, + "total_trigger_dispatch_count": 1 + }, + "expected_state_fragment": { + "world_flags": { + "world.disable_stock_buying_and_selling": true + }, + "packed_event_collection": { + "records": [ + { + "import_outcome": "imported", + "decoded_actions": [ + { + "kind": "set_world_flag", + "key": "world.disable_stock_buying_and_selling", + "value": true + } + ] + } + ] + } + } +} diff --git a/fixtures/runtime/packed-event-world-parity-save-slice.json b/fixtures/runtime/packed-event-world-flag-save-slice.json similarity index 75% rename from fixtures/runtime/packed-event-world-parity-save-slice.json rename to fixtures/runtime/packed-event-world-flag-save-slice.json index d824e66..6497fea 100644 --- a/fixtures/runtime/packed-event-world-parity-save-slice.json +++ b/fixtures/runtime/packed-event-world-flag-save-slice.json @@ -1,14 +1,14 @@ { "format_version": 1, - "save_slice_id": "packed-event-world-parity-save-slice", + "save_slice_id": "packed-event-world-flag-save-slice", "source": { - "description": "Tracked save-slice document preserving the remaining whole-game descriptor frontier.", - "original_save_filename": "captured-world-parity.gms", - "original_save_sha256": "world-parity-sample-sha256", + "description": "Tracked save-slice document proving the first real world-flag descriptor imports and executes.", + "original_save_filename": "captured-world-flag.gms", + "original_save_sha256": "world-flag-sample-sha256", "notes": [ "tracked as JSON save-slice document rather than raw .smp", - "keeps the still-unmapped world descriptor family explicit after the first real whole-game condition batch moved to checked-in metadata", - "whole-game world-flag descriptor identity is checked in, but keyed runtime mapping remains parity-only" + "descriptor 110 now lowers through checked-in runtime key metadata", + "whole-game world-flag descriptor sample for Disable Stock Buying and Selling" ] }, "save_slice": { @@ -35,14 +35,14 @@ "live_record_count": 1, "live_entry_ids": [49], "decoded_record_count": 1, - "imported_runtime_record_count": 0, + "imported_runtime_record_count": 1, "records": [ { "record_index": 0, "live_entry_id": 49, "payload_offset": 32320, "payload_len": 112, - "decode_status": "parity_only", + "decode_status": "executable", "payload_family": "real_packed_v1", "trigger_kind": 6, "one_shot": false, @@ -84,21 +84,27 @@ "semantic_preview": "Set Disable Stock Buying and Selling to TRUE", "locomotive_name": null, "notes": [ - "checked-in whole-game descriptor family without a grounded executable key mapping yet" + "checked-in whole-game world-flag descriptor family" ] } ], "decoded_conditions": [], - "decoded_actions": [], - "executable_import_ready": false, + "decoded_actions": [ + { + "kind": "set_world_flag", + "key": "world.disable_stock_buying_and_selling", + "value": true + } + ], + "executable_import_ready": true, "notes": [ - "world-side descriptor remains parity-only" + "checked-in whole-game world-flag grouped-effect import sample" ] } ] }, "notes": [ - "whole-game parity frontier sample" + "whole-game world-flag effect sample" ] } } diff --git a/fixtures/runtime/packed-event-world-parity-save-slice-fixture.json b/fixtures/runtime/packed-event-world-parity-save-slice-fixture.json deleted file mode 100644 index 1cb7d0b..0000000 --- a/fixtures/runtime/packed-event-world-parity-save-slice-fixture.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "format_version": 1, - "fixture_id": "packed-event-world-parity-save-slice-fixture", - "source": { - "kind": "captured-runtime", - "description": "Fixture keeping the current whole-game descriptor and condition frontier explicit." - }, - "state_save_slice_path": "packed-event-world-parity-save-slice.json", - "commands": [ - { - "kind": "service_trigger_kind", - "trigger_kind": 7 - } - ], - "expected_summary": { - "packed_event_collection_present": true, - "packed_event_record_count": 1, - "packed_event_decoded_record_count": 1, - "packed_event_imported_runtime_record_count": 0, - "event_runtime_record_count": 0, - "packed_event_blocked_unmapped_world_descriptor_count": 1, - "packed_event_blocked_unmapped_world_condition_count": 0 - }, - "expected_state_fragment": { - "packed_event_collection": { - "records": [ - { - "import_outcome": "blocked_unmapped_world_descriptor" - } - ] - } - } -}