Extend event effects through add-building strip
This commit is contained in:
parent
4945430149
commit
b2da02befa
7 changed files with 3319 additions and 1161 deletions
|
|
@ -125,6 +125,9 @@ enum Command {
|
|||
RuntimeInspectSmp {
|
||||
smp_path: PathBuf,
|
||||
},
|
||||
RuntimeInspectCompactEventDispatchCluster {
|
||||
root_path: PathBuf,
|
||||
},
|
||||
RuntimeSummarizeSaveLoad {
|
||||
smp_path: PathBuf,
|
||||
},
|
||||
|
|
@ -293,6 +296,51 @@ struct RuntimeSmpInspectionOutput {
|
|||
inspection: SmpInspectionReport,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct RuntimeCompactEventDispatchClusterOutput {
|
||||
root_path: String,
|
||||
report: RuntimeCompactEventDispatchClusterReport,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct RuntimeCompactEventDispatchClusterReport {
|
||||
maps_scanned: usize,
|
||||
maps_with_event_runtime_collection: usize,
|
||||
maps_with_dispatch_strip_records: usize,
|
||||
dispatch_strip_record_count: usize,
|
||||
unknown_descriptor_ids: Vec<u32>,
|
||||
unknown_descriptor_special_condition_label_matches: Vec<String>,
|
||||
unknown_descriptor_occurrences:
|
||||
BTreeMap<u32, Vec<RuntimeCompactEventDispatchClusterOccurrence>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
struct RuntimeCompactEventDispatchClusterOccurrence {
|
||||
path: String,
|
||||
record_index: usize,
|
||||
live_entry_id: u32,
|
||||
payload_family: String,
|
||||
signature_family: Option<String>,
|
||||
condition_tuples: Vec<RuntimeCompactEventDispatchClusterConditionTuple>,
|
||||
rows: Vec<RuntimeCompactEventDispatchClusterRow>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
struct RuntimeCompactEventDispatchClusterConditionTuple {
|
||||
raw_condition_id: i32,
|
||||
subtype: u8,
|
||||
metric: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
struct RuntimeCompactEventDispatchClusterRow {
|
||||
group_index: usize,
|
||||
descriptor_id: u32,
|
||||
descriptor_label: Option<String>,
|
||||
opcode: u8,
|
||||
raw_scalar_value: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct RuntimeSaveLoadSummaryOutput {
|
||||
path: String,
|
||||
|
|
@ -898,6 +946,9 @@ fn real_main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
Command::RuntimeInspectSmp { smp_path } => {
|
||||
run_runtime_inspect_smp(&smp_path)?;
|
||||
}
|
||||
Command::RuntimeInspectCompactEventDispatchCluster { root_path } => {
|
||||
run_runtime_inspect_compact_event_dispatch_cluster(&root_path)?;
|
||||
}
|
||||
Command::RuntimeSummarizeSaveLoad { smp_path } => {
|
||||
run_runtime_summarize_save_load(&smp_path)?;
|
||||
}
|
||||
|
|
@ -1116,6 +1167,14 @@ fn parse_command() -> Result<Command, Box<dyn std::error::Error>> {
|
|||
smp_path: PathBuf::from(path),
|
||||
})
|
||||
}
|
||||
[command, subcommand, root_path]
|
||||
if command == "runtime"
|
||||
&& subcommand == "inspect-compact-event-dispatch-cluster" =>
|
||||
{
|
||||
Ok(Command::RuntimeInspectCompactEventDispatchCluster {
|
||||
root_path: PathBuf::from(root_path),
|
||||
})
|
||||
}
|
||||
[command, subcommand, path]
|
||||
if command == "runtime" && subcommand == "summarize-save-load" =>
|
||||
{
|
||||
|
|
@ -1400,7 +1459,7 @@ fn parse_command() -> Result<Command, Box<dyn std::error::Error>> {
|
|||
})
|
||||
}
|
||||
_ => Err(
|
||||
"usage: rrt-cli [validate [repo-root] | finance eval <snapshot.json> | finance diff <left.json> <right.json> | runtime validate-fixture <fixture.json> | runtime summarize-fixture <fixture.json> | runtime export-fixture-state <fixture.json> <snapshot.json> | runtime diff-state <left.json> <right.json> | runtime summarize-state <snapshot.json> | runtime import-state <input.json> <snapshot.json> | runtime inspect-smp <file.smp> | runtime summarize-save-load <file.smp> | runtime load-save-slice <file.smp> | runtime inspect-save-company-chairman <file.smp> | runtime inspect-save-placed-structure-triplets <file.smp> | runtime compare-region-fixed-row-runs <left.gms> <right.gms> | runtime inspect-periodic-company-service-trace <file.smp> | runtime inspect-region-service-trace <file.smp> | runtime inspect-infrastructure-asset-trace <file.smp> | runtime inspect-save-region-queued-notice-records <file.smp> | runtime inspect-placed-structure-dynamic-side-buffer <file.smp> | runtime inspect-unclassified-save-collections <file.smp> | runtime import-save-state <file.smp> <snapshot.json> | runtime export-save-slice <file.smp> <save-slice.json> | runtime export-overlay-import <snapshot.json> <save-slice.json> <overlay-import.json> | runtime inspect-pk4 <file.pk4> | runtime inspect-cargo-types <CargoTypes-dir> | runtime inspect-cargo-skins <Cargo106.PK4> | runtime inspect-cargo-economy-sources <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-production-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-price-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-win <file.win> | runtime extract-pk4-entry <file.pk4> <entry-name> <output-path> | runtime inspect-campaign-exe <RT3.exe> | runtime compare-classic-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-105-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-candidate-table <file1> <file2> [fileN...] | runtime compare-recipe-book-lines <file1> <file2> [fileN...] | runtime compare-setup-payload-core <file1> <file2> [fileN...] | runtime compare-setup-launch-payload <file1> <file2> [fileN...] | runtime compare-post-special-conditions-scalars <file1> <file2> [fileN...] | runtime scan-candidate-table-headers <root-dir> | runtime scan-special-conditions <root-dir> | runtime scan-aligned-runtime-rule-band <root-dir> | runtime scan-post-special-conditions-scalars <root-dir> | runtime scan-post-special-conditions-tail <root-dir> | runtime scan-recipe-book-lines <root-dir> | runtime export-profile-block <save.gms> <profile.json>]"
|
||||
"usage: rrt-cli [validate [repo-root] | finance eval <snapshot.json> | finance diff <left.json> <right.json> | runtime validate-fixture <fixture.json> | runtime summarize-fixture <fixture.json> | runtime export-fixture-state <fixture.json> <snapshot.json> | runtime diff-state <left.json> <right.json> | runtime summarize-state <snapshot.json> | runtime import-state <input.json> <snapshot.json> | runtime inspect-smp <file.smp> | runtime inspect-compact-event-dispatch-cluster <maps-dir> | runtime summarize-save-load <file.smp> | runtime load-save-slice <file.smp> | runtime inspect-save-company-chairman <file.smp> | runtime inspect-save-placed-structure-triplets <file.smp> | runtime compare-region-fixed-row-runs <left.gms> <right.gms> | runtime inspect-periodic-company-service-trace <file.smp> | runtime inspect-region-service-trace <file.smp> | runtime inspect-infrastructure-asset-trace <file.smp> | runtime inspect-save-region-queued-notice-records <file.smp> | runtime inspect-placed-structure-dynamic-side-buffer <file.smp> | runtime inspect-unclassified-save-collections <file.smp> | runtime import-save-state <file.smp> <snapshot.json> | runtime export-save-slice <file.smp> <save-slice.json> | runtime export-overlay-import <snapshot.json> <save-slice.json> <overlay-import.json> | runtime inspect-pk4 <file.pk4> | runtime inspect-cargo-types <CargoTypes-dir> | runtime inspect-cargo-skins <Cargo106.PK4> | runtime inspect-cargo-economy-sources <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-production-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-cargo-price-selector <CargoTypes-dir> <Cargo106.PK4> | runtime inspect-win <file.win> | runtime extract-pk4-entry <file.pk4> <entry-name> <output-path> | runtime inspect-campaign-exe <RT3.exe> | runtime compare-classic-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-105-profile <save1.gms> <save2.gms> [saveN.gms...] | runtime compare-candidate-table <file1> <file2> [fileN...] | runtime compare-recipe-book-lines <file1> <file2> [fileN...] | runtime compare-setup-payload-core <file1> <file2> [fileN...] | runtime compare-setup-launch-payload <file1> <file2> [fileN...] | runtime compare-post-special-conditions-scalars <file1> <file2> [fileN...] | runtime scan-candidate-table-headers <root-dir> | runtime scan-special-conditions <root-dir> | runtime scan-aligned-runtime-rule-band <root-dir> | runtime scan-post-special-conditions-scalars <root-dir> | runtime scan-post-special-conditions-tail <root-dir> | runtime scan-recipe-book-lines <root-dir> | runtime export-profile-block <save.gms> <profile.json>]"
|
||||
.into(),
|
||||
),
|
||||
}
|
||||
|
|
@ -1605,6 +1664,116 @@ fn run_runtime_inspect_smp(smp_path: &Path) -> Result<(), Box<dyn std::error::Er
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn run_runtime_inspect_compact_event_dispatch_cluster(
|
||||
root_path: &Path,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut input_paths = Vec::new();
|
||||
collect_compact_event_dispatch_cluster_input_paths(root_path, &mut input_paths)?;
|
||||
input_paths.sort();
|
||||
|
||||
let mut maps_with_event_runtime_collection = 0usize;
|
||||
let mut maps_with_dispatch_strip_records = 0usize;
|
||||
let mut dispatch_strip_record_count = 0usize;
|
||||
let mut unknown_descriptor_occurrences =
|
||||
BTreeMap::<u32, Vec<RuntimeCompactEventDispatchClusterOccurrence>>::new();
|
||||
|
||||
for path in &input_paths {
|
||||
let inspection = inspect_smp_file(path)?;
|
||||
let Some(summary) = inspection.event_runtime_collection_summary else {
|
||||
continue;
|
||||
};
|
||||
maps_with_event_runtime_collection += 1;
|
||||
|
||||
let mut map_dispatch_strip_record_count = 0usize;
|
||||
for record in &summary.records {
|
||||
let matching_rows = record
|
||||
.grouped_effect_rows
|
||||
.iter()
|
||||
.filter(|row| {
|
||||
compact_event_dispatch_strip_opcode(row.opcode)
|
||||
&& row.descriptor_label.is_none()
|
||||
})
|
||||
.fold(
|
||||
BTreeMap::<u32, Vec<RuntimeCompactEventDispatchClusterRow>>::new(),
|
||||
|mut grouped, row| {
|
||||
grouped.entry(row.descriptor_id).or_default().push(
|
||||
RuntimeCompactEventDispatchClusterRow {
|
||||
group_index: row.group_index,
|
||||
descriptor_id: row.descriptor_id,
|
||||
descriptor_label: row.descriptor_label.clone(),
|
||||
opcode: row.opcode,
|
||||
raw_scalar_value: row.raw_scalar_value,
|
||||
},
|
||||
);
|
||||
grouped
|
||||
},
|
||||
);
|
||||
if matching_rows.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
map_dispatch_strip_record_count += 1;
|
||||
let condition_tuples = record
|
||||
.standalone_condition_rows
|
||||
.iter()
|
||||
.map(|row| RuntimeCompactEventDispatchClusterConditionTuple {
|
||||
raw_condition_id: row.raw_condition_id,
|
||||
subtype: row.subtype,
|
||||
metric: row.metric.clone(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let signature_family = compact_event_signature_family_from_notes(&record.notes);
|
||||
|
||||
for (descriptor_id, rows) in matching_rows {
|
||||
unknown_descriptor_occurrences
|
||||
.entry(descriptor_id)
|
||||
.or_default()
|
||||
.push(RuntimeCompactEventDispatchClusterOccurrence {
|
||||
path: path.display().to_string(),
|
||||
record_index: record.record_index,
|
||||
live_entry_id: record.live_entry_id,
|
||||
payload_family: record.payload_family.clone(),
|
||||
signature_family: signature_family.clone(),
|
||||
condition_tuples: condition_tuples.clone(),
|
||||
rows,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if map_dispatch_strip_record_count > 0 {
|
||||
maps_with_dispatch_strip_records += 1;
|
||||
dispatch_strip_record_count += map_dispatch_strip_record_count;
|
||||
}
|
||||
}
|
||||
|
||||
let unknown_descriptor_ids = unknown_descriptor_occurrences
|
||||
.keys()
|
||||
.copied()
|
||||
.collect::<Vec<_>>();
|
||||
let unknown_descriptor_special_condition_label_matches = unknown_descriptor_ids
|
||||
.iter()
|
||||
.filter_map(|descriptor_id| {
|
||||
special_condition_label_for_compact_dispatch_descriptor(*descriptor_id)
|
||||
.map(|label| format!("{descriptor_id} -> {label}"))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let report = RuntimeCompactEventDispatchClusterOutput {
|
||||
root_path: root_path.display().to_string(),
|
||||
report: RuntimeCompactEventDispatchClusterReport {
|
||||
maps_scanned: input_paths.len(),
|
||||
maps_with_event_runtime_collection,
|
||||
maps_with_dispatch_strip_records,
|
||||
dispatch_strip_record_count,
|
||||
unknown_descriptor_ids,
|
||||
unknown_descriptor_special_condition_label_matches,
|
||||
unknown_descriptor_occurrences,
|
||||
},
|
||||
};
|
||||
println!("{}", serde_json::to_string_pretty(&report)?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_runtime_summarize_save_load(smp_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let inspection = inspect_smp_file(smp_path)?;
|
||||
let summary = inspection.save_load_summary.ok_or_else(|| {
|
||||
|
|
@ -3731,6 +3900,73 @@ fn parse_special_condition_slot_index(label: &str) -> Option<u8> {
|
|||
slot_index.parse().ok()
|
||||
}
|
||||
|
||||
fn compact_event_dispatch_strip_opcode(opcode: u8) -> bool {
|
||||
matches!(opcode, 0x04..=0x08 | 0x0d | 0x10..=0x13 | 0x16)
|
||||
}
|
||||
|
||||
fn compact_event_signature_family_from_notes(notes: &[String]) -> Option<String> {
|
||||
notes.iter().find_map(|note| {
|
||||
note.strip_prefix("compact signature family = ")
|
||||
.map(ToString::to_string)
|
||||
})
|
||||
}
|
||||
|
||||
fn special_condition_label_for_compact_dispatch_descriptor(
|
||||
descriptor_id: u32,
|
||||
) -> Option<&'static str> {
|
||||
let band_index = descriptor_id.checked_sub(535)? as usize;
|
||||
SPECIAL_CONDITION_LABELS.get(band_index).copied()
|
||||
}
|
||||
|
||||
fn collect_compact_event_dispatch_cluster_input_paths(
|
||||
root_path: &Path,
|
||||
out: &mut Vec<PathBuf>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let metadata = match fs::symlink_metadata(root_path) {
|
||||
Ok(metadata) => metadata,
|
||||
Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => return Ok(()),
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
if metadata.file_type().is_symlink() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if root_path.is_file() {
|
||||
if root_path
|
||||
.extension()
|
||||
.and_then(|ext| ext.to_str())
|
||||
.is_some_and(|ext| ext.eq_ignore_ascii_case("gmp"))
|
||||
{
|
||||
out.push(root_path.to_path_buf());
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let entries = match fs::read_dir(root_path) {
|
||||
Ok(entries) => entries,
|
||||
Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => return Ok(()),
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
|
||||
for entry in entries {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
collect_compact_event_dispatch_cluster_input_paths(&path, out)?;
|
||||
continue;
|
||||
}
|
||||
if path
|
||||
.extension()
|
||||
.and_then(|ext| ext.to_str())
|
||||
.is_some_and(|ext| ext.eq_ignore_ascii_case("gmp"))
|
||||
{
|
||||
out.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_hex_offset(text: &str) -> Option<usize> {
|
||||
text.strip_prefix("0x")
|
||||
.and_then(|digits| usize::from_str_radix(digits, 16).ok())
|
||||
|
|
|
|||
|
|
@ -9485,6 +9485,16 @@ fn parse_event_runtime_collection_summary_with_tag_width(
|
|||
.collect::<Vec<_>>();
|
||||
mutation_candidate_unknown_descriptor_ids.sort_unstable();
|
||||
mutation_candidate_unknown_descriptor_ids.dedup();
|
||||
let mut mutation_candidate_special_condition_label_matches =
|
||||
mutation_candidate_unknown_descriptor_ids
|
||||
.iter()
|
||||
.filter_map(|descriptor_id| {
|
||||
known_special_condition_label_for_compact_descriptor_id(*descriptor_id)
|
||||
.map(|label| format!("{descriptor_id} -> {label}"))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
mutation_candidate_special_condition_label_matches.sort();
|
||||
mutation_candidate_special_condition_label_matches.dedup();
|
||||
let mut dispatch_strip_unknown_condition_ids = records
|
||||
.iter()
|
||||
.filter(|record| {
|
||||
|
|
@ -9533,6 +9543,12 @@ fn parse_event_runtime_collection_summary_with_tag_width(
|
|||
mutation_candidate_unknown_descriptor_ids
|
||||
));
|
||||
}
|
||||
if !mutation_candidate_special_condition_label_matches.is_empty() {
|
||||
control_lane_notes.push(format!(
|
||||
"unlabeled 0x00431b20 dispatch-strip descriptor ids matching known special-condition label_id-2000 values = {:?}",
|
||||
mutation_candidate_special_condition_label_matches
|
||||
));
|
||||
}
|
||||
if !dispatch_strip_unknown_condition_ids.is_empty() {
|
||||
control_lane_notes.push(format!(
|
||||
"standalone condition ids still missing checked-in labels in the 0x00431b20 dispatch strip = {:?}",
|
||||
|
|
@ -9587,6 +9603,16 @@ fn opcode_reaches_world_apply_compact_runtime_effect_dispatch_strip(opcode: u8)
|
|||
matches!(opcode, 0x04..=0x08 | 0x0d | 0x10..=0x13 | 0x16)
|
||||
}
|
||||
|
||||
fn known_special_condition_label_for_compact_descriptor_id(
|
||||
descriptor_id: u32,
|
||||
) -> Option<&'static str> {
|
||||
let label_id = descriptor_id.checked_add(2000)?;
|
||||
KNOWN_SPECIAL_CONDITION_DEFINITIONS
|
||||
.iter()
|
||||
.find(|definition| definition.label_id == label_id)
|
||||
.map(|definition| definition.label)
|
||||
}
|
||||
|
||||
fn try_parse_nondirect_event_runtime_record_summaries(
|
||||
records_payload: &[u8],
|
||||
records_payload_offset: usize,
|
||||
|
|
@ -23874,8 +23900,8 @@ mod tests {
|
|||
#[test]
|
||||
fn checked_in_event_effect_table_covers_the_full_exported_descriptor_set() {
|
||||
let rows = checked_in_event_effect_descriptor_rows();
|
||||
assert_eq!(rows.len(), 520);
|
||||
for descriptor_id in 0..520_u32 {
|
||||
assert_eq!(rows.len(), 614);
|
||||
for descriptor_id in 0..614_u32 {
|
||||
assert!(
|
||||
real_grouped_effect_descriptor_metadata(descriptor_id).is_some(),
|
||||
"descriptor {descriptor_id} should be recoverable from the checked-in effect table"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue