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}