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::BlockState;
10use azalea_block::fluid_state::FluidState;
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}
100impl From<i32> for MinecraftEntityId {
101    fn from(id: i32) -> Self {
102        Self(id)
103    }
104}
105impl From<u32> for MinecraftEntityId {
106    fn from(id: u32) -> Self {
107        Self(id as i32)
108    }
109}
110
111/// Keep track of certain metadatas that are only relevant for this partial
112/// world.
113#[derive(Debug, Default)]
114pub struct PartialEntityInfos {
115    // note: using MinecraftEntityId for entity ids is acceptable here since
116    // there's no chance of collisions here
117    /// The entity id of the player that owns this partial world. This will
118    /// make `RelativeEntityUpdate` pretend this entity doesn't exist so
119    /// it doesn't get modified from outside sources.
120    pub owner_entity: Option<Entity>,
121    /// A counter for each entity that tracks how many updates we've observed
122    /// for it.
123    ///
124    /// This is used for shared worlds (i.e. swarms), to make sure we don't
125    /// update entities twice on accident.
126    pub updates_received: IntMap<MinecraftEntityId, u32>,
127}
128
129impl PartialEntityInfos {
130    pub fn new(owner_entity: Option<Entity>) -> Self {
131        Self {
132            owner_entity,
133            updates_received: IntMap::default(),
134        }
135    }
136}
137
138/// A world where the chunks are stored as weak pointers. This is used for
139/// shared worlds.
140#[derive(Default, Debug)]
141pub struct Instance {
142    pub chunks: ChunkStorage,
143
144    /// An index of all the entities we know are in the chunks of the world
145    pub entities_by_chunk: HashMap<ChunkPos, HashSet<Entity>>,
146
147    /// An index of Minecraft entity IDs to Azalea ECS entities.
148    ///
149    /// You should avoid using this (particularly if you're using swarms) and
150    /// instead use `azalea_entity::EntityIdIndex`, since some servers may
151    /// give different entity IDs for the same entities to different
152    /// players.
153    pub entity_by_id: IntMap<MinecraftEntityId, Entity>,
154
155    pub registries: RegistryHolder,
156}
157
158impl Instance {
159    pub fn get_block_state(&self, pos: &BlockPos) -> Option<BlockState> {
160        self.chunks.get_block_state(pos)
161    }
162
163    pub fn get_fluid_state(&self, pos: &BlockPos) -> Option<FluidState> {
164        self.chunks.get_block_state(pos).map(FluidState::from)
165    }
166
167    pub fn set_block_state(&self, pos: &BlockPos, state: BlockState) -> Option<BlockState> {
168        self.chunks.set_block_state(pos, state)
169    }
170}
171
172impl Debug for PartialInstance {
173    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
174        f.debug_struct("PartialInstance")
175            .field("chunks", &self.chunks)
176            .field("entity_infos", &self.entity_infos)
177            .finish()
178    }
179}
180
181impl Default for PartialInstance {
182    /// Creates a completely self-contained `PartialInstance`. This is only for
183    /// testing and shouldn't be used in actual code!
184    fn default() -> Self {
185        let chunk_storage = PartialChunkStorage::default();
186        let entity_storage = PartialEntityInfos::default();
187        Self {
188            chunks: chunk_storage,
189            entity_infos: entity_storage,
190        }
191    }
192}
193
194impl From<ChunkStorage> for Instance {
195    /// Make an empty world from this `ChunkStorage`. This is meant to be a
196    /// convenience function for tests.
197    fn from(chunks: ChunkStorage) -> Self {
198        Self {
199            chunks,
200            entities_by_chunk: HashMap::new(),
201            entity_by_id: IntMap::default(),
202            registries: RegistryHolder::default(),
203        }
204    }
205}