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 java_hash_code(&self) -> i32 {
239 self.y()
240 .wrapping_add(self.z().wrapping_mul(31))
241 .wrapping_mul(31)
242 .wrapping_add(self.x())
243 }
244
245 #[must_use]
247 pub const fn above(&self) -> Self {
248 self.offset(0, 1, 0)
249 }
250
251 #[must_use]
253 pub const fn above_n(&self, n: i32) -> Self {
254 self.offset(0, n, 0)
255 }
256
257 #[must_use]
259 pub const fn below(&self) -> Self {
260 self.offset(0, -1, 0)
261 }
262
263 #[must_use]
265 pub const fn below_n(&self, n: i32) -> Self {
266 self.offset(0, -n, 0)
267 }
268
269 #[must_use]
271 pub const fn north(&self) -> Self {
272 self.offset(0, 0, -1)
273 }
274
275 #[must_use]
277 pub const fn south(&self) -> Self {
278 self.offset(0, 0, 1)
279 }
280
281 #[must_use]
283 pub const fn west(&self) -> Self {
284 self.offset(-1, 0, 0)
285 }
286
287 #[must_use]
289 pub const fn east(&self) -> Self {
290 self.offset(1, 0, 0)
291 }
292
293 #[must_use]
295 pub fn relative(self, direction: Direction) -> Self {
296 Self(self.0 + direction.offset_vec())
297 }
298
299 #[must_use]
301 pub fn breadth_first_traversal<NP, P>(
302 start_pos: Self,
303 max_depth: i32,
304 max_count: i32,
305 mut neighbor_provider: NP,
306 mut node_processor: P,
307 ) -> i32
308 where
309 NP: FnMut(Self, &mut dyn FnMut(Self)),
310 P: FnMut(Self) -> TraversalNodeStatus,
311 {
312 let mut nodes = VecDeque::from([(start_pos, 0)]);
313 let mut visited = FxHashSet::default();
314 let mut count = 0;
315
316 while let Some((current_pos, depth)) = nodes.pop_front() {
317 if !visited.insert(current_pos) {
318 continue;
319 }
320
321 let next = node_processor(current_pos);
322 if next == TraversalNodeStatus::Skip {
323 continue;
324 }
325
326 if next == TraversalNodeStatus::Stop {
327 break;
328 }
329
330 count += 1;
331 if count >= max_count {
332 return count;
333 }
334
335 if depth < max_depth {
336 let next_depth = depth + 1;
337 neighbor_provider(current_pos, &mut |pos| nodes.push_back((pos, next_depth)));
338 }
339 }
340
341 count
342 }
343
344 #[must_use]
346 pub fn relative_n(&self, direction: Direction, n: i32) -> Self {
347 if n == 0 {
348 *self
349 } else {
350 Self(self.0 + direction.offset_vec() * n)
351 }
352 }
353
354 #[must_use]
361 pub fn spiral_around(
362 center: Self,
363 radius: i32,
364 first_direction: Direction,
365 second_direction: Direction,
366 ) -> SpiralAround {
367 assert!(radius >= 0, "spiral radius must be non-negative");
368 assert!(
369 first_direction.get_axis() != second_direction.get_axis(),
370 "spiral directions cannot be on the same axis"
371 );
372
373 let cursor = center.relative(second_direction);
374 SpiralAround {
375 directions: [
376 first_direction,
377 second_direction,
378 first_direction.opposite(),
379 second_direction.opposite(),
380 ],
381 cursor,
382 legs: 4 * radius,
383 leg: -1,
384 leg_size: 0,
385 leg_index: 0,
386 last: cursor,
387 }
388 }
389
390 #[must_use]
392 pub const fn relative_axis(&self, axis: Axis, n: i32) -> Self {
393 if n == 0 {
394 *self
395 } else {
396 match axis {
397 Axis::X => self.offset(n, 0, 0),
398 Axis::Y => self.offset(0, n, 0),
399 Axis::Z => self.offset(0, 0, n),
400 }
401 }
402 }
403
404 #[must_use]
406 pub const fn at_y(&self, y: i32) -> Self {
407 Self::new(self.0.x, y, self.0.z)
408 }
409
410 #[must_use]
412 pub const fn multiply(&self, factor: i32) -> Self {
413 if factor == 1 {
414 *self
415 } else if factor == 0 {
416 Self::ZERO
417 } else {
418 Self::new(self.0.x * factor, self.0.y * factor, self.0.z * factor)
419 }
420 }
421
422 #[must_use]
424 pub fn get_center(&self) -> (f64, f64, f64) {
425 (
426 f64::from(self.0.x) + 0.5,
427 f64::from(self.0.y) + 0.5,
428 f64::from(self.0.z) + 0.5,
429 )
430 }
431
432 #[must_use]
434 pub fn get_bottom_center(&self) -> (f64, f64, f64) {
435 (
436 f64::from(self.0.x) + 0.5,
437 f64::from(self.0.y),
438 f64::from(self.0.z) + 0.5,
439 )
440 }
441
442 #[must_use]
444 pub fn closer_to_center_than(&self, position: DVec3, distance: f64) -> bool {
445 let (x, y, z) = self.get_center();
446 DVec3::new(x, y, z).distance_squared(position) < distance * distance
447 }
448
449 #[must_use]
451 pub fn closer_than(&self, other: BlockPos, distance: f64) -> bool {
452 let dx = f64::from(self.0.x - other.0.x);
453 let dy = f64::from(self.0.y - other.0.y);
454 let dz = f64::from(self.0.z - other.0.z);
455 dx * dx + dy * dy + dz * dz < distance * distance
456 }
457
458 #[must_use]
460 pub const fn containing(x: f64, y: f64, z: f64) -> Self {
461 Self::new(x.floor() as i32, y.floor() as i32, z.floor() as i32)
462 }
463
464 #[must_use]
466 pub const fn min(a: BlockPos, b: BlockPos) -> Self {
467 Self::new(a.0.x.min(b.0.x), a.0.y.min(b.0.y), a.0.z.min(b.0.z))
468 }
469
470 #[must_use]
472 pub const fn max(a: BlockPos, b: BlockPos) -> Self {
473 Self::new(a.0.x.max(b.0.x), a.0.y.max(b.0.y), a.0.z.max(b.0.z))
474 }
475
476 #[must_use]
478 pub const fn within_manhattan(
479 self,
480 reach_x: i32,
481 reach_y: i32,
482 reach_z: i32,
483 ) -> BlockPosWithinManhattan {
484 BlockPosWithinManhattan {
485 origin: self,
486 reach_x,
487 reach_y,
488 reach_z,
489 max_depth: reach_x + reach_y + reach_z,
490 current_depth: 0,
491 max_x: 0,
492 max_y: 0,
493 x: 0,
494 y: 0,
495 pending_z_mirror: None,
496 done: false,
497 }
498 }
499 #[must_use]
504 pub const fn between_closed(a: Self, b: Self) -> BetweenClosed {
505 Self::between_closed_coords(
506 a.0.x.min(b.0.x),
507 a.0.y.min(b.0.y),
508 a.0.z.min(b.0.z),
509 a.0.x.max(b.0.x),
510 a.0.y.max(b.0.y),
511 a.0.z.max(b.0.z),
512 )
513 }
514
515 #[must_use]
519 pub const fn between_closed_coords(
520 min_x: i32,
521 min_y: i32,
522 min_z: i32,
523 max_x: i32,
524 max_y: i32,
525 max_z: i32,
526 ) -> BetweenClosed {
527 let width = max_x - min_x + 1;
528 let height = max_y - min_y + 1;
529 let depth = max_z - min_z + 1;
530
531 BetweenClosed {
532 min_x,
533 min_y,
534 min_z,
535 width,
536 height,
537 index: 0,
538 end: width * height * depth,
539 }
540 }
541
542 #[must_use]
545 pub fn find_closest_match(
546 self,
547 horizontal_search_radius: i32,
548 vertical_search_radius: i32,
549 mut predicate: impl FnMut(BlockPos) -> bool,
550 ) -> Option<BlockPos> {
551 self.within_manhattan(
552 horizontal_search_radius,
553 vertical_search_radius,
554 horizontal_search_radius,
555 )
556 .find(|pos| predicate(*pos))
557 }
558}
559
560#[derive(Debug, Clone)]
562pub struct BlockPosWithinManhattan {
563 origin: BlockPos,
564 reach_x: i32,
565 reach_y: i32,
566 reach_z: i32,
567 max_depth: i32,
568 current_depth: i32,
569 max_x: i32,
570 max_y: i32,
571 x: i32,
572 y: i32,
573 pending_z_mirror: Option<BlockPos>,
574 done: bool,
575}
576
577impl Iterator for BlockPosWithinManhattan {
578 type Item = BlockPos;
579
580 fn next(&mut self) -> Option<Self::Item> {
581 if let Some(pos) = self.pending_z_mirror.take() {
582 return Some(pos);
583 }
584 if self.done {
585 return None;
586 }
587
588 loop {
589 if self.y > self.max_y {
590 self.x += 1;
591 if self.x > self.max_x {
592 self.current_depth += 1;
593 if self.current_depth > self.max_depth {
594 self.done = true;
595 return None;
596 }
597
598 self.max_x = self.reach_x.min(self.current_depth);
599 self.x = -self.max_x;
600 }
601
602 self.max_y = self.reach_y.min(self.current_depth - self.x.abs());
603 self.y = -self.max_y;
604 }
605
606 let x = self.x;
607 let y = self.y;
608 let z = self.current_depth - x.abs() - y.abs();
609 self.y += 1;
610 if z > self.reach_z {
611 continue;
612 }
613
614 let pos = self.origin.offset(x, y, z);
615 if z != 0 {
616 self.pending_z_mirror = Some(self.origin.offset(x, y, -z));
617 }
618 return Some(pos);
619 }
620 }
621}
622
623impl ReadFrom for BlockPos {
624 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
625 let packed = <i64 as ReadFrom>::read(data)?;
626 Ok(PackedBlockPos::from_raw(packed).into())
627 }
628}
629
630#[derive(Debug, Clone, PartialEq, Eq, Hash)]
632pub struct GlobalPos {
633 pub dimension: Identifier,
635 pub pos: BlockPos,
637}
638
639impl GlobalPos {
640 #[must_use]
642 pub const fn new(dimension: Identifier, pos: BlockPos) -> Self {
643 Self { dimension, pos }
644 }
645}
646
647impl ReadFrom for GlobalPos {
648 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
649 Ok(Self {
650 dimension: <Identifier as ReadFrom>::read(data)?,
651 pos: BlockPos::read(data)?,
652 })
653 }
654}
655
656impl WriteTo for GlobalPos {
657 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
658 self.dimension.write(writer)?;
659 self.pos.write(writer)
660 }
661}
662
663#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
665pub struct SectionPos(pub IVec3);
666
667impl SectionPos {
668 const SECTION_BITS: i32 = 4;
669 const SECTION_SIZE: i32 = 1 << Self::SECTION_BITS; pub(super) const SECTION_MASK: i32 = Self::SECTION_SIZE - 1; #[must_use]
674 pub const fn new(x: i32, y: i32, z: i32) -> Self {
675 Self(IVec3::new(x, y, z))
676 }
677
678 #[must_use]
680 #[inline]
681 pub const fn block_to_section_coord(block_coord: i32) -> i32 {
682 block_coord >> Self::SECTION_BITS
683 }
684
685 #[must_use]
687 pub const fn from_block_pos(pos: BlockPos) -> Self {
688 Self::new(
689 Self::block_to_section_coord(pos.0.x),
690 Self::block_to_section_coord(pos.0.y),
691 Self::block_to_section_coord(pos.0.z),
692 )
693 }
694
695 #[must_use]
697 pub fn from_entity_pos(pos: DVec3) -> Self {
698 Self::from_block_pos(BlockPos::from(pos))
699 }
700
701 #[must_use]
703 pub const fn x(&self) -> i32 {
704 self.0.x
705 }
706
707 #[must_use]
709 pub const fn y(&self) -> i32 {
710 self.0.y
711 }
712
713 #[must_use]
715 pub const fn z(&self) -> i32 {
716 self.0.z
717 }
718
719 #[must_use]
721 pub const fn relative_to_block_x(&self, relative: PackedSectionBlockPos) -> i32 {
722 (self.0.x << Self::SECTION_BITS) + relative.x() as i32
723 }
724
725 #[must_use]
727 pub const fn relative_to_block_y(&self, relative: PackedSectionBlockPos) -> i32 {
728 (self.0.y << Self::SECTION_BITS) + relative.y() as i32
729 }
730
731 #[must_use]
733 pub const fn relative_to_block_z(&self, relative: PackedSectionBlockPos) -> i32 {
734 (self.0.z << Self::SECTION_BITS) + relative.z() as i32
735 }
736
737 #[must_use]
740 #[inline]
741 pub const fn section_relative_pos(pos: BlockPos) -> PackedSectionBlockPos {
742 PackedSectionBlockPos::from_block_pos(pos)
743 }
744
745 #[must_use]
747 pub const fn relative_to_block_pos(&self, relative: PackedSectionBlockPos) -> BlockPos {
748 BlockPos(IVec3::new(
749 self.relative_to_block_x(relative),
750 self.relative_to_block_y(relative),
751 self.relative_to_block_z(relative),
752 ))
753 }
754}
755
756impl ReadFrom for SectionPos {
757 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
758 Ok(<PackedSectionPos as ReadFrom>::read(data)?.into())
759 }
760}
761
762impl WriteTo for SectionPos {
763 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
764 PackedSectionPos::from(*self).write(writer)
765 }
766}
767
768#[cfg(test)]
769mod tests {
770 use glam::DVec3;
771
772 use super::BlockPos;
773
774 #[test]
775 fn closer_to_center_than_uses_block_center() {
776 let pos = BlockPos::new(0, 0, 0);
777 assert!(pos.closer_to_center_than(DVec3::new(0.5, 0.5, 0.5), 1.0));
778 assert!(!pos.closer_to_center_than(DVec3::new(1.5, 0.5, 0.5), 1.0));
779 }
780
781 #[test]
782 fn closer_than_compares_integer_offsets_strictly() {
783 let pos = BlockPos::new(0, 64, 0);
784 assert!(pos.closer_than(BlockPos::new(3, 64, 3), 5.0));
785 assert!(!pos.closer_than(BlockPos::new(3, 64, 4), 5.0));
786 assert!(pos.closer_than(BlockPos::new(-3, 60, 0), 5.1));
787 }
788}