// SPDX-License-Identifier: AGPL-3.0-only //! A bounded cache of regenerated chunk baselines, shared across the worker pool. use std::num::NonZeroUsize; use std::sync::{Arc, Mutex}; use lru::LruCache; use shared::generator::VoxelGenerator; use shared::world::{Chunk, ChunkPos}; /// A least-recently-used cache of chunk baselines, cloneable so every worker shares one store. #[derive(Clone)] pub struct ChunkCache { /// The shared LRU store. inner: Arc>>, } impl ChunkCache { /// Creates an empty cache holding at most `capacity` baselines before evicting the least-recently-used entry. #[must_use] pub fn new(capacity: NonZeroUsize) -> Self { Self { inner: Arc::new(Mutex::new(LruCache::new(capacity))), } } /// Returns the baseline for `pos`, generating and caching it on a miss. #[must_use] pub fn get_or_generate(&self, pos: ChunkPos, generator: &VoxelGenerator) -> Chunk { // A poisoned lock cannot yield a usable cache; recovering the guard lets generation proceed rather than propagating a panic across every worker that shares this cache. let mut guard = self .inner .lock() .unwrap_or_else(std::sync::PoisonError::into_inner); if let Some(hit) = guard.get(&pos) { return hit.clone(); } let chunk = generator.generate_chunk(pos); guard.put(pos, chunk.clone()); chunk } } #[cfg(test)] impl ChunkCache { /// Number of baselines currently resident. Test-only introspection. fn len(&self) -> usize { self.inner .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) .len() } /// Whether a baseline for `pos` is currently resident. Test-only introspection. fn contains(&self, pos: ChunkPos) -> bool { self.inner .lock() .unwrap_or_else(std::sync::PoisonError::into_inner) .contains(&pos) } } #[cfg(test)] #[path = "tests/chunk_cache.rs"] mod tests;