azalea_world/
world.rs

1use std::{
2    collections::{HashMap, HashSet},
3    fmt::{self, Debug, Display},
4    hash::{Hash, Hasher},
5    io::{self, Cursor},
6};
7
8use azalea_block::{BlockState, fluid_state::FluidState};
9use azalea_buf::{AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
10use azalea_core::{
11    position::{BlockPos, ChunkPos},
12    registry_holder::RegistryHolder,
13};
14use azalea_registry::data::Biome;
15use bevy_ecs::{component::Component, entity::Entity};
16use derive_more::{Deref, DerefMut};
17use nohash_hasher::IntMap;
18#[cfg(feature = "serde")]
19use serde::{Deserialize, Serialize};
20
21use crate::{ChunkStorage, PartialChunkStorage};
22
23/// PartialInstances are usually owned by clients, and hold strong references to
24/// chunks and entities in [`Instance`]s.
25///
26/// Basically, they hold the chunks and entities that are within render
27/// distance but can still access chunks and entities owned by other
28/// `PartialInstance`s that have the same `Instance`.
29///
30/// This is primarily useful for having multiple clients in the same Instance.
31pub struct PartialInstance {
32    pub chunks: PartialChunkStorage,
33    /// Some metadata about entities, like what entities are in certain chunks.
34    /// This does not contain the entity data itself, that's in the ECS.
35    pub entity_infos: PartialEntityInfos,
36}
37
38impl PartialInstance {
39    pub fn new(chunk_radius: u32, owner_entity: Option<Entity>) -> Self {
40        PartialInstance {
41            chunks: PartialChunkStorage::new(chunk_radius),
42            entity_infos: PartialEntityInfos::new(owner_entity),
43        }
44    }
45
46    /// Clears the internal references to chunks in the PartialInstance and
47    /// resets the view center.
48    pub fn reset(&mut self) {
49        self.chunks = PartialChunkStorage::new(self.chunks.chunk_radius);
50    }
51}
52
53/// An entity ID used by Minecraft.
54///
55/// These IDs are picked by the server. Some server software (like Bungeecord)
56/// may pick entity IDs per-player, so you should avoid relying on them for
57/// identifying IDs (especially if you're using a shared world -- i.e. a swarm).
58///
59/// You might find [`Entity`] more useful, since that's an ID decided by us that
60/// is likely to be correct across shared worlds. You could also use the
61/// `EntityUuid` from `azalea_entity`, that one is unlikely to change even
62/// across server restarts.
63///
64/// This serializes as a i32. Usually it's a VarInt in the protocol, but not
65/// always. If you do need it to serialize as a VarInt, make sure to use use the
66/// `#[var]` attribute.
67///
68/// [`Entity`]: bevy_ecs::entity::Entity
69#[derive(Clone, Component, Copy, Debug, Default, Deref, DerefMut, Eq, PartialEq)]
70#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
71pub struct MinecraftEntityId(pub i32);
72
73impl Hash for MinecraftEntityId {
74    fn hash<H: Hasher>(&self, hasher: &mut H) {
75        hasher.write_i32(self.0);
76    }
77}
78impl nohash_hasher::IsEnabled for MinecraftEntityId {}
79
80// we can't have the default be #[var] because mojang doesn't use varints for
81// entities sometimes :(
82impl AzaleaRead for MinecraftEntityId {
83    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
84        i32::azalea_read(buf).map(MinecraftEntityId)
85    }
86}
87impl AzaleaWrite for MinecraftEntityId {
88    fn azalea_write(&self, buf: &mut impl io::Write) -> io::Result<()> {
89        i32::azalea_write(&self.0, buf)
90    }
91}
92impl AzaleaReadVar for MinecraftEntityId {
93    fn azalea_read_var(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
94        i32::azalea_read_var(buf).map(MinecraftEntityId)
95    }
96}
97impl AzaleaWriteVar for MinecraftEntityId {
98    fn azalea_write_var(&self, buf: &mut impl io::Write) -> io::Result<()> {
99        i32::azalea_write_var(&self.0, buf)
100    }
101}
102impl Display for MinecraftEntityId {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        write!(f, "eid({})", self.0)
105    }
106}
107impl From<i32> for MinecraftEntityId {
108    fn from(id: i32) -> Self {
109        Self(id)
110    }
111}
112impl From<u32> for MinecraftEntityId {
113    fn from(id: u32) -> Self {
114        Self(id as i32)
115    }
116}
117
118/// Keep track of certain metadatas that are only relevant for this partial
119/// world.
120#[derive(Debug, Default)]
121pub struct PartialEntityInfos {
122    /// The entity ID of the player that owns this partial world.
123    ///
124    /// This will make `RelativeEntityUpdate` pretend this entity doesn't exist
125    /// so it doesn't get modified from outside sources.
126    pub owner_entity: Option<Entity>,
127    /// A counter for each entity that tracks how many updates we've observed
128    /// for it.
129    ///
130    /// This is used for shared worlds (i.e. swarms), to make sure we don't
131    /// update entities twice on accident.
132    pub updates_received: IntMap<MinecraftEntityId, u32>,
133    // ^ note: using MinecraftEntityId for entity ids is acceptable here since
134    // there's no chance of collisions
135}
136
137impl PartialEntityInfos {
138    pub fn new(owner_entity: Option<Entity>) -> Self {
139        Self {
140            owner_entity,
141            updates_received: IntMap::default(),
142        }
143    }
144}
145
146/// A world where the chunks are stored as weak pointers. This is used for
147/// shared worlds.
148///
149/// Also see [`PartialInstance`].
150///
151/// This is sometimes interchangeably called a "world". However, this type is
152/// called `Instance` to avoid colliding with the `World` type from Bevy ECS.
153#[derive(Debug, Default)]
154#[doc(alias("world", "dimension"))]
155pub struct Instance {
156    pub chunks: ChunkStorage,
157
158    /// An index of all the entities we know are in the chunks of the world
159    pub entities_by_chunk: HashMap<ChunkPos, HashSet<Entity>>,
160
161    /// An index of Minecraft entity IDs to Azalea ECS entities.
162    ///
163    /// You should avoid using this (particularly if you're using swarms) and
164    /// instead use `azalea_entity::EntityIdIndex`, since some servers may
165    /// give different entity IDs for the same entities to different
166    /// players.
167    pub entity_by_id: IntMap<MinecraftEntityId, Entity>,
168
169    pub registries: RegistryHolder,
170}
171
172impl Instance {
173    /// Get the block at the given position, or `None` if it's outside of the
174    /// world that we have loaded.
175    pub fn get_block_state(&self, pos: BlockPos) -> Option<BlockState> {
176        self.chunks.get_block_state(pos)
177    }
178
179    /// Similar to [`Self::get_block_state`], but returns data about the fluid
180    /// at the position, including for waterlogged blocks.
181    pub fn get_fluid_state(&self, pos: BlockPos) -> Option<FluidState> {
182        self.chunks.get_block_state(pos).map(FluidState::from)
183    }
184
185    /// Get the biome at the given position.
186    ///
187    /// You can then use `Client::with_resolved_registry` to get the name and
188    /// data from the biome.
189    ///
190    /// Note that biomes are internally stored as 4x4x4 blocks, so if you're
191    /// writing code that searches for a specific biome it'll probably be more
192    /// efficient to avoid scanning every single block.
193    pub fn get_biome(&self, pos: BlockPos) -> Option<Biome> {
194        self.chunks.get_biome(pos)
195    }
196
197    pub fn set_block_state(&self, pos: BlockPos, state: BlockState) -> Option<BlockState> {
198        self.chunks.set_block_state(pos, state)
199    }
200}
201
202impl Debug for PartialInstance {
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        f.debug_struct("PartialInstance")
205            .field("chunks", &self.chunks)
206            .field("entity_infos", &self.entity_infos)
207            .finish()
208    }
209}
210
211impl Default for PartialInstance {
212    /// Creates a completely self-contained `PartialInstance`. This is only for
213    /// testing and shouldn't be used in actual code!
214    fn default() -> Self {
215        let chunk_storage = PartialChunkStorage::default();
216        let entity_storage = PartialEntityInfos::default();
217        Self {
218            chunks: chunk_storage,
219            entity_infos: entity_storage,
220        }
221    }
222}
223
224impl From<ChunkStorage> for Instance {
225    /// Make an empty world from this `ChunkStorage`. This is meant to be a
226    /// convenience function for tests.
227    fn from(chunks: ChunkStorage) -> Self {
228        Self {
229            chunks,
230            entities_by_chunk: HashMap::new(),
231            entity_by_id: IntMap::default(),
232            registries: RegistryHolder::default(),
233        }
234    }
235}