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/// PartialInstances are usually owned by clients, and hold strong references to
18/// chunks and entities in [`Instance`]s.
19///
20/// Basically, they hold the chunks and entities that are within render
21/// distance but can still access chunks and entities owned by other
22/// `PartialInstance`s that have the same `Instance`.
23///
24/// This is primarily useful for having multiple clients in the same Instance.
25pub struct PartialInstance {
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 PartialInstance {
33    pub fn new(chunk_radius: u32, owner_entity: Option<Entity>) -> Self {
34        PartialInstance {
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 PartialInstance 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 world where the chunks are stored as weak pointers. This is used for
79/// shared worlds.
80///
81/// Also see [`PartialInstance`].
82///
83/// This is sometimes interchangeably called a "world". However, this type is
84/// called `Instance` to avoid colliding with the `World` type from Bevy ECS.
85#[derive(Debug, Default)]
86#[doc(alias("world", "dimension"))]
87pub struct Instance {
88    pub chunks: ChunkStorage,
89
90    /// An index of all the entities we know are in the chunks of the world
91    pub entities_by_chunk: HashMap<ChunkPos, HashSet<Entity>>,
92
93    /// An index of Minecraft entity IDs to Azalea ECS entities.
94    ///
95    /// You should avoid using this (particularly if you're using swarms) and
96    /// instead use `azalea_entity::EntityIdIndex`, since some servers may
97    /// give different entity IDs for the same entities to different
98    /// players.
99    pub entity_by_id: IntMap<azalea_core::entity_id::MinecraftEntityId, Entity>,
100
101    pub registries: RegistryHolder,
102}
103
104impl Instance {
105    /// Get the block at the given position, or `None` if it's outside of the
106    /// world that we have loaded.
107    pub fn get_block_state(&self, pos: BlockPos) -> Option<BlockState> {
108        self.chunks.get_block_state(pos)
109    }
110
111    /// Similar to [`Self::get_block_state`], but returns data about the fluid
112    /// at the position, including for waterlogged blocks.
113    pub fn get_fluid_state(&self, pos: BlockPos) -> Option<FluidState> {
114        self.chunks.get_block_state(pos).map(FluidState::from)
115    }
116
117    /// Get the biome at the given position.
118    ///
119    /// You can then use `Client::with_resolved_registry` to get the name and
120    /// data from the biome.
121    ///
122    /// Note that biomes are internally stored as 4x4x4 blocks, so if you're
123    /// writing code that searches for a specific biome it'll probably be more
124    /// efficient to avoid scanning every single block.
125    pub fn get_biome(&self, pos: BlockPos) -> Option<Biome> {
126        self.chunks.get_biome(pos)
127    }
128
129    pub fn set_block_state(&self, pos: BlockPos, state: BlockState) -> Option<BlockState> {
130        self.chunks.set_block_state(pos, state)
131    }
132}
133
134impl Debug for PartialInstance {
135    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136        f.debug_struct("PartialInstance")
137            .field("chunks", &self.chunks)
138            .field("entity_infos", &self.entity_infos)
139            .finish()
140    }
141}
142
143impl Default for PartialInstance {
144    /// Creates a completely self-contained `PartialInstance`. This is only for
145    /// testing and shouldn't be used in actual code!
146    fn default() -> Self {
147        let chunk_storage = PartialChunkStorage::default();
148        let entity_storage = PartialEntityInfos::default();
149        Self {
150            chunks: chunk_storage,
151            entity_infos: entity_storage,
152        }
153    }
154}
155
156impl From<ChunkStorage> for Instance {
157    /// Make an empty world from this `ChunkStorage`. This is meant to be a
158    /// convenience function for tests.
159    fn from(chunks: ChunkStorage) -> Self {
160        Self {
161            chunks,
162            entities_by_chunk: HashMap::new(),
163            entity_by_id: IntMap::default(),
164            registries: RegistryHolder::default(),
165        }
166    }
167}