Skip to main content

azalea_world/
world.rs

1use std::{
2    collections::{HashMap, HashSet},
3    fmt::{self, Debug},
4};
5
6use azalea_block::{BlockState, fluid_state::FluidState};
7use azalea_core::{
8    position::{BlockPos, ChunkPos},
9    registry_holder::RegistryHolder,
10};
11use azalea_registry::data::Biome;
12use bevy_ecs::entity::Entity;
13use nohash_hasher::IntMap;
14
15use crate::{ChunkStorage, PartialChunkStorage};
16
17/// A reference to a slice of the world, as seen by an individual client.
18///
19/// A `PartialWorld` will typically be owned by a client, and it holds strong
20/// references to chunks and entities in a [`World`]s.
21///
22/// In other words, this type holds the chunks and entities that are within
23/// render distance for a client. The same chunk/entity may be referenced by
24/// more than one `PartialWorld`.
25pub struct PartialWorld {
26    pub chunks: PartialChunkStorage,
27    /// Some metadata about entities, like what entities are in certain chunks.
28    /// This does not contain the entity data itself, that's in the ECS.
29    pub entity_infos: PartialEntityInfos,
30}
31
32impl PartialWorld {
33    pub fn new(chunk_radius: u32, owner_entity: Option<Entity>) -> Self {
34        PartialWorld {
35            chunks: PartialChunkStorage::new(chunk_radius),
36            entity_infos: PartialEntityInfos::new(owner_entity),
37        }
38    }
39
40    /// Clears the internal references to chunks in the [`PartialWorld`] and
41    /// resets the view center.
42    pub fn reset(&mut self) {
43        self.chunks = PartialChunkStorage::new(self.chunks.chunk_radius);
44    }
45}
46
47#[deprecated = "moved to `azalea_core::entity_id::MinecraftEntityId`."]
48pub type MinecraftEntityId = azalea_core::entity_id::MinecraftEntityId;
49
50/// Keep track of certain metadatas that are only relevant for this partial
51/// world.
52#[derive(Debug, Default)]
53pub struct PartialEntityInfos {
54    /// The entity ID of the player that owns this partial world.
55    ///
56    /// This will make `RelativeEntityUpdate` pretend this entity doesn't exist
57    /// so it doesn't get modified from outside sources.
58    pub owner_entity: Option<Entity>,
59    /// A counter for each entity that tracks how many updates we've observed
60    /// for it.
61    ///
62    /// This is used for shared worlds (i.e. swarms), to make sure we don't
63    /// update entities twice on accident.
64    pub updates_received: IntMap<azalea_core::entity_id::MinecraftEntityId, u32>,
65    // ^ note: using MinecraftEntityId for entity ids is acceptable here since
66    // there's no chance of collisions
67}
68
69impl PartialEntityInfos {
70    pub fn new(owner_entity: Option<Entity>) -> Self {
71        Self {
72            owner_entity,
73            updates_received: IntMap::default(),
74        }
75    }
76}
77
78/// A Minecraft world, sometimes referred to as a dimension.
79///
80/// This is the most commonly used type to interact with the world that a client
81/// is in. In here, all chunks are stored as weak pointers, which means that
82/// clients in different areas of the same world can share the same `World`
83/// instance.
84///
85/// Also see [`PartialWorld`].
86///
87/// Note that this is distinct from Bevy's `World` type, which contains all of
88/// the data for every client. When referring to the Bevy `World` within Azalea,
89/// the term "ECS" is generally used instead.
90#[derive(Debug, Default)]
91#[doc(alias("instance", "dimension", "level"))]
92pub struct World {
93    pub chunks: ChunkStorage,
94
95    /// An index of all the entities we know are in the chunks of the world
96    pub entities_by_chunk: HashMap<ChunkPos, HashSet<Entity>>,
97
98    /// An index of Minecraft entity IDs to Azalea ECS entities.
99    ///
100    /// You should avoid using this (particularly if you're using swarms) and
101    /// instead use `azalea_entity::EntityIdIndex`, since some servers may
102    /// give different entity IDs for the same entities to different
103    /// players.
104    pub entity_by_id: IntMap<azalea_core::entity_id::MinecraftEntityId, Entity>,
105
106    pub registries: RegistryHolder,
107}
108
109#[deprecated = "renamed to `World`."]
110pub type Instance = World;
111
112impl World {
113    /// Get the block at the given position, or `None` if it's outside of the
114    /// world that we have loaded.
115    pub fn get_block_state(&self, pos: BlockPos) -> Option<BlockState> {
116        self.chunks.get_block_state(pos)
117    }
118
119    /// Similar to [`Self::get_block_state`], but returns data about the fluid
120    /// at the position, including for waterlogged blocks.
121    pub fn get_fluid_state(&self, pos: BlockPos) -> Option<FluidState> {
122        self.chunks.get_block_state(pos).map(FluidState::from)
123    }
124
125    /// Get the biome at the given position.
126    ///
127    /// You can then use `Client::with_resolved_registry` to get the name and
128    /// data from the biome.
129    ///
130    /// Note that biomes are internally stored as 4x4x4 blocks, so if you're
131    /// writing code that searches for a specific biome it'll probably be more
132    /// efficient to avoid scanning every single block.
133    pub fn get_biome(&self, pos: BlockPos) -> Option<Biome> {
134        self.chunks.get_biome(pos)
135    }
136
137    pub fn set_block_state(&self, pos: BlockPos, state: BlockState) -> Option<BlockState> {
138        self.chunks.set_block_state(pos, state)
139    }
140}
141
142impl Debug for PartialWorld {
143    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144        f.debug_struct("PartialWorld")
145            .field("chunks", &self.chunks)
146            .field("entity_infos", &self.entity_infos)
147            .finish()
148    }
149}
150
151impl Default for PartialWorld {
152    /// Creates a completely self-contained [`PartialWorld`]. This is only for
153    /// testing and shouldn't be used in actual code!
154    fn default() -> Self {
155        let chunk_storage = PartialChunkStorage::default();
156        let entity_storage = PartialEntityInfos::default();
157        Self {
158            chunks: chunk_storage,
159            entity_infos: entity_storage,
160        }
161    }
162}
163
164impl From<ChunkStorage> for World {
165    /// Make an empty world from this `ChunkStorage`. This is meant to be a
166    /// convenience function for tests.
167    fn from(chunks: ChunkStorage) -> Self {
168        Self {
169            chunks,
170            entities_by_chunk: HashMap::new(),
171            entity_by_id: IntMap::default(),
172            registries: RegistryHolder::default(),
173        }
174    }
175}