feat: implement chunk unloading infrastructure
Chunk unloading system with cleanly abstracted decision layer. - ChunkMap: replace (bool, i32) timer tuple with () presence marker; remove FIFO cycle fields pending_unload/unload_cycle/unload_cursor/ unload_countdown. loaded_chunks is now HashMap<IVec2, ()>. - handle_chunk_events: simplify re-entrancy check to contains_key; remove parallel Mutex dedup machinery — load detection is now trivial. - is_standable: single HashMap get; return false for unloaded chunks instead of falling back to is_standable_slow (pathfinding correctness). - remove_chunk_data: bounds-iteration tile cleanup for chunk unload. - ChunkOwner component + chunk_entity_index: static terrain entities (floor tiles, fixtures, trees) tagged at spawn and despawned by chunk. Mobile entities (dorfs, pigs, rabbits) not tracked — safe by design since they were never indexed. - unload_chunk: canonical single-chunk unload function; cleans loaded_chunks, despawns entities, removes tilemap data, marks render chunk dirty via ChunkZKey::from_world, updates connectivity, fires rebake. Full docstring with step-by-step state map, working example for dynamic unload, and "what it does NOT handle" section. - dynamic_chunk_unloading_system: no-op stub. Re-enable by writing a system that diffs wanted vs loaded chunks and calls unload_chunk. Wired out of FixedUpdate during foundation work. - render chunk fix: ChunkZKey::from_world now pub(crate) so unload can derive the correct render chunk key (spans 4x4 world chunks) matching what build_quilted_terrain_sprites uses at spawn. - FloorTilePrefab::spawn: pre-existing bug — return Entity not ().
This commit is contained in:
@@ -232,14 +232,14 @@ impl TileMap {
|
||||
}
|
||||
|
||||
/// O(1) standability check using bit-packed chunk data.
|
||||
/// Falls back to HashMap lookups if chunk data is not available.
|
||||
/// Returns false if chunk is not loaded (unloaded chunks have no valid tiles).
|
||||
pub fn is_standable(&self, world_pos: IVec3) -> bool {
|
||||
let chunk_pos = world_to_chunk(world_pos);
|
||||
if let Some(chunk) = self.chunks.get(&chunk_pos) {
|
||||
let (local_x, local_y, z) = ChunkData::world_to_local(world_pos);
|
||||
return chunk.is_standable(local_x, local_y, z);
|
||||
}
|
||||
self.is_standable_slow(world_pos)
|
||||
let Some(chunk) = self.chunks.get(&chunk_pos) else {
|
||||
return false;
|
||||
};
|
||||
let (local_x, local_y, z) = ChunkData::world_to_local(world_pos);
|
||||
chunk.is_standable(local_x, local_y, z)
|
||||
}
|
||||
|
||||
/// Fallback standability check using HashMap lookups.
|
||||
@@ -283,4 +283,28 @@ impl TileMap {
|
||||
.map(|t| t.astar_weight)
|
||||
.unwrap_or(100)
|
||||
}
|
||||
|
||||
/// Remove all tile data for a specific chunk from the TileMap.
|
||||
/// Iterates all positions in the chunk volume and removes from HashMaps.
|
||||
/// Used during chunk unloading to clean up tile data.
|
||||
pub fn remove_chunk_data(&mut self, chunk_pos: IVec2) {
|
||||
use crate::constants::ITILE_SIZE;
|
||||
use crate::world::chunks::{CHUNK_SIZE, Z_ABOVE, Z_BELOW};
|
||||
|
||||
for local_x in 0..CHUNK_SIZE {
|
||||
for local_y in 0..CHUNK_SIZE {
|
||||
for z in -Z_BELOW as i32..=Z_ABOVE as i32 {
|
||||
let pos = IVec3::new(
|
||||
chunk_pos.x * CHUNK_SIZE + local_x,
|
||||
chunk_pos.y * CHUNK_SIZE + local_y,
|
||||
z,
|
||||
) * ITILE_SIZE;
|
||||
self.floor_tiles.remove(&pos);
|
||||
self.fixture_tiles.remove(&pos);
|
||||
self.item_tiles.remove(&pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.chunks.remove(&chunk_pos);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user