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 ().
Phase 1: Bit-packed standability
- Add ChunkData struct with 4 bitsets per chunk (stand_in/on for floor/fixture)
- Replace 4 HashMap lookups per standability check with O(1) bit operations
- Memory: ~2KB bitsets per chunk vs ~50KB HashMap overhead
Phase 2: Reactive connectivity
- Add dirty_chunks HashSet to ChunkMap for incremental updates
- update_chunk_connectivity now O(d) where d = dirty chunks
- Early exit when no changes, preventing O(N) full rebuilds
Phase 3: Async terrain baking
- Move terrain generation to AsyncComputeTaskPool
- spawn_terrain_tasks: non-blocking task spawn (~34µs)
- apply_terrain_blobs: batched entity spawn on main thread
- Eliminates main-thread stutters during world generation
- Add Arc<AHashMap> wrapper around floor_tiles, fixture_tiles, item_tiles
- Copy-on-write semantics: Arc::make_mut clones only if other Arcs exist
- Add insert_floor, insert_fixture, insert_item, remove_item methods
- Add get_floor_mut, get_fixture_mut for visibility updates
- Update all mutation sites to use new TileMap methods
- Enables cheap Arc::clone for async pathfinding workers
- Single-threaded pathfinding: no clone, direct access
- Multi-threaded pathfinding: clone Arc, read without locks
- Replace std HashMap (SipHash) with ahash::AHashMap for fast non-crypto hashing
- Pack tile data from tuples to structs: FloorTileData (~35 bytes) and FixtureTileData (~18 bytes)
- FloorTileData: pack 3 bools into single flags byte, use u8 for id/weight
- FixtureTileData: pack 2 bools into single flags byte
- Update all accessors: is_standable_tile, visibility, terrain generation, forestry
- Preparation for async pathfinding (Arc wrapping to come in follow-up)
Memory reduction: ~54% for floor tiles (76→35 bytes), ~62% for fixtures (48→18 bytes)
Hash performance: AHashMap uses fxhash, faster than SipHash for game data