1use std::{
2 collections::VecDeque,
3 hash::{Hash, Hasher},
4 io::{self, Cursor, Write},
5};
6
7use glam::{DVec3, IVec2, IVec3};
8use rustc_hash::FxHashSet;
9
10use crate::{
11 axis::Axis,
12 direction::Direction,
13 serial::{ReadFrom, WriteTo},
14};
15
16use super::{
17 identifier::Identifier,
18 packed_position::{PackedBlockPos, PackedChunkPos, PackedSectionBlockPos, PackedSectionPos},
19};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub struct ChunkPos(pub IVec2);
24
25impl Hash for ChunkPos {
26 fn hash<H: Hasher>(&self, state: &mut H) {
27 state.write_u64(PackedChunkPos::from(*self).as_raw() as u64);
28 }
29}
30
31impl ChunkPos {
32 const OFFSETS: [(i32, i32); 8] = [
33 (-1, -1),
34 (0, -1),
35 (1, -1),
36 (-1, 0),
37 (1, 0),
38 (-1, 1),
39 (0, 1),
40 (1, 1),
41 ];
42
43 const SAFETY_MARGIN_CHUNKS: i32 = (32 + 12 + 1) * 2;
47
48 pub const MAX_COORDINATE_VALUE: i32 =
51 SectionPos::block_to_section_coord(BlockPos::MAX_HORIZONTAL_COORDINATE)
52 - Self::SAFETY_MARGIN_CHUNKS;
53
54 #[must_use]
56 pub fn neighbors(self) -> [ChunkPos; 8] {
57 Self::OFFSETS.map(|(dx, dy)| ChunkPos::new(self.0.x + dx, self.0.y + dy))
58 }
59
60 #[must_use]
61 #[inline]
62 pub const fn new(x: i32, y: i32) -> Self {
64 Self(IVec2::new(x, y))
65 }
66
67 #[must_use]
69 pub const fn from_block_pos(pos: BlockPos) -> Self {
70 Self::new(
71 SectionPos::block_to_section_coord(pos.0.x),
72 SectionPos::block_to_section_coord(pos.0.z),
73 )
74 }
75
76 #[must_use]
78 pub fn from_entity_pos(pos: DVec3) -> Self {
79 Self::from_block_pos(BlockPos::from(pos))
80 }
81
82 #[must_use]
85 #[inline]
86 pub const fn is_valid(x: i32, z: i32) -> bool {
87 x.abs().max(z.abs()) <= Self::MAX_COORDINATE_VALUE
88 }
89}
90
91impl WriteTo for ChunkPos {
92 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
93 self.0.write(writer)
94 }
95}
96
97impl ReadFrom for ChunkPos {
98 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
99 Ok(Self(IVec2::read(data)?))
100 }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
105pub struct BlockPos(pub IVec3);
106
107#[derive(Clone, Copy, Debug, PartialEq, Eq)]
109pub enum TraversalNodeStatus {
110 Accept,
112 Skip,
114 Stop,
116}
117
118#[derive(Clone, Debug)]
120pub struct SpiralAround {
121 directions: [Direction; 4],
122 cursor: BlockPos,
123 legs: i32,
124 leg: i32,
125 leg_size: i32,
126 leg_index: i32,
127 last: BlockPos,
128}
129
130impl Iterator for SpiralAround {
131 type Item = BlockPos;
132
133 fn next(&mut self) -> Option<Self::Item> {
134 let direction_index = (self.leg + 4).rem_euclid(4) as usize;
135 self.cursor = self.last.relative(self.directions[direction_index]);
136 self.last = self.cursor;
137
138 if self.leg_index >= self.leg_size {
139 if self.leg >= self.legs {
140 return None;
141 }
142
143 self.leg += 1;
144 self.leg_index = 0;
145 self.leg_size = self.leg / 2 + 1;
146 }
147
148 self.leg_index += 1;
149 Some(self.cursor)
150 }
151}
152#[derive(Debug, Clone)]
154pub struct BetweenClosed {
155 min_x: i32,
156 min_y: i32,
157 min_z: i32,
158 width: i32,
159 height: i32,
160 index: i32,
161 end: i32,
162}
163
164impl Iterator for BetweenClosed {
165 type Item = BlockPos;
166
167 fn next(&mut self) -> Option<Self::Item> {
168 if self.index == self.end {
169 return None;
170 }
171
172 let x = self.index % self.width;
173 let slice = self.index / self.width;
174 let y = slice % self.height;
175 let z = slice / self.height;
176 self.index += 1;
177
178 Some(BlockPos::new(
179 self.min_x + x,
180 self.min_y + y,
181 self.min_z + z,
182 ))
183 }
184
185 fn size_hint(&self) -> (usize, Option<usize>) {
186 let remaining = (self.end - self.index).max(0) as usize;
187 (remaining, Some(remaining))
188 }
189}
190impl From<DVec3> for BlockPos {
191 fn from(value: DVec3) -> Self {
192 BlockPos(IVec3 {
193 x: value.x.floor() as i32,
194 y: value.y.floor() as i32,
195 z: value.z.floor() as i32,
196 })
197 }
198}
199
200impl BlockPos {
201 pub const ZERO: BlockPos = BlockPos(IVec3::new(0, 0, 0));
202
203 pub const MAX_HORIZONTAL_COORDINATE: i32 = (1 << PackedBlockPos::HORIZONTAL_BITS) / 2 - 1;
205
206 #[must_use]
208 pub const fn new(x: i32, y: i32, z: i32) -> Self {
209 Self(IVec3::new(x, y, z))
210 }
211
212 #[must_use]
214 pub const fn offset(&self, dx: i32, dy: i32, dz: i32) -> Self {
215 Self(IVec3::new(self.0.x + dx, self.0.y + dy, self.0.z + dz))
216 }
217
218 #[must_use]
220 pub const fn x(&self) -> i32 {
221 self.0.x
222 }
223
224 #[must_use]
226 pub const fn y(&self) -> i32 {
227 self.0.y
228 }
229
230 #[must_use]
232 pub const fn z(&self) -> i32 {
233 self.0.z
234 }
235
236 #[must_use]
238 pub const fn above(&self) -> Self {
239 self.offset(0, 1, 0)
240 }
241
242 #[must_use]
244 pub const fn above_n(&self, n: i32) -> Self {
245 self.offset(0, n, 0)
246 }
247
248 #[must_use]
250 pub const fn below(&self) -> Self {
251 self.offset(0, -1, 0)
252 }
253
254 #[must_use]
256 pub const fn below_n(&self, n: i32) -> Self {
257 self.offset(0, -n, 0)
258 }
259
260 #[must_use]
262 pub const fn north(&self) -> Self {
263 self.offset(0, 0, -1)
264 }
265
266 #[must_use]
268 pub const fn south(&self) -> Self {
269 self.offset(0, 0, 1)
270 }
271
272 #[must_use]
274 pub const fn west(&self) -> Self {
275 self.offset(-1, 0, 0)
276 }
277
278 #[must_use]
280 pub const fn east(&self) -> Self {
281 self.offset(1, 0, 0)
282 }
283
284 #[must_use]
286 pub fn relative(self, direction: Direction) -> Self {
287 Self(self.0 + direction.offset_vec())
288 }
289
290 #[must_use]
292 pub fn breadth_first_traversal<NP, P>(
293 start_pos: Self,
294 max_depth: i32,
295 max_count: i32,
296 mut neighbor_provider: NP,
297 mut node_processor: P,
298 ) -> i32
299 where
300 NP: FnMut(Self, &mut dyn FnMut(Self)),
301 P: FnMut(Self) -> TraversalNodeStatus,
302 {
303 let mut nodes = VecDeque::from([(start_pos, 0)]);
304 let mut visited = FxHashSet::default();
305 let mut count = 0;
306
307 while let Some((current_pos, depth)) = nodes.pop_front() {
308 if !visited.insert(current_pos) {
309 continue;
310 }
311
312 let next = node_processor(current_pos);
313 if next == TraversalNodeStatus::Skip {
314 continue;
315 }
316
317 if next == TraversalNodeStatus::Stop {
318 break;
319 }
320
321 count += 1;
322 if count >= max_count {
323 return count;
324 }
325
326 if depth < max_depth {
327 let next_depth = depth + 1;
328 neighbor_provider(current_pos, &mut |pos| nodes.push_back((pos, next_depth)));
329 }
330 }
331
332 count
333 }
334
335 #[must_use]
337 pub fn relative_n(&self, direction: Direction, n: i32) -> Self {
338 if n == 0 {
339 *self
340 } else {
341 Self(self.0 + direction.offset_vec() * n)
342 }
343 }
344
345 #[must_use]
351 pub fn spiral_around(
352 center: Self,
353 radius: i32,
354 first_direction: Direction,
355 second_direction: Direction,
356 ) -> SpiralAround {
357 assert!(radius >= 0, "spiral radius must be non-negative");
358 assert!(
359 first_direction.get_axis() != second_direction.get_axis(),
360 "spiral directions cannot be on the same axis"
361 );
362
363 let cursor = center.relative(second_direction);
364 SpiralAround {
365 directions: [
366 first_direction,
367 second_direction,
368 first_direction.opposite(),
369 second_direction.opposite(),
370 ],
371 cursor,
372 legs: 4 * radius,
373 leg: -1,
374 leg_size: 0,
375 leg_index: 0,
376 last: cursor,
377 }
378 }
379
380 #[must_use]
382 pub const fn relative_axis(&self, axis: Axis, n: i32) -> Self {
383 if n == 0 {
384 *self
385 } else {
386 match axis {
387 Axis::X => self.offset(n, 0, 0),
388 Axis::Y => self.offset(0, n, 0),
389 Axis::Z => self.offset(0, 0, n),
390 }
391 }
392 }
393
394 #[must_use]
396 pub const fn at_y(&self, y: i32) -> Self {
397 Self::new(self.0.x, y, self.0.z)
398 }
399
400 #[must_use]
402 pub const fn multiply(&self, factor: i32) -> Self {
403 if factor == 1 {
404 *self
405 } else if factor == 0 {
406 Self::ZERO
407 } else {
408 Self::new(self.0.x * factor, self.0.y * factor, self.0.z * factor)
409 }
410 }
411
412 #[must_use]
414 pub fn get_center(&self) -> (f64, f64, f64) {
415 (
416 f64::from(self.0.x) + 0.5,
417 f64::from(self.0.y) + 0.5,
418 f64::from(self.0.z) + 0.5,
419 )
420 }
421
422 #[must_use]
424 pub fn get_bottom_center(&self) -> (f64, f64, f64) {
425 (
426 f64::from(self.0.x) + 0.5,
427 f64::from(self.0.y),
428 f64::from(self.0.z) + 0.5,
429 )
430 }
431
432 #[must_use]
434 pub const fn containing(x: f64, y: f64, z: f64) -> Self {
435 Self::new(x.floor() as i32, y.floor() as i32, z.floor() as i32)
436 }
437
438 #[must_use]
440 pub const fn min(a: BlockPos, b: BlockPos) -> Self {
441 Self::new(a.0.x.min(b.0.x), a.0.y.min(b.0.y), a.0.z.min(b.0.z))
442 }
443
444 #[must_use]
446 pub const fn max(a: BlockPos, b: BlockPos) -> Self {
447 Self::new(a.0.x.max(b.0.x), a.0.y.max(b.0.y), a.0.z.max(b.0.z))
448 }
449
450 #[must_use]
452 pub const fn within_manhattan(
453 self,
454 reach_x: i32,
455 reach_y: i32,
456 reach_z: i32,
457 ) -> BlockPosWithinManhattan {
458 BlockPosWithinManhattan {
459 origin: self,
460 reach_x,
461 reach_y,
462 reach_z,
463 max_depth: reach_x + reach_y + reach_z,
464 current_depth: 0,
465 max_x: 0,
466 max_y: 0,
467 x: 0,
468 y: 0,
469 pending_z_mirror: None,
470 done: false,
471 }
472 }
473 #[must_use]
478 pub const fn between_closed(a: Self, b: Self) -> BetweenClosed {
479 Self::between_closed_coords(
480 a.0.x.min(b.0.x),
481 a.0.y.min(b.0.y),
482 a.0.z.min(b.0.z),
483 a.0.x.max(b.0.x),
484 a.0.y.max(b.0.y),
485 a.0.z.max(b.0.z),
486 )
487 }
488
489 #[must_use]
493 pub const fn between_closed_coords(
494 min_x: i32,
495 min_y: i32,
496 min_z: i32,
497 max_x: i32,
498 max_y: i32,
499 max_z: i32,
500 ) -> BetweenClosed {
501 let width = max_x - min_x + 1;
502 let height = max_y - min_y + 1;
503 let depth = max_z - min_z + 1;
504
505 BetweenClosed {
506 min_x,
507 min_y,
508 min_z,
509 width,
510 height,
511 index: 0,
512 end: width * height * depth,
513 }
514 }
515
516 #[must_use]
518 pub fn find_closest_match(
519 self,
520 horizontal_search_radius: i32,
521 vertical_search_radius: i32,
522 mut predicate: impl FnMut(BlockPos) -> bool,
523 ) -> Option<BlockPos> {
524 self.within_manhattan(
525 horizontal_search_radius,
526 vertical_search_radius,
527 horizontal_search_radius,
528 )
529 .find(|pos| predicate(*pos))
530 }
531}
532
533#[derive(Debug, Clone)]
535pub struct BlockPosWithinManhattan {
536 origin: BlockPos,
537 reach_x: i32,
538 reach_y: i32,
539 reach_z: i32,
540 max_depth: i32,
541 current_depth: i32,
542 max_x: i32,
543 max_y: i32,
544 x: i32,
545 y: i32,
546 pending_z_mirror: Option<BlockPos>,
547 done: bool,
548}
549
550impl Iterator for BlockPosWithinManhattan {
551 type Item = BlockPos;
552
553 fn next(&mut self) -> Option<Self::Item> {
554 if let Some(pos) = self.pending_z_mirror.take() {
555 return Some(pos);
556 }
557 if self.done {
558 return None;
559 }
560
561 loop {
562 if self.y > self.max_y {
563 self.x += 1;
564 if self.x > self.max_x {
565 self.current_depth += 1;
566 if self.current_depth > self.max_depth {
567 self.done = true;
568 return None;
569 }
570
571 self.max_x = self.reach_x.min(self.current_depth);
572 self.x = -self.max_x;
573 }
574
575 self.max_y = self.reach_y.min(self.current_depth - self.x.abs());
576 self.y = -self.max_y;
577 }
578
579 let x = self.x;
580 let y = self.y;
581 let z = self.current_depth - x.abs() - y.abs();
582 self.y += 1;
583 if z > self.reach_z {
584 continue;
585 }
586
587 let pos = self.origin.offset(x, y, z);
588 if z != 0 {
589 self.pending_z_mirror = Some(self.origin.offset(x, y, -z));
590 }
591 return Some(pos);
592 }
593 }
594}
595
596impl ReadFrom for BlockPos {
597 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
598 let packed = <i64 as ReadFrom>::read(data)?;
599 Ok(PackedBlockPos::from_raw(packed).into())
600 }
601}
602
603#[derive(Debug, Clone, PartialEq, Eq, Hash)]
605pub struct GlobalPos {
606 pub dimension: Identifier,
608 pub pos: BlockPos,
610}
611
612impl GlobalPos {
613 #[must_use]
615 pub const fn new(dimension: Identifier, pos: BlockPos) -> Self {
616 Self { dimension, pos }
617 }
618}
619
620impl ReadFrom for GlobalPos {
621 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
622 Ok(Self {
623 dimension: <Identifier as ReadFrom>::read(data)?,
624 pos: BlockPos::read(data)?,
625 })
626 }
627}
628
629impl WriteTo for GlobalPos {
630 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
631 self.dimension.write(writer)?;
632 self.pos.write(writer)
633 }
634}
635
636#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
638pub struct SectionPos(pub IVec3);
639
640impl SectionPos {
641 const SECTION_BITS: i32 = 4;
642 const SECTION_SIZE: i32 = 1 << Self::SECTION_BITS; pub(super) const SECTION_MASK: i32 = Self::SECTION_SIZE - 1; #[must_use]
647 pub const fn new(x: i32, y: i32, z: i32) -> Self {
648 Self(IVec3::new(x, y, z))
649 }
650
651 #[must_use]
653 #[inline]
654 pub const fn block_to_section_coord(block_coord: i32) -> i32 {
655 block_coord >> Self::SECTION_BITS
656 }
657
658 #[must_use]
660 pub const fn from_block_pos(pos: BlockPos) -> Self {
661 Self::new(
662 Self::block_to_section_coord(pos.0.x),
663 Self::block_to_section_coord(pos.0.y),
664 Self::block_to_section_coord(pos.0.z),
665 )
666 }
667
668 #[must_use]
670 pub fn from_entity_pos(pos: DVec3) -> Self {
671 Self::from_block_pos(BlockPos::from(pos))
672 }
673
674 #[must_use]
676 pub const fn x(&self) -> i32 {
677 self.0.x
678 }
679
680 #[must_use]
682 pub const fn y(&self) -> i32 {
683 self.0.y
684 }
685
686 #[must_use]
688 pub const fn z(&self) -> i32 {
689 self.0.z
690 }
691
692 #[must_use]
694 pub const fn relative_to_block_x(&self, relative: PackedSectionBlockPos) -> i32 {
695 (self.0.x << Self::SECTION_BITS) + relative.x() as i32
696 }
697
698 #[must_use]
700 pub const fn relative_to_block_y(&self, relative: PackedSectionBlockPos) -> i32 {
701 (self.0.y << Self::SECTION_BITS) + relative.y() as i32
702 }
703
704 #[must_use]
706 pub const fn relative_to_block_z(&self, relative: PackedSectionBlockPos) -> i32 {
707 (self.0.z << Self::SECTION_BITS) + relative.z() as i32
708 }
709
710 #[must_use]
713 #[inline]
714 pub const fn section_relative_pos(pos: BlockPos) -> PackedSectionBlockPos {
715 PackedSectionBlockPos::from_block_pos(pos)
716 }
717
718 #[must_use]
720 pub const fn relative_to_block_pos(&self, relative: PackedSectionBlockPos) -> BlockPos {
721 BlockPos(IVec3::new(
722 self.relative_to_block_x(relative),
723 self.relative_to_block_y(relative),
724 self.relative_to_block_z(relative),
725 ))
726 }
727}
728
729impl ReadFrom for SectionPos {
730 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
731 Ok(<PackedSectionPos as ReadFrom>::read(data)?.into())
732 }
733}
734
735impl WriteTo for SectionPos {
736 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
737 PackedSectionPos::from(*self).write(writer)
738 }
739}