fix(simulation): address PR #81 review — critical and high-priority issues
Critical fixes: - storyteller: replace .expect() with guard + log in activation_pass (Hoshe #1) - content/loader: validate inverted tile_bounds before iteration (Hoshe #C) - save_io: persist ActivationState on save/load (Tyre #7) High-priority fixes: - storyteller: f64 intermediate for observation_time_ticks scoring (Hoshe #A) - storyteller: deduplicate copresent entities before scoring (Hoshe #B) - storyteller: skip activation_pass at tick 0 (Hoshe #G) - storyteller: explicit .before(advance_tick) ordering (Tyre #9) - save_state: insert EngagementRecord on NPC deserialize (Tyre #10) - save_io: reset TriangleActivatedQueue + MovementHistoryBuffer on load (Tyre #8) - debug: validate teleport target walkability (Hoshe #E) - debug: reject SkipToContamination when tick past delay (Hoshe #F) - debug: DebugEnabled defaults to cfg!(debug_assertions) (Hoshe #3) - debug: log when response overwritten (Hoshe #2) - content/loader: error on tile dimension mismatch (Hoshe #5) - content/types: DistrictMeta.description optional (Hoshe #D) - types: version docs updated to v18 (Tyre #1, #2, #3) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -377,29 +377,43 @@ fn apply_location_tiles(location: &Location, walkability: &mut WalkabilityMap) -
|
||||
return false;
|
||||
};
|
||||
|
||||
// Guard: inverted bounds cause (y_max - y_min) to be negative, which wraps to
|
||||
// ~18 quintillion when cast to usize, silently writing tiles at garbage positions.
|
||||
if bounds.x_min > bounds.x_max || bounds.y_min > bounds.y_max {
|
||||
tracing::error!(
|
||||
"Location '{}': inverted tile_bounds (x: {}..={}, y: {}..={}), skipping",
|
||||
location.canonical_id,
|
||||
bounds.x_min, bounds.x_max,
|
||||
bounds.y_min, bounds.y_max,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
let expected_height = (bounds.y_max - bounds.y_min + 1) as usize;
|
||||
let expected_width = (bounds.x_max - bounds.x_min + 1) as usize;
|
||||
|
||||
if tiles.len() != expected_height {
|
||||
tracing::warn!(
|
||||
"Location '{}': tile row count {} != expected height {} (from tile_bounds)",
|
||||
tracing::error!(
|
||||
"Location '{}': tile row count {} != expected height {} (from tile_bounds) — skipping to prevent walkability holes",
|
||||
location.canonical_id,
|
||||
tiles.len(),
|
||||
expected_height,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (row_idx, row) in tiles.iter().enumerate() {
|
||||
let y = bounds.y_min + row_idx as i32;
|
||||
|
||||
if row.len() != expected_width {
|
||||
tracing::warn!(
|
||||
"Location '{}' row {}: length {} != expected width {}",
|
||||
tracing::error!(
|
||||
"Location '{}' row {}: length {} != expected width {} — skipping row to prevent walkability holes",
|
||||
location.canonical_id,
|
||||
row_idx,
|
||||
row.len(),
|
||||
expected_width,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (col_idx, ch) in row.chars().enumerate() {
|
||||
|
||||
Reference in New Issue
Block a user