azalea_world/
world.rs

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