valence_server/layer/chunk/
unloaded.rs
1use std::cmp::Ordering;
2use std::collections::BTreeMap;
3
4use valence_nbt::Compound;
5use valence_protocol::BlockState;
6use valence_registry::biome::BiomeId;
7
8use super::chunk::{
9 check_biome_oob, check_block_oob, check_section_oob, BiomeContainer, BlockStateContainer,
10 Chunk, MAX_HEIGHT, SECTION_BLOCK_COUNT,
11};
12
13#[derive(Clone, Default, Debug)]
14pub struct UnloadedChunk {
15 pub(super) sections: Vec<Section>,
16 pub(super) block_entities: BTreeMap<u32, Compound>,
17}
18
19#[derive(Clone, Default, Debug)]
20pub(super) struct Section {
21 pub(super) block_states: BlockStateContainer,
22 pub(super) biomes: BiomeContainer,
23}
24
25impl UnloadedChunk {
26 pub fn new() -> Self {
27 Self::default()
28 }
29
30 pub fn with_height(height: u32) -> Self {
31 Self {
32 sections: vec![Section::default(); height as usize / 16],
33 block_entities: BTreeMap::new(),
34 }
35 }
36
37 pub fn set_height(&mut self, height: u32) {
45 let new_count = height.min(MAX_HEIGHT) as usize / 16;
46 let old_count = self.sections.len();
47
48 match new_count.cmp(&old_count) {
49 Ordering::Less => {
50 self.sections.truncate(new_count);
51 self.sections.shrink_to_fit();
52
53 let cutoff = SECTION_BLOCK_COUNT as u32 * new_count as u32;
54 self.block_entities.retain(|idx, _| *idx < cutoff);
55 }
56 Ordering::Equal => {}
57 Ordering::Greater => {
58 let diff = new_count - old_count;
59 self.sections.reserve_exact(diff);
60 self.sections.extend((0..diff).map(|_| Section::default()));
61 }
62 }
63 }
64}
65
66impl Chunk for UnloadedChunk {
67 fn height(&self) -> u32 {
68 self.sections.len() as u32 * 16
69 }
70
71 fn block_state(&self, x: u32, y: u32, z: u32) -> BlockState {
72 check_block_oob(self, x, y, z);
73
74 let idx = x + z * 16 + y % 16 * 16 * 16;
75 self.sections[y as usize / 16]
76 .block_states
77 .get(idx as usize)
78 }
79
80 fn set_block_state(&mut self, x: u32, y: u32, z: u32, block: BlockState) -> BlockState {
81 check_block_oob(self, x, y, z);
82
83 let idx = x + z * 16 + y % 16 * 16 * 16;
84 self.sections[y as usize / 16]
85 .block_states
86 .set(idx as usize, block)
87 }
88
89 fn fill_block_state_section(&mut self, sect_y: u32, block: BlockState) {
90 check_section_oob(self, sect_y);
91
92 self.sections[sect_y as usize].block_states.fill(block);
93 }
94
95 fn block_entity(&self, x: u32, y: u32, z: u32) -> Option<&Compound> {
96 check_block_oob(self, x, y, z);
97
98 let idx = x + z * 16 + y * 16 * 16;
99 self.block_entities.get(&idx)
100 }
101
102 fn block_entity_mut(&mut self, x: u32, y: u32, z: u32) -> Option<&mut Compound> {
103 check_block_oob(self, x, y, z);
104
105 let idx = x + z * 16 + y * 16 * 16;
106 self.block_entities.get_mut(&idx)
107 }
108
109 fn set_block_entity(
110 &mut self,
111 x: u32,
112 y: u32,
113 z: u32,
114 block_entity: Option<Compound>,
115 ) -> Option<Compound> {
116 check_block_oob(self, x, y, z);
117
118 let idx = x + z * 16 + y * 16 * 16;
119
120 match block_entity {
121 Some(be) => self.block_entities.insert(idx, be),
122 None => self.block_entities.remove(&idx),
123 }
124 }
125
126 fn clear_block_entities(&mut self) {
127 self.block_entities.clear();
128 }
129
130 fn biome(&self, x: u32, y: u32, z: u32) -> BiomeId {
131 check_biome_oob(self, x, y, z);
132
133 let idx = x + z * 4 + y % 4 * 4 * 4;
134 self.sections[y as usize / 4].biomes.get(idx as usize)
135 }
136
137 fn set_biome(&mut self, x: u32, y: u32, z: u32, biome: BiomeId) -> BiomeId {
138 check_biome_oob(self, x, y, z);
139
140 let idx = x + z * 4 + y % 4 * 4 * 4;
141 self.sections[y as usize / 4]
142 .biomes
143 .set(idx as usize, biome)
144 }
145
146 fn fill_biome_section(&mut self, sect_y: u32, biome: BiomeId) {
147 check_section_oob(self, sect_y);
148
149 self.sections[sect_y as usize].biomes.fill(biome);
150 }
151
152 fn shrink_to_fit(&mut self) {
153 for sect in &mut self.sections {
154 sect.block_states.shrink_to_fit();
155 sect.biomes.shrink_to_fit();
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn unloaded_chunk_resize_removes_block_entities() {
166 let mut chunk = UnloadedChunk::with_height(32);
167
168 assert_eq!(chunk.height(), 32);
169
170 chunk.set_block_entity(0, 5, 0, Some(Compound::new()));
172
173 chunk.set_block_entity(0, 16, 0, Some(Compound::new()));
175
176 chunk.set_height(16);
178 assert_eq!(chunk.height(), 16);
179
180 assert_eq!(chunk.block_entity(0, 5, 0), Some(&Compound::new()));
181 assert_eq!(chunk.set_block_entity(0, 5, 0, None), Some(Compound::new()));
182 assert!(chunk.block_entities.is_empty());
183 }
184}