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}