1#[allow(clippy::module_inception)]
2mod chunk;
3pub mod loaded;
4mod paletted_container;
5pub mod unloaded;
6
7use std::borrow::Cow;
8use std::collections::hash_map::{Entry, OccupiedEntry, VacantEntry};
9use std::fmt;
10
11use bevy_app::prelude::*;
12use bevy_ecs::prelude::*;
13pub use chunk::{MAX_HEIGHT, *};
14pub use loaded::LoadedChunk;
15use rustc_hash::FxHashMap;
16pub use unloaded::UnloadedChunk;
17use valence_math::{DVec3, Vec3};
18use valence_nbt::Compound;
19use valence_protocol::encode::{PacketWriter, WritePacket};
20use valence_protocol::packets::play::particle_s2c::Particle;
21use valence_protocol::packets::play::{ParticleS2c, PlaySoundS2c};
22use valence_protocol::sound::{Sound, SoundCategory, SoundId};
23use valence_protocol::{BiomePos, BlockPos, ChunkPos, CompressionThreshold, Encode, Ident, Packet};
24use valence_registry::biome::{BiomeId, BiomeRegistry};
25use valence_registry::DimensionTypeRegistry;
26use valence_server_common::Server;
27
28use super::bvh::GetChunkPos;
29use super::message::Messages;
30use super::{Layer, UpdateLayersPostClientSet, UpdateLayersPreClientSet};
31
32#[derive(Component, Debug)]
36pub struct ChunkLayer {
37 messages: ChunkLayerMessages,
38 chunks: FxHashMap<ChunkPos, LoadedChunk>,
39 info: ChunkLayerInfo,
40}
41
42pub(crate) struct ChunkLayerInfo {
44 dimension_type_name: Ident<String>,
45 height: u32,
46 min_y: i32,
47 biome_registry_len: usize,
48 threshold: CompressionThreshold,
49}
50
51impl fmt::Debug for ChunkLayerInfo {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 f.debug_struct("ChunkLayerInfo")
54 .field("dimension_type_name", &self.dimension_type_name)
55 .field("height", &self.height)
56 .field("min_y", &self.min_y)
57 .field("biome_registry_len", &self.biome_registry_len)
58 .field("threshold", &self.threshold)
59 .finish()
61 }
62}
63
64type ChunkLayerMessages = Messages<GlobalMsg, LocalMsg>;
65
66#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
67pub(crate) enum GlobalMsg {
68 Packet,
70 PacketExcept { except: Entity },
73}
74
75#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
76pub(crate) enum LocalMsg {
77 PacketAt {
79 pos: ChunkPos,
80 },
81 PacketAtExcept {
82 pos: ChunkPos,
83 except: Entity,
84 },
85 RadiusAt {
86 center: BlockPos,
87 radius_squared: u32,
88 },
89 RadiusAtExcept {
90 center: BlockPos,
91 radius_squared: u32,
92 except: Entity,
93 },
94 ChangeChunkState {
100 pos: ChunkPos,
101 },
102 ChangeBiome {
105 pos: ChunkPos,
106 },
107}
108
109impl GetChunkPos for LocalMsg {
110 fn chunk_pos(&self) -> ChunkPos {
111 match *self {
112 LocalMsg::PacketAt { pos } => pos,
113 LocalMsg::PacketAtExcept { pos, .. } => pos,
114 LocalMsg::RadiusAt { center, .. } => center.into(),
115 LocalMsg::RadiusAtExcept { center, .. } => center.into(),
116 LocalMsg::ChangeBiome { pos } => pos,
117 LocalMsg::ChangeChunkState { pos } => pos,
118 }
119 }
120}
121
122impl ChunkLayer {
123 pub(crate) const LOAD: u8 = 0;
124 pub(crate) const UNLOAD: u8 = 1;
125 pub(crate) const OVERWRITE: u8 = 2;
126
127 #[track_caller]
129 pub fn new<N: Into<Ident<String>>>(
130 dimension_type_name: N,
131 dimensions: &DimensionTypeRegistry,
132 biomes: &BiomeRegistry,
133 server: &Server,
134 ) -> Self {
135 let dimension_type_name = dimension_type_name.into();
136
137 let dim = &dimensions[dimension_type_name.as_str_ident()];
138
139 assert!(
140 (0..MAX_HEIGHT as i32).contains(&dim.height),
141 "invalid dimension height of {}",
142 dim.height
143 );
144
145 Self {
146 messages: Messages::new(),
147 chunks: Default::default(),
148 info: ChunkLayerInfo {
149 dimension_type_name,
150 height: dim.height as u32,
151 min_y: dim.min_y,
152 biome_registry_len: biomes.iter().len(),
153 threshold: server.compression_threshold(),
154 },
155 }
156 }
157
158 pub fn dimension_type_name(&self) -> Ident<&str> {
160 self.info.dimension_type_name.as_str_ident()
161 }
162
163 pub fn height(&self) -> u32 {
165 self.info.height
166 }
167
168 pub fn min_y(&self) -> i32 {
170 self.info.min_y
171 }
172
173 pub fn chunk<P: Into<ChunkPos>>(&self, pos: P) -> Option<&LoadedChunk> {
175 self.chunks.get(&pos.into())
176 }
177
178 pub fn chunk_mut<P: Into<ChunkPos>>(&mut self, pos: P) -> Option<&mut LoadedChunk> {
181 self.chunks.get_mut(&pos.into())
182 }
183
184 pub fn insert_chunk<P: Into<ChunkPos>>(
187 &mut self,
188 pos: P,
189 chunk: UnloadedChunk,
190 ) -> Option<UnloadedChunk> {
191 match self.chunk_entry(pos) {
192 ChunkEntry::Occupied(mut oe) => Some(oe.insert(chunk)),
193 ChunkEntry::Vacant(ve) => {
194 ve.insert(chunk);
195 None
196 }
197 }
198 }
199
200 pub fn remove_chunk<P: Into<ChunkPos>>(&mut self, pos: P) -> Option<UnloadedChunk> {
203 match self.chunk_entry(pos) {
204 ChunkEntry::Occupied(oe) => Some(oe.remove()),
205 ChunkEntry::Vacant(_) => None,
206 }
207 }
208
209 pub fn clear_chunks(&mut self) {
211 self.retain_chunks(|_, _| false)
212 }
213
214 pub fn retain_chunks<F>(&mut self, mut f: F)
216 where
217 F: FnMut(ChunkPos, &mut LoadedChunk) -> bool,
218 {
219 self.chunks.retain(|pos, chunk| {
220 if !f(*pos, chunk) {
221 self.messages
222 .send_local_infallible(LocalMsg::ChangeChunkState { pos: *pos }, |b| {
223 b.push(Self::UNLOAD)
224 });
225
226 false
227 } else {
228 true
229 }
230 });
231 }
232
233 pub fn chunk_entry<P: Into<ChunkPos>>(&mut self, pos: P) -> ChunkEntry {
235 match self.chunks.entry(pos.into()) {
236 Entry::Occupied(oe) => ChunkEntry::Occupied(OccupiedChunkEntry {
237 messages: &mut self.messages,
238 entry: oe,
239 }),
240 Entry::Vacant(ve) => ChunkEntry::Vacant(VacantChunkEntry {
241 height: self.info.height,
242 messages: &mut self.messages,
243 entry: ve,
244 }),
245 }
246 }
247
248 pub fn chunks(&self) -> impl Iterator<Item = (ChunkPos, &LoadedChunk)> + Clone + '_ {
251 self.chunks.iter().map(|(pos, chunk)| (*pos, chunk))
252 }
253
254 pub fn chunks_mut(&mut self) -> impl Iterator<Item = (ChunkPos, &mut LoadedChunk)> + '_ {
257 self.chunks.iter_mut().map(|(pos, chunk)| (*pos, chunk))
258 }
259
260 pub fn shrink_to_fit(&mut self) {
262 for (_, chunk) in self.chunks_mut() {
263 chunk.shrink_to_fit();
264 }
265
266 self.chunks.shrink_to_fit();
267 self.messages.shrink_to_fit();
268 }
269
270 pub fn block<P: Into<BlockPos>>(&self, pos: P) -> Option<BlockRef> {
271 let pos = pos.into();
272
273 let y = pos
274 .y
275 .checked_sub(self.info.min_y)
276 .and_then(|y| y.try_into().ok())?;
277
278 if y >= self.info.height {
279 return None;
280 }
281
282 let chunk = self.chunk(pos)?;
283
284 let x = pos.x.rem_euclid(16) as u32;
285 let z = pos.z.rem_euclid(16) as u32;
286
287 Some(chunk.block(x, y, z))
288 }
289
290 pub fn set_block<P, B>(&mut self, pos: P, block: B) -> Option<Block>
291 where
292 P: Into<BlockPos>,
293 B: IntoBlock,
294 {
295 let pos = pos.into();
296
297 let y = pos
298 .y
299 .checked_sub(self.info.min_y)
300 .and_then(|y| y.try_into().ok())?;
301
302 if y >= self.info.height {
303 return None;
304 }
305
306 let chunk = self.chunk_mut(pos)?;
307
308 let x = pos.x.rem_euclid(16) as u32;
309 let z = pos.z.rem_euclid(16) as u32;
310
311 Some(chunk.set_block(x, y, z, block))
312 }
313
314 pub fn block_entity_mut<P: Into<BlockPos>>(&mut self, pos: P) -> Option<&mut Compound> {
315 let pos = pos.into();
316
317 let y = pos
318 .y
319 .checked_sub(self.info.min_y)
320 .and_then(|y| y.try_into().ok())?;
321
322 if y >= self.info.height {
323 return None;
324 }
325
326 let chunk = self.chunk_mut(pos)?;
327
328 let x = pos.x.rem_euclid(16) as u32;
329 let z = pos.z.rem_euclid(16) as u32;
330
331 chunk.block_entity_mut(x, y, z)
332 }
333
334 pub fn biome<P: Into<BiomePos>>(&self, pos: P) -> Option<BiomeId> {
335 let pos = pos.into();
336
337 let y = pos
338 .y
339 .checked_sub(self.info.min_y / 4)
340 .and_then(|y| y.try_into().ok())?;
341
342 if y >= self.info.height / 4 {
343 return None;
344 }
345
346 let chunk = self.chunk(pos)?;
347
348 let x = pos.x.rem_euclid(4) as u32;
349 let z = pos.z.rem_euclid(4) as u32;
350
351 Some(chunk.biome(x, y, z))
352 }
353
354 pub fn set_biome<P: Into<BiomePos>>(&mut self, pos: P, biome: BiomeId) -> Option<BiomeId> {
355 let pos = pos.into();
356
357 let y = pos
358 .y
359 .checked_sub(self.info.min_y / 4)
360 .and_then(|y| y.try_into().ok())?;
361
362 if y >= self.info.height / 4 {
363 return None;
364 }
365
366 let chunk = self.chunk_mut(pos)?;
367
368 let x = pos.x.rem_euclid(4) as u32;
369 let z = pos.z.rem_euclid(4) as u32;
370
371 Some(chunk.set_biome(x, y, z, biome))
372 }
373
374 pub(crate) fn info(&self) -> &ChunkLayerInfo {
375 &self.info
376 }
377
378 pub(crate) fn messages(&self) -> &ChunkLayerMessages {
379 &self.messages
380 }
381
382 pub fn play_particle<P, O>(
387 &mut self,
388 particle: &Particle,
389 long_distance: bool,
390 position: P,
391 offset: O,
392 max_speed: f32,
393 count: i32,
394 ) where
395 P: Into<DVec3>,
396 O: Into<Vec3>,
397 {
398 let position = position.into();
399
400 self.view_writer(position).write_packet(&ParticleS2c {
401 particle: Cow::Borrowed(particle),
402 long_distance,
403 position,
404 offset: offset.into(),
405 max_speed,
406 count,
407 });
408 }
409
410 pub fn play_sound<P: Into<DVec3>>(
415 &mut self,
416 sound: Sound,
417 category: SoundCategory,
418 position: P,
419 volume: f32,
420 pitch: f32,
421 ) {
422 let position = position.into();
423
424 self.view_writer(position).write_packet(&PlaySoundS2c {
425 id: SoundId::Direct {
426 id: sound.to_ident().into(),
427 range: None,
428 },
429 category,
430 position: (position * 8.0).as_ivec3(),
431 volume,
432 pitch,
433 seed: rand::random(),
434 });
435 }
436}
437
438impl Layer for ChunkLayer {
439 type ExceptWriter<'a> = ExceptWriter<'a>;
440
441 type ViewWriter<'a> = ViewWriter<'a>;
442
443 type ViewExceptWriter<'a> = ViewExceptWriter<'a>;
444
445 type RadiusWriter<'a> = RadiusWriter<'a>;
446
447 type RadiusExceptWriter<'a> = RadiusExceptWriter<'a>;
448
449 fn except_writer(&mut self, except: Entity) -> Self::ExceptWriter<'_> {
450 ExceptWriter {
451 layer: self,
452 except,
453 }
454 }
455
456 fn view_writer(&mut self, pos: impl Into<ChunkPos>) -> Self::ViewWriter<'_> {
457 ViewWriter {
458 layer: self,
459 pos: pos.into(),
460 }
461 }
462
463 fn view_except_writer(
464 &mut self,
465 pos: impl Into<ChunkPos>,
466 except: Entity,
467 ) -> Self::ViewExceptWriter<'_> {
468 ViewExceptWriter {
469 layer: self,
470 pos: pos.into(),
471 except,
472 }
473 }
474
475 fn radius_writer(
476 &mut self,
477 center: impl Into<BlockPos>,
478 radius: u32,
479 ) -> Self::RadiusWriter<'_> {
480 RadiusWriter {
481 layer: self,
482 center: center.into(),
483 radius,
484 }
485 }
486
487 fn radius_except_writer(
488 &mut self,
489 center: impl Into<BlockPos>,
490 radius: u32,
491 except: Entity,
492 ) -> Self::RadiusExceptWriter<'_> {
493 RadiusExceptWriter {
494 layer: self,
495 center: center.into(),
496 radius,
497 except,
498 }
499 }
500}
501
502impl WritePacket for ChunkLayer {
503 fn write_packet_fallible<P>(&mut self, packet: &P) -> anyhow::Result<()>
504 where
505 P: Packet + Encode,
506 {
507 self.messages.send_global(GlobalMsg::Packet, |b| {
508 PacketWriter::new(b, self.info.threshold).write_packet_fallible(packet)
509 })
510 }
511
512 fn write_packet_bytes(&mut self, bytes: &[u8]) {
513 self.messages
514 .send_global_infallible(GlobalMsg::Packet, |b| b.extend_from_slice(bytes));
515 }
516}
517
518pub struct ExceptWriter<'a> {
519 layer: &'a mut ChunkLayer,
520 except: Entity,
521}
522
523impl WritePacket for ExceptWriter<'_> {
524 fn write_packet_fallible<P>(&mut self, packet: &P) -> anyhow::Result<()>
525 where
526 P: Packet + Encode,
527 {
528 self.layer.messages.send_global(
529 GlobalMsg::PacketExcept {
530 except: self.except,
531 },
532 |b| PacketWriter::new(b, self.layer.info.threshold).write_packet_fallible(packet),
533 )
534 }
535
536 fn write_packet_bytes(&mut self, bytes: &[u8]) {
537 self.layer.messages.send_global_infallible(
538 GlobalMsg::PacketExcept {
539 except: self.except,
540 },
541 |b| b.extend_from_slice(bytes),
542 )
543 }
544}
545
546pub struct ViewWriter<'a> {
547 layer: &'a mut ChunkLayer,
548 pos: ChunkPos,
549}
550
551impl WritePacket for ViewWriter<'_> {
552 fn write_packet_fallible<P>(&mut self, packet: &P) -> anyhow::Result<()>
553 where
554 P: Packet + Encode,
555 {
556 self.layer
557 .messages
558 .send_local(LocalMsg::PacketAt { pos: self.pos }, |b| {
559 PacketWriter::new(b, self.layer.info.threshold).write_packet_fallible(packet)
560 })
561 }
562
563 fn write_packet_bytes(&mut self, bytes: &[u8]) {
564 self.layer
565 .messages
566 .send_local_infallible(LocalMsg::PacketAt { pos: self.pos }, |b| {
567 b.extend_from_slice(bytes)
568 });
569 }
570}
571
572pub struct ViewExceptWriter<'a> {
573 layer: &'a mut ChunkLayer,
574 pos: ChunkPos,
575 except: Entity,
576}
577
578impl WritePacket for ViewExceptWriter<'_> {
579 fn write_packet_fallible<P>(&mut self, packet: &P) -> anyhow::Result<()>
580 where
581 P: Packet + Encode,
582 {
583 self.layer.messages.send_local(
584 LocalMsg::PacketAtExcept {
585 pos: self.pos,
586 except: self.except,
587 },
588 |b| PacketWriter::new(b, self.layer.info.threshold).write_packet_fallible(packet),
589 )
590 }
591
592 fn write_packet_bytes(&mut self, bytes: &[u8]) {
593 self.layer.messages.send_local_infallible(
594 LocalMsg::PacketAtExcept {
595 pos: self.pos,
596 except: self.except,
597 },
598 |b| b.extend_from_slice(bytes),
599 );
600 }
601}
602
603pub struct RadiusWriter<'a> {
604 layer: &'a mut ChunkLayer,
605 center: BlockPos,
606 radius: u32,
607}
608
609impl WritePacket for RadiusWriter<'_> {
610 fn write_packet_fallible<P>(&mut self, packet: &P) -> anyhow::Result<()>
611 where
612 P: Packet + Encode,
613 {
614 self.layer.messages.send_local(
615 LocalMsg::RadiusAt {
616 center: self.center,
617 radius_squared: self.radius,
618 },
619 |b| PacketWriter::new(b, self.layer.info.threshold).write_packet_fallible(packet),
620 )
621 }
622
623 fn write_packet_bytes(&mut self, bytes: &[u8]) {
624 self.layer.messages.send_local_infallible(
625 LocalMsg::RadiusAt {
626 center: self.center,
627 radius_squared: self.radius,
628 },
629 |b| b.extend_from_slice(bytes),
630 );
631 }
632}
633
634pub struct RadiusExceptWriter<'a> {
635 layer: &'a mut ChunkLayer,
636 center: BlockPos,
637 radius: u32,
638 except: Entity,
639}
640
641impl WritePacket for RadiusExceptWriter<'_> {
642 fn write_packet_fallible<P>(&mut self, packet: &P) -> anyhow::Result<()>
643 where
644 P: Packet + Encode,
645 {
646 self.layer.messages.send_local(
647 LocalMsg::RadiusAtExcept {
648 center: self.center,
649 radius_squared: self.radius,
650 except: self.except,
651 },
652 |b| PacketWriter::new(b, self.layer.info.threshold).write_packet_fallible(packet),
653 )
654 }
655
656 fn write_packet_bytes(&mut self, bytes: &[u8]) {
657 self.layer.messages.send_local_infallible(
658 LocalMsg::RadiusAtExcept {
659 center: self.center,
660 radius_squared: self.radius,
661 except: self.except,
662 },
663 |b| b.extend_from_slice(bytes),
664 );
665 }
666}
667
668#[derive(Debug)]
669pub enum ChunkEntry<'a> {
670 Occupied(OccupiedChunkEntry<'a>),
671 Vacant(VacantChunkEntry<'a>),
672}
673
674impl<'a> ChunkEntry<'a> {
675 pub fn or_default(self) -> &'a mut LoadedChunk {
676 match self {
677 ChunkEntry::Occupied(oe) => oe.into_mut(),
678 ChunkEntry::Vacant(ve) => ve.insert(UnloadedChunk::new()),
679 }
680 }
681}
682
683#[derive(Debug)]
684pub struct OccupiedChunkEntry<'a> {
685 messages: &'a mut ChunkLayerMessages,
686 entry: OccupiedEntry<'a, ChunkPos, LoadedChunk>,
687}
688
689impl<'a> OccupiedChunkEntry<'a> {
690 pub fn get(&self) -> &LoadedChunk {
691 self.entry.get()
692 }
693
694 pub fn get_mut(&mut self) -> &mut LoadedChunk {
695 self.entry.get_mut()
696 }
697
698 pub fn insert(&mut self, chunk: UnloadedChunk) -> UnloadedChunk {
699 self.messages.send_local_infallible(
700 LocalMsg::ChangeChunkState {
701 pos: *self.entry.key(),
702 },
703 |b| b.push(ChunkLayer::OVERWRITE),
704 );
705
706 self.entry.get_mut().insert(chunk)
707 }
708
709 pub fn into_mut(self) -> &'a mut LoadedChunk {
710 self.entry.into_mut()
711 }
712
713 pub fn key(&self) -> &ChunkPos {
714 self.entry.key()
715 }
716
717 pub fn remove(self) -> UnloadedChunk {
718 self.messages.send_local_infallible(
719 LocalMsg::ChangeChunkState {
720 pos: *self.entry.key(),
721 },
722 |b| b.push(ChunkLayer::UNLOAD),
723 );
724
725 self.entry.remove().remove()
726 }
727
728 pub fn remove_entry(mut self) -> (ChunkPos, UnloadedChunk) {
729 let pos = *self.entry.key();
730 let chunk = self.entry.get_mut().remove();
731
732 self.messages.send_local_infallible(
733 LocalMsg::ChangeChunkState {
734 pos: *self.entry.key(),
735 },
736 |b| b.push(ChunkLayer::UNLOAD),
737 );
738
739 (pos, chunk)
740 }
741}
742
743#[derive(Debug)]
744pub struct VacantChunkEntry<'a> {
745 height: u32,
746 messages: &'a mut ChunkLayerMessages,
747 entry: VacantEntry<'a, ChunkPos, LoadedChunk>,
748}
749
750impl<'a> VacantChunkEntry<'a> {
751 pub fn insert(self, chunk: UnloadedChunk) -> &'a mut LoadedChunk {
752 let mut loaded = LoadedChunk::new(self.height);
753 loaded.insert(chunk);
754
755 self.messages.send_local_infallible(
756 LocalMsg::ChangeChunkState {
757 pos: *self.entry.key(),
758 },
759 |b| b.push(ChunkLayer::LOAD),
760 );
761
762 self.entry.insert(loaded)
763 }
764
765 pub fn into_key(self) -> ChunkPos {
766 *self.entry.key()
767 }
768
769 pub fn key(&self) -> &ChunkPos {
770 self.entry.key()
771 }
772}
773
774pub(super) fn build(app: &mut App) {
775 app.add_systems(
776 PostUpdate,
777 (
778 update_chunk_layers_pre_client.in_set(UpdateLayersPreClientSet),
779 update_chunk_layers_post_client.in_set(UpdateLayersPostClientSet),
780 ),
781 );
782}
783
784fn update_chunk_layers_pre_client(mut layers: Query<&mut ChunkLayer>) {
785 for layer in &mut layers {
786 let layer = layer.into_inner();
787
788 for (&pos, chunk) in &mut layer.chunks {
789 chunk.update_pre_client(pos, &layer.info, &mut layer.messages);
790 }
791
792 layer.messages.ready();
793 }
794}
795
796fn update_chunk_layers_post_client(mut layers: Query<&mut ChunkLayer>) {
797 for mut layer in &mut layers {
798 layer.messages.unready();
799 }
800}