azalea_world/chunk/
storage.rs1use std::{
2 any::Any,
3 collections::hash_map::Entry,
4 fmt::{self, Debug},
5 ops::{Deref, DerefMut},
6 sync::{Arc, Weak},
7};
8
9use azalea_block::{BlockState, fluid_state::FluidState};
10use azalea_core::position::{BlockPos, ChunkBiomePos, ChunkBlockPos, ChunkPos};
11use azalea_registry::data::Biome;
12use nohash_hasher::IntMap;
13use parking_lot::RwLock;
14
15use crate::Chunk;
16
17pub struct ChunkStorage(pub Box<dyn ChunkStorageTrait>);
21
22pub trait ChunkStorageTrait: Send + Sync + Any {
23 fn min_y(&self) -> i32;
25 fn height(&self) -> u32;
27 #[must_use]
29 fn get(&self, pos: &ChunkPos) -> Option<Arc<RwLock<Chunk>>>;
30 #[must_use]
36 fn upsert(&mut self, pos: ChunkPos, chunk: Chunk) -> Arc<RwLock<Chunk>>;
37 fn chunks(&self) -> Box<[&ChunkPos]>;
38 fn clone_box(&self) -> Box<dyn ChunkStorageTrait>;
39
40 fn get_block_state(&self, pos: BlockPos) -> Option<BlockState> {
47 let chunk = self.get(&ChunkPos::from(pos))?;
48 let chunk = chunk.read();
49 chunk.get_block_state(&ChunkBlockPos::from(pos), self.min_y())
50 }
51 fn set_block_state(&self, pos: BlockPos, state: BlockState) -> Option<BlockState> {
56 if pos.y < self.min_y() || pos.y >= (self.min_y() + self.height() as i32) {
57 return None;
58 }
59 let chunk = self.get(&ChunkPos::from(pos))?;
60 let mut chunk = chunk.write();
61 Some(chunk.get_and_set_block_state(&ChunkBlockPos::from(pos), state, self.min_y()))
62 }
63 fn get_fluid_state(&self, pos: BlockPos) -> Option<FluidState> {
64 let block_state = self.get_block_state(pos)?;
65 Some(FluidState::from(block_state))
66 }
67 fn get_biome(&self, pos: BlockPos) -> Option<Biome> {
68 let chunk = self.get(&ChunkPos::from(pos))?;
69 let chunk = chunk.read();
70 chunk.get_biome(ChunkBiomePos::from(pos), self.min_y())
71 }
72}
73impl ChunkStorage {
74 pub fn new(height: u32, min_y: i32) -> Self {
77 Self(Box::new(WeakChunkStorage::new(height, min_y)))
78 }
79
80 pub fn new_with(inner: Box<dyn ChunkStorageTrait>) -> Self {
83 Self(inner)
84 }
85}
86
87impl Deref for ChunkStorage {
88 type Target = dyn ChunkStorageTrait;
89
90 fn deref(&self) -> &Self::Target {
91 &*self.0
92 }
93}
94impl DerefMut for ChunkStorage {
95 fn deref_mut(&mut self) -> &mut Self::Target {
96 &mut *self.0
97 }
98}
99impl Clone for ChunkStorage {
100 fn clone(&self) -> Self {
101 Self(self.0.clone_box())
102 }
103}
104
105#[derive(Clone, Debug)]
113pub struct WeakChunkStorage {
114 pub height: u32,
119 pub min_y: i32,
125 pub map: IntMap<ChunkPos, Weak<RwLock<Chunk>>>,
126}
127
128impl WeakChunkStorage {
129 pub fn new(height: u32, min_y: i32) -> Self {
130 WeakChunkStorage {
131 height,
132 min_y,
133 map: IntMap::default(),
134 }
135 }
136}
137impl ChunkStorageTrait for WeakChunkStorage {
138 fn min_y(&self) -> i32 {
139 self.min_y
140 }
141 fn height(&self) -> u32 {
142 self.height
143 }
144 fn get(&self, pos: &ChunkPos) -> Option<Arc<RwLock<Chunk>>> {
145 self.map.get(pos).and_then(|chunk| chunk.upgrade())
146 }
147
148 fn upsert(&mut self, pos: ChunkPos, chunk: Chunk) -> Arc<RwLock<Chunk>> {
149 match self.map.entry(pos) {
150 Entry::Occupied(mut e) => {
151 if let Some(existing) = e.get_mut().upgrade() {
152 *existing.write() = chunk;
153 existing
154 } else {
155 let arc = Arc::new(RwLock::new(chunk));
156 e.insert(Arc::downgrade(&arc));
157 arc
158 }
159 }
160 Entry::Vacant(e) => {
161 let arc = Arc::new(RwLock::new(chunk));
162 e.insert(Arc::downgrade(&arc));
163 arc
164 }
165 }
166 }
167
168 fn chunks(&self) -> Box<[&ChunkPos]> {
169 self.map.keys().collect::<Vec<_>>().into_boxed_slice()
170 }
171
172 fn clone_box(&self) -> Box<dyn ChunkStorageTrait> {
173 Box::new(self.clone())
174 }
175}
176
177impl Default for WeakChunkStorage {
178 fn default() -> Self {
179 Self::new(384, -64)
180 }
181}
182impl Default for ChunkStorage {
183 fn default() -> Self {
184 Self::new(384, -64)
185 }
186}
187
188impl Debug for ChunkStorage {
189 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190 f.debug_struct("ChunkStorage")
191 .field("min_y", &self.0.min_y())
192 .field("height", &self.0.height())
193 .field("chunk_count", &self.0.chunks().len())
194 .finish()
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use azalea_core::position::{BlockPos, ChunkPos};
201
202 use crate::{
203 Chunk,
204 chunk::{partial::PartialChunkStorage, storage::ChunkStorage},
205 };
206
207 #[test]
208 fn test_out_of_bounds_y() {
209 let mut chunk_storage = ChunkStorage::default();
210 let mut partial_chunk_storage = PartialChunkStorage::default();
211 partial_chunk_storage.set(
212 &ChunkPos { x: 0, z: 0 },
213 Some(Chunk::default()),
214 &mut chunk_storage,
215 );
216 assert!(
217 chunk_storage
218 .get_block_state(BlockPos { x: 0, y: 319, z: 0 })
219 .is_some()
220 );
221 assert!(
222 chunk_storage
223 .get_block_state(BlockPos { x: 0, y: 320, z: 0 })
224 .is_none()
225 );
226 assert!(
227 chunk_storage
228 .get_block_state(BlockPos { x: 0, y: 338, z: 0 })
229 .is_none()
230 );
231 assert!(
232 chunk_storage
233 .get_block_state(BlockPos { x: 0, y: -64, z: 0 })
234 .is_some()
235 );
236 assert!(
237 chunk_storage
238 .get_block_state(BlockPos { x: 0, y: -65, z: 0 })
239 .is_none()
240 );
241 }
242}