Skip to main content

azalea_world/
container.rs

1use std::{
2    collections::HashMap,
3    fmt::{self, Display},
4    sync::{Arc, Weak},
5};
6
7use azalea_core::registry_holder::RegistryHolder;
8use azalea_registry::identifier::Identifier;
9use bevy_ecs::{component::Component, resource::Resource};
10use derive_more::{Deref, DerefMut};
11use nohash_hasher::IntMap;
12use parking_lot::RwLock;
13use rustc_hash::FxHashMap;
14use tracing::{debug, error};
15
16use crate::{ChunkStorage, World};
17
18/// A container of [`World`] instances.
19///
20/// Worlds are stored as a `Weak` pointer here, so if no clients are using a
21/// world then it will be forgotten.
22#[derive(Default, Resource)]
23pub struct Worlds {
24    // We just refer to the chunks here and don't include entities because there's not that many
25    // cases where we'd want to get every entity in the world (just getting the entities in chunks
26    // should work fine).
27
28    // Entities are garbage collected (by manual reference counting in EntityUuidIndex) so we don't
29    // need to worry about them here.
30
31    // If it looks like we're relying on the server giving us unique world names, that's because we
32    // are. An evil server could give us two worlds with the same name and then we'd have no way of
33    // telling them apart. We hope most servers are nice and don't do that. Perhaps this should be
34    // changed in the future to be configurable.
35    pub map: FxHashMap<WorldName, Weak<RwLock<World>>>,
36}
37
38impl Worlds {
39    pub fn new() -> Self {
40        Worlds::default()
41    }
42
43    /// Get a world instance from the container.
44    ///
45    /// Returns `None` if none of the clients are in the requested world.
46    pub fn get(&self, name: &WorldName) -> Option<Arc<RwLock<World>>> {
47        self.map.get(name).and_then(|world| world.upgrade())
48    }
49
50    /// Add an empty world to the container (unless it already exists) and
51    /// returns a strong reference to the world.
52    #[must_use = "the world will be immediately forgotten if unused"]
53    pub fn get_or_insert(
54        &mut self,
55        name: WorldName,
56        height: u32,
57        min_y: i32,
58        default_registries: &RegistryHolder,
59    ) -> Arc<RwLock<World>> {
60        match self.map.get(&name).and_then(|world| world.upgrade()) {
61            Some(existing_lock) => {
62                let existing = existing_lock.read();
63                if existing.chunks.height != height {
64                    error!(
65                        "Shared world height mismatch: {} != {height}",
66                        existing.chunks.height
67                    );
68                }
69                if existing.chunks.min_y != min_y {
70                    error!(
71                        "Shared world min_y mismatch: {} != {min_y}",
72                        existing.chunks.min_y
73                    );
74                }
75                existing_lock.clone()
76            }
77            _ => {
78                let world = Arc::new(RwLock::new(World {
79                    chunks: ChunkStorage::new(height, min_y),
80                    entities_by_chunk: HashMap::new(),
81                    entity_by_id: IntMap::default(),
82                    registries: default_registries.clone(),
83                }));
84                debug!("Added new world {name:?}");
85                self.map.insert(name, Arc::downgrade(&world));
86                world
87            }
88        }
89    }
90}
91
92/// The name of the [`World`] (aka dimension) that an entity is in.
93///
94/// If two entities share the same world name, then Azalea assumes that they're
95/// in the same world.
96#[derive(Clone, Component, Debug, Deref, DerefMut, Eq, Hash, PartialEq)]
97#[doc(alias("dimension"))]
98pub struct WorldName(pub Identifier);
99impl WorldName {
100    /// Create a new `WorldName` with the given name.
101    pub fn new(name: &str) -> Self {
102        Self(Identifier::new(name))
103    }
104}
105impl Display for WorldName {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        self.0.fmt(f)
108    }
109}
110impl From<Identifier> for WorldName {
111    fn from(ident: Identifier) -> Self {
112        Self(ident)
113    }
114}