mc_core/world/
level.rs

1use std::sync::{RwLock, Arc, RwLockReadGuard, RwLockWriteGuard};
2use std::collections::{HashMap, HashSet};
3use std::collections::hash_map::Entry;
4use std::fmt::{Debug, Formatter};
5
6use hecs::{World as EcsWorld, EntityBuilder, Entity, EntityRef};
7use uuid::Uuid;
8
9use crate::entity::{GlobalEntities, EntityType};
10use crate::block::{GlobalBlocks, BlockState};
11use crate::biome::GlobalBiomes;
12use crate::heightmap::GlobalHeightmaps;
13use crate::pos::{EntityPos, BlockPos};
14use crate::debug;
15
16use super::source::{LevelSource, ChunkLoadRequest, ChunkSaveRequest, LevelSourceError, ProtoChunk};
17use super::chunk::{Chunk, ChunkHeight, ChunkResult, ChunkError};
18
19
20/// A structure that contains the static environment of a World, this can be used for multiple
21/// `Level`s through an `Arc<LevelEnv>`.
22pub struct LevelEnv {
23    /// Global blocks palette.
24    pub blocks: GlobalBlocks,
25    /// Global biomes palette.
26    pub biomes: GlobalBiomes,
27    /// Global entity types palette.
28    pub entities: GlobalEntities,
29    /// Global heightmap types palette.
30    pub heightmaps: GlobalHeightmaps
31}
32
33impl LevelEnv {
34
35    pub fn new(
36        blocks: GlobalBlocks,
37        biomes: GlobalBiomes,
38        entities: GlobalEntities,
39        heightmaps: GlobalHeightmaps
40    ) -> Self {
41        LevelEnv {
42            blocks,
43            biomes,
44            entities,
45            heightmaps
46        }
47    }
48
49}
50
51impl Debug for LevelEnv {
52    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
53        f.debug_struct("LevelEnv")
54            .field("states_count", &self.blocks.states_count())
55            .field("blocks_count", &self.blocks.blocks_count())
56            .field("entity_types_count", &self.entities.entity_types_count())
57            .field("heightmaps_count", &self.heightmaps.heightmaps_count())
58            .finish()
59    }
60}
61
62
63/// Main storage for a level, part of a World. This structure is intentionally not `Sync + Send`,
64/// however each chunk is stored in a `RwLock` in order to make them shared across threads if
65/// you want.
66pub struct Level {
67    /// The unique ID of this level (among all levels of the world).
68    id: String,
69    /// The global environment used by this level, this environment should not be mutated afterward.
70    /// It contains the global blocks and biomes palettes, it also contains
71    env: Arc<LevelEnv>,
72    /// The level loader used to load uncached chunks either from a generator or from an anvil file
73    /// system loader.
74    source: Box<dyn LevelSource>,
75    /// A set of chunk positions that has been requested to the level source.
76    loading_chunks: HashSet<(i32, i32)>,
77    /// The configured height of this level.
78    height: ChunkHeight,
79    /// Chunk storage.
80    pub chunks: ChunkStorage,
81    /// Entities storage.
82    pub entities: EntityStorage
83}
84
85impl Level {
86
87    pub fn new<S>(id: String, env: Arc<LevelEnv>, height: ChunkHeight, source: S) -> Self
88    where
89        S: LevelSource + 'static,
90    {
91
92        assert_ne!(env.blocks.states_count(), 0, "The given environment has no state, a level requires at least one block state.");
93        assert_ne!(env.biomes.biomes_count(), 0, "The given environment has no biome, a level requires at least one biome.");
94
95        Level {
96            id,
97            height,
98            source: Box::new(source),
99            loading_chunks: HashSet::new(),
100            chunks: ChunkStorage {
101                chunks: HashMap::new()
102            },
103            entities: EntityStorage {
104                ecs: EcsWorld::new(),
105                builder: EntityBuilder::new()
106            },
107            env,
108        }
109
110    }
111
112    /// Return the unique ID (unique in the owning world).
113    pub fn get_id(&self) -> &String {
114        &self.id
115    }
116
117    /// Return the level environment used by this level.
118    pub fn get_env(&self) -> &Arc<LevelEnv> {
119        &self.env
120    }
121
122    /// Return the minimum and maximum chunks position allowed in this world.
123    /// The limits can -128 to 127, it is more than enough.
124    pub fn get_height(&self) -> ChunkHeight {
125        self.height
126    }
127
128    // CHUNKS LOADING (FROM SOURCE) //
129
130    /// Request internal level source to load the given chunk.
131    pub fn request_chunk_load(&mut self, cx: i32, cz: i32) -> bool {
132        if !self.loading_chunks.contains(&(cx, cz)) {
133            // debug!("Request chunk load at {}/{}", cx, cz);
134            match self.source.request_chunk_load(ChunkLoadRequest {
135                env: Arc::clone(&self.env),
136                height: self.height,
137                cx,
138                cz
139            }) {
140                Ok(_) => {
141                    self.loading_chunks.insert((cx, cz));
142                    true
143                }
144                Err(_) => false
145            }
146        } else {
147            false
148        }
149    }
150
151    /// Poll loaded chunks from internal level source, all successfully loaded chunks
152    /// are added to the underlying `LevelStorage`. The callback is called for each
153    /// loaded chunks or loading error.
154    pub fn load_chunks_with_callback<F>(&mut self, mut callback: F)
155    where
156        F: FnMut(i32, i32, Result<&Arc<RwLock<Chunk>>, LevelSourceError>),
157    {
158        while let Some(res) = self.source.poll_chunk() {
159            match res {
160                Ok(ProtoChunk {
161                    inner: chunk,
162                    mut proto_entities,
163                    dirty
164                }) => {
165
166                    let mut chunk = *chunk;
167                    let (cx, cz) = chunk.get_position();
168                    // debug!("Loaded chunk at {}/{}", cx, cz);
169
170                    self.loading_chunks.remove(&(cx, cz));
171
172                    // This list retains all entity handles with the same order as proto chunk
173                    // entities data.
174                    let mut entities_handles = Vec::with_capacity(proto_entities.len());
175
176                    // Then we only add entities without their passengers, but store their handles.
177                    for (entity_builder, _) in &mut proto_entities {
178                        unsafe {
179                            let entity = self.entities.add_entity_unchecked(entity_builder);
180                            chunk.add_entity_unchecked(entity);
181                            entities_handles.push(entity);
182                        }
183                    }
184
185                    // Now that we have created all our entities, we can set passengers.
186                    for (i, (_, passengers)) in proto_entities.into_iter().enumerate() {
187                        if !passengers.is_empty() {
188
189                            // Here we don't check is passengers is empty because ProtoChunk only
190                            // set 'Some' if there are passengers.
191
192                            // SAFETY: Unwrap is safe because the entity was just created with `BaseEntity` component.
193                            let mut base_entity = self.entities.ecs.get_mut::<BaseEntity>(entities_handles[i]).unwrap();
194                            let mut passengers_handles = Vec::with_capacity(passengers.len());
195
196                            for passenger_proto_index in passengers {
197                                passengers_handles.push(entities_handles[passenger_proto_index]);
198                            }
199
200                            base_entity.passengers = Some(passengers_handles);
201
202                        }
203                    }
204
205                    let chunk_arc = self.chunks.insert_chunk(chunk);
206                    callback(cx, cz, Ok(chunk_arc));
207
208                    if dirty {
209                        self.request_chunk_save(cx, cz);
210                    }
211
212                },
213                Err((err, chunk_info)) => {
214                    // IDE shows an error for 'Display' not being implemented, but we use the
215                    // crate 'thiserror' to implement it through a custom derive.
216                    debug!("Failed to load chunk at {}/{}: {}", chunk_info.cx, chunk_info.cz, err);
217                    self.loading_chunks.remove(&(chunk_info.cx, chunk_info.cz));
218                    callback(chunk_info.cx, chunk_info.cz, Err(err));
219                }
220            }
221        }
222    }
223
224    /// Poll loaded chunks from internal level source, all successfully loaded chunks
225    /// are added to the underlying `LevelStorage`.
226    #[inline]
227    pub fn load_chunks(&mut self) {
228        self.load_chunks_with_callback(|_, _, _| {});
229    }
230
231    /// Returns the number of chunks being loaded or queued for loading (use `load_chunks` or
232    /// `load_chunks_with_callback` to load actually them.
233    #[inline]
234    pub fn get_loading_chunks_count(&self) -> usize {
235        self.loading_chunks.len()
236    }
237
238    // CHUNK SAVING (TO SOURCE) //
239
240    pub fn request_chunk_save(&mut self, cx: i32, cz: i32) -> bool {
241        if let Some(chunk) = self.chunks.get_chunk_arc(cx, cz) {
242            self.source.request_chunk_save(ChunkSaveRequest {
243                cx,
244                cz,
245                chunk
246            }).is_ok()
247        } else {
248            false
249        }
250    }
251
252    // ENTITIES //
253
254    pub fn spawn_entity(&mut self, entity_type: &'static EntityType, pos: EntityPos) -> Option<Entity> {
255
256        if !self.env.entities.has_entity_type(entity_type) {
257            return None;
258        }
259
260        let chunk = self.chunks.get_chunk_at_block_mut(BlockPos::from(&pos));
261        let entity = unsafe { self.entities.spawn_entity_unchecked(entity_type, pos) };
262
263        if let Some(mut chunk) = chunk {
264            unsafe {
265                chunk.add_entity_unchecked(entity);
266            }
267        }
268
269        Some(entity)
270
271    }
272
273}
274
275
276pub struct ChunkStorage {
277    /// Storing all cached chunks that were loaded from source.
278    chunks: HashMap<(i32, i32), Arc<RwLock<Chunk>>>,
279}
280
281impl ChunkStorage {
282
283    // CHUNKS //
284
285    pub fn get_chunks_count(&self) -> usize {
286        self.chunks.len()
287    }
288
289    /// Insert a chunk at a specific position.
290    pub fn insert_chunk(&mut self, chunk: Chunk) -> &Arc<RwLock<Chunk>> {
291        let pos = chunk.get_position();
292        let arc = Arc::new(RwLock::new(chunk));
293        match self.chunks.entry(pos) {
294            Entry::Occupied(mut o) => {
295                o.insert(arc);
296                o.into_mut()
297            },
298            Entry::Vacant(v) => {
299                v.insert(arc)
300            }
301        }
302    }
303
304    pub fn get_chunk_arc(&self, cx: i32, cz: i32) -> Option<Arc<RwLock<Chunk>>> {
305        self.chunks.get(&(cx, cz)).map(Arc::clone)
306    }
307
308    /// Return true if a chunk is loaded at a specific position.
309    pub fn is_chunk_loaded(&self, cx: i32, cz: i32) -> bool {
310        self.chunks.contains_key(&(cx, cz))
311    }
312
313    /// Get a chunk reference at specific coordinates.
314    pub fn get_chunk(&self, cx: i32, cz: i32) -> Option<RwLockReadGuard<Chunk>> {
315        self.chunks.get(&(cx, cz)).map(|arc| arc.read().unwrap())
316    }
317
318    /// Get a mutable chunk reference at specific coordinates.
319    pub fn get_chunk_mut(&self, cx: i32, cz: i32) -> Option<RwLockWriteGuard<Chunk>> {
320        self.chunks.get(&(cx, cz)).map(|arc| arc.write().unwrap())
321    }
322
323    /// Get a chunk reference at specific blocks coordinates.
324    #[inline]
325    pub fn get_chunk_at(&self, x: i32, z: i32) -> Option<RwLockReadGuard<Chunk>> {
326        self.get_chunk(x >> 4, z >> 4)
327    }
328
329    /// Get a mutable chunk reference at specific blocks coordinates.
330    #[inline]
331    pub fn get_chunk_at_mut(&self, x: i32, z: i32) -> Option<RwLockWriteGuard<Chunk>> {
332        self.get_chunk_mut(x >> 4, z >> 4)
333    }
334
335    #[inline]
336    pub fn get_chunk_at_block(&self, block_pos: BlockPos) -> Option<RwLockReadGuard<Chunk>> {
337        self.get_chunk_at(block_pos.x, block_pos.z)
338    }
339
340    #[inline]
341    pub fn get_chunk_at_block_mut(&self, block_pos: BlockPos) -> Option<RwLockWriteGuard<Chunk>> {
342        self.get_chunk_at_mut(block_pos.x, block_pos.z)
343    }
344
345    // BLOCKS //
346
347    pub fn set_block_at(&self, x: i32, y: i32, z: i32, block: &'static BlockState) -> ChunkResult<()> {
348        if let Some(mut chunk) = self.get_chunk_at_mut(x, z) {
349            chunk.set_block_at(x, y, z, block)
350        } else {
351            Err(ChunkError::ChunkUnloaded)
352        }
353    }
354
355    pub fn get_block_at(&self, x: i32, y: i32, z: i32) -> ChunkResult<&'static BlockState> {
356        if let Some(chunk) = self.get_chunk_at(x, z) {
357            chunk.get_block_at(x, y, z)
358        } else {
359            Err(ChunkError::ChunkUnloaded)
360        }
361    }
362
363}
364
365
366pub struct EntityStorage {
367    /// The ECS storing all entities in the level.
368    pub ecs: EcsWorld,
369    /// Internal entity builder kept
370    builder: EntityBuilder,
371}
372
373impl EntityStorage {
374
375    /// Spawn an entity in the level owning this storage, you must give its type and position,
376    /// its handle is returned. If the given entity type is not supported by the level's
377    /// environment, `None` is returned.
378    ///
379    /// # Safety:
380    /// This method is made for internal use because the entity type must be supported checked
381    /// to be supported by level's environment. The returned entity handle must also be added
382    /// in the the associated chunk if existing.
383    ///
384    /// # See:
385    /// Use `Level::spawn_entity` instead of this method if you want to avoid safety issues.
386    pub unsafe fn spawn_entity_unchecked(&mut self, entity_type: &'static EntityType, pos: EntityPos) -> Entity {
387
388        self.builder.add(BaseEntity::new(entity_type, Uuid::new_v4(), pos));
389
390        for &component in entity_type.codecs {
391            component.default(&mut self.builder);
392        }
393
394        self.ecs.spawn(self.builder.build())
395
396    }
397
398    /// Add a raw entity from a builder, this method is unsafe because the caller must ensure
399    /// that the builder contains a `BaseEntity` component with an entity
400    pub unsafe fn add_entity_unchecked(&mut self, entity_builder: &mut EntityBuilder) -> Entity {
401        self.ecs.spawn(entity_builder.build())
402    }
403
404    pub fn remove_entity(&mut self, entity: Entity) -> bool {
405        self.ecs.despawn(entity).is_ok()
406    }
407
408    pub fn get_entity_ref(&self, entity: Entity) -> Option<EntityRef> {
409        self.ecs.entity(entity).ok()
410    }
411
412}
413
414/// Base entity component, present in all entities of a level, must not be removed.
415pub struct BaseEntity {
416    pub entity_type: &'static EntityType,
417    pub uuid: Uuid,
418    pub pos: EntityPos,
419    /// An optional list of entities that are on top of this one.
420    passengers: Option<Vec<Entity>>
421}
422
423impl BaseEntity {
424
425    pub fn new(entity_type: &'static EntityType, uuid: Uuid, pos: EntityPos) -> Self {
426        Self {
427            entity_type,
428            uuid,
429            pos,
430            passengers: None
431        }
432    }
433
434}