valence_server/layer/chunk/
chunk.rs1use valence_nbt::Compound;
2use valence_protocol::BlockState;
3use valence_registry::biome::BiomeId;
4
5use super::paletted_container::PalettedContainer;
6
7pub trait Chunk {
11 fn height(&self) -> u32;
13
14 #[track_caller]
21 fn block(&self, x: u32, y: u32, z: u32) -> BlockRef {
22 BlockRef {
23 state: self.block_state(x, y, z),
24 nbt: self.block_entity(x, y, z),
25 }
26 }
27
28 #[track_caller]
36 fn set_block(&mut self, x: u32, y: u32, z: u32, block: impl IntoBlock) -> Block {
37 let block = block.into_block();
38 let state = self.set_block_state(x, y, z, block.state);
39 let nbt = self.set_block_entity(x, y, z, block.nbt);
40
41 Block { state, nbt }
42 }
43
44 fn fill_blocks(&mut self, block: impl IntoBlock) {
46 let block = block.into_block();
47
48 self.fill_block_states(block.state);
49
50 if block.nbt.is_some() {
51 for x in 0..16 {
52 for z in 0..16 {
53 for y in 0..self.height() {
54 self.set_block_entity(x, y, z, block.nbt.clone());
55 }
56 }
57 }
58 } else {
59 self.clear_block_entities();
60 }
61 }
62
63 #[track_caller]
70 fn block_state(&self, x: u32, y: u32, z: u32) -> BlockState;
71
72 #[track_caller]
84 fn set_block_state(&mut self, x: u32, y: u32, z: u32, block: BlockState) -> BlockState;
85
86 fn fill_block_states(&mut self, block: BlockState) {
92 for sect_y in 0..self.height() / 16 {
93 self.fill_block_state_section(sect_y, block);
94 }
95 }
96
97 #[track_caller]
108 fn fill_block_state_section(&mut self, sect_y: u32, block: BlockState);
109
110 #[track_caller]
117 fn block_entity(&self, x: u32, y: u32, z: u32) -> Option<&Compound>;
118
119 #[track_caller]
127 fn block_entity_mut(&mut self, x: u32, y: u32, z: u32) -> Option<&mut Compound>;
128
129 #[track_caller]
141 fn set_block_entity(
142 &mut self,
143 x: u32,
144 y: u32,
145 z: u32,
146 block_entity: Option<Compound>,
147 ) -> Option<Compound>;
148
149 fn clear_block_entities(&mut self);
155
156 #[track_caller]
167 fn biome(&self, x: u32, y: u32, z: u32) -> BiomeId;
168
169 #[track_caller]
181 fn set_biome(&mut self, x: u32, y: u32, z: u32, biome: BiomeId) -> BiomeId;
182
183 fn fill_biomes(&mut self, biome: BiomeId) {
185 for sect_y in 0..self.height() / 16 {
186 self.fill_biome_section(sect_y, biome);
187 }
188 }
189
190 #[track_caller]
196 fn fill_biome_section(&mut self, sect_y: u32, biome: BiomeId);
197
198 fn clear(&mut self) {
201 self.fill_block_states(BlockState::AIR);
202 self.fill_biomes(BiomeId::default());
203 self.clear_block_entities();
204 }
205
206 fn shrink_to_fit(&mut self);
212}
213
214#[derive(Clone, PartialEq, Default, Debug)]
217pub struct Block {
218 pub state: BlockState,
219 pub nbt: Option<Compound>,
220}
221
222impl Block {
223 pub const fn new(state: BlockState, nbt: Option<Compound>) -> Self {
224 Self { state, nbt }
225 }
226}
227
228#[derive(Copy, Clone, PartialEq, Default, Debug)]
230pub struct BlockRef<'a> {
231 pub state: BlockState,
232 pub nbt: Option<&'a Compound>,
233}
234
235impl<'a> BlockRef<'a> {
236 pub const fn new(state: BlockState, nbt: Option<&'a Compound>) -> Self {
237 Self { state, nbt }
238 }
239}
240
241pub trait IntoBlock {
242 fn into_block(self) -> Block;
244}
245
246impl IntoBlock for Block {
247 fn into_block(self) -> Block {
248 self
249 }
250}
251
252impl IntoBlock for BlockRef<'_> {
253 fn into_block(self) -> Block {
254 Block {
255 state: self.state,
256 nbt: self.nbt.cloned(),
257 }
258 }
259}
260
261impl IntoBlock for BlockState {
264 fn into_block(self) -> Block {
265 Block {
266 state: self,
267 nbt: self.block_entity_kind().map(|_| Compound::new()),
268 }
269 }
270}
271
272pub(super) const SECTION_BLOCK_COUNT: usize = 16 * 16 * 16;
273pub(super) const SECTION_BIOME_COUNT: usize = 4 * 4 * 4;
274
275pub const MAX_HEIGHT: u32 = 4096;
277
278pub(super) type BlockStateContainer =
279 PalettedContainer<BlockState, SECTION_BLOCK_COUNT, { SECTION_BLOCK_COUNT / 2 }>;
280
281pub(super) type BiomeContainer =
282 PalettedContainer<BiomeId, SECTION_BIOME_COUNT, { SECTION_BIOME_COUNT / 2 }>;
283
284#[inline]
285#[track_caller]
286pub(super) fn check_block_oob(chunk: &impl Chunk, x: u32, y: u32, z: u32) {
287 assert!(
288 x < 16 && y < chunk.height() && z < 16,
289 "chunk block offsets of ({x}, {y}, {z}) are out of bounds"
290 );
291}
292
293#[inline]
294#[track_caller]
295pub(super) fn check_biome_oob(chunk: &impl Chunk, x: u32, y: u32, z: u32) {
296 assert!(
297 x < 4 && y < chunk.height() / 4 && z < 4,
298 "chunk biome offsets of ({x}, {y}, {z}) are out of bounds"
299 );
300}
301
302#[inline]
303#[track_caller]
304pub(super) fn check_section_oob(chunk: &impl Chunk, sect_y: u32) {
305 assert!(
306 sect_y < chunk.height() / 16,
307 "chunk section offset of {sect_y} is out of bounds"
308 );
309}
310
311pub(super) const fn bit_width(n: usize) -> usize {
313 (usize::BITS - n.leading_zeros()) as usize
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319 use crate::layer::chunk::{LoadedChunk, UnloadedChunk};
320
321 #[test]
322 fn chunk_get_set() {
323 fn check(mut chunk: impl Chunk) {
324 assert_eq!(
325 chunk.set_block_state(1, 2, 3, BlockState::CHAIN),
326 BlockState::AIR
327 );
328 assert_eq!(
329 chunk.set_block_state(1, 2, 3, BlockState::AIR),
330 BlockState::CHAIN
331 );
332
333 assert_eq!(chunk.set_block_entity(1, 2, 3, Some(Compound::new())), None);
334 assert_eq!(chunk.set_block_entity(1, 2, 3, None), Some(Compound::new()));
335 }
336
337 let unloaded = UnloadedChunk::with_height(512);
338 let loaded = LoadedChunk::new(512);
339
340 check(unloaded);
341 check(loaded);
342 }
343
344 #[cfg(debug_assertions)]
345 #[test]
346 #[should_panic]
347 fn chunk_debug_oob_0() {
348 let mut chunk = UnloadedChunk::with_height(512);
349 chunk.set_block_state(0, 0, 16, BlockState::AIR);
350 }
351
352 #[cfg(debug_assertions)]
353 #[test]
354 #[should_panic]
355 fn chunk_debug_oob_1() {
356 let mut chunk = LoadedChunk::new(512);
357 chunk.set_block_state(0, 0, 16, BlockState::AIR);
358 }
359
360 #[cfg(debug_assertions)]
361 #[test]
362 #[should_panic]
363 fn chunk_debug_oob_2() {
364 let mut chunk = UnloadedChunk::with_height(512);
365 chunk.set_block_entity(0, 0, 16, None);
366 }
367
368 #[cfg(debug_assertions)]
369 #[test]
370 #[should_panic]
371 fn chunk_debug_oob_3() {
372 let mut chunk = LoadedChunk::new(512);
373 chunk.set_block_entity(0, 0, 16, None);
374 }
375
376 #[cfg(debug_assertions)]
377 #[test]
378 #[should_panic]
379 fn chunk_debug_oob_4() {
380 let mut chunk = UnloadedChunk::with_height(512);
381 chunk.set_biome(0, 0, 4, BiomeId::DEFAULT);
382 }
383
384 #[cfg(debug_assertions)]
385 #[test]
386 #[should_panic]
387 fn chunk_debug_oob_5() {
388 let mut chunk = LoadedChunk::new(512);
389 chunk.set_biome(0, 0, 4, BiomeId::DEFAULT);
390 }
391
392 #[cfg(debug_assertions)]
393 #[test]
394 #[should_panic]
395 fn chunk_debug_oob_6() {
396 let mut chunk = UnloadedChunk::with_height(512);
397 chunk.fill_block_state_section(chunk.height() / 16, BlockState::AIR);
398 }
399
400 #[cfg(debug_assertions)]
401 #[test]
402 #[should_panic]
403 fn chunk_debug_oob_7() {
404 let mut chunk = LoadedChunk::new(512);
405 chunk.fill_block_state_section(chunk.height() / 16, BlockState::AIR);
406 }
407
408 #[cfg(debug_assertions)]
409 #[test]
410 #[should_panic]
411 fn chunk_debug_oob_8() {
412 let mut chunk = UnloadedChunk::with_height(512);
413 chunk.fill_biome_section(chunk.height() / 16, BiomeId::DEFAULT);
414 }
415
416 #[cfg(debug_assertions)]
417 #[test]
418 #[should_panic]
419 fn chunk_debug_oob_9() {
420 let mut chunk = LoadedChunk::new(512);
421 chunk.fill_biome_section(chunk.height() / 16, BiomeId::DEFAULT);
422 }
423}