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
153impl From<DVec3> for BlockPos {
154 fn from(value: DVec3) -> Self {
155 BlockPos(IVec3 {
156 x: value.x.floor() as i32,
157 y: value.y.floor() as i32,
158 z: value.z.floor() as i32,
159 })
160 }
161}
162
163impl BlockPos {
164 pub const ZERO: BlockPos = BlockPos(IVec3::new(0, 0, 0));
165
166 pub const MAX_HORIZONTAL_COORDINATE: i32 = (1 << PackedBlockPos::HORIZONTAL_BITS) / 2 - 1;
168
169 #[must_use]
171 pub const fn new(x: i32, y: i32, z: i32) -> Self {
172 Self(IVec3::new(x, y, z))
173 }
174
175 #[must_use]
177 pub const fn offset(&self, dx: i32, dy: i32, dz: i32) -> Self {
178 Self(IVec3::new(self.0.x + dx, self.0.y + dy, self.0.z + dz))
179 }
180
181 #[must_use]
183 pub const fn x(&self) -> i32 {
184 self.0.x
185 }
186
187 #[must_use]
189 pub const fn y(&self) -> i32 {
190 self.0.y
191 }
192
193 #[must_use]
195 pub const fn z(&self) -> i32 {
196 self.0.z
197 }
198
199 #[must_use]
201 pub const fn above(&self) -> Self {
202 self.offset(0, 1, 0)
203 }
204
205 #[must_use]
207 pub const fn above_n(&self, n: i32) -> Self {
208 self.offset(0, n, 0)
209 }
210
211 #[must_use]
213 pub const fn below(&self) -> Self {
214 self.offset(0, -1, 0)
215 }
216
217 #[must_use]
219 pub const fn below_n(&self, n: i32) -> Self {
220 self.offset(0, -n, 0)
221 }
222
223 #[must_use]
225 pub const fn north(&self) -> Self {
226 self.offset(0, 0, -1)
227 }
228
229 #[must_use]
231 pub const fn south(&self) -> Self {
232 self.offset(0, 0, 1)
233 }
234
235 #[must_use]
237 pub const fn west(&self) -> Self {
238 self.offset(-1, 0, 0)
239 }
240
241 #[must_use]
243 pub const fn east(&self) -> Self {
244 self.offset(1, 0, 0)
245 }
246
247 #[must_use]
249 pub fn relative(self, direction: Direction) -> Self {
250 Self(self.0 + direction.offset_vec())
251 }
252
253 #[must_use]
255 pub fn breadth_first_traversal<NP, P>(
256 start_pos: Self,
257 max_depth: i32,
258 max_count: i32,
259 mut neighbor_provider: NP,
260 mut node_processor: P,
261 ) -> i32
262 where
263 NP: FnMut(Self, &mut dyn FnMut(Self)),
264 P: FnMut(Self) -> TraversalNodeStatus,
265 {
266 let mut nodes = VecDeque::from([(start_pos, 0)]);
267 let mut visited = FxHashSet::default();
268 let mut count = 0;
269
270 while let Some((current_pos, depth)) = nodes.pop_front() {
271 if !visited.insert(current_pos) {
272 continue;
273 }
274
275 let next = node_processor(current_pos);
276 if next == TraversalNodeStatus::Skip {
277 continue;
278 }
279
280 if next == TraversalNodeStatus::Stop {
281 break;
282 }
283
284 count += 1;
285 if count >= max_count {
286 return count;
287 }
288
289 if depth < max_depth {
290 let next_depth = depth + 1;
291 neighbor_provider(current_pos, &mut |pos| nodes.push_back((pos, next_depth)));
292 }
293 }
294
295 count
296 }
297
298 #[must_use]
300 pub fn relative_n(&self, direction: Direction, n: i32) -> Self {
301 if n == 0 {
302 *self
303 } else {
304 Self(self.0 + direction.offset_vec() * n)
305 }
306 }
307
308 #[must_use]
314 pub fn spiral_around(
315 center: Self,
316 radius: i32,
317 first_direction: Direction,
318 second_direction: Direction,
319 ) -> SpiralAround {
320 assert!(radius >= 0, "spiral radius must be non-negative");
321 assert!(
322 first_direction.get_axis() != second_direction.get_axis(),
323 "spiral directions cannot be on the same axis"
324 );
325
326 let cursor = center.relative(second_direction);
327 SpiralAround {
328 directions: [
329 first_direction,
330 second_direction,
331 first_direction.opposite(),
332 second_direction.opposite(),
333 ],
334 cursor,
335 legs: 4 * radius,
336 leg: -1,
337 leg_size: 0,
338 leg_index: 0,
339 last: cursor,
340 }
341 }
342
343 #[must_use]
345 pub const fn relative_axis(&self, axis: Axis, n: i32) -> Self {
346 if n == 0 {
347 *self
348 } else {
349 match axis {
350 Axis::X => self.offset(n, 0, 0),
351 Axis::Y => self.offset(0, n, 0),
352 Axis::Z => self.offset(0, 0, n),
353 }
354 }
355 }
356
357 #[must_use]
359 pub const fn at_y(&self, y: i32) -> Self {
360 Self::new(self.0.x, y, self.0.z)
361 }
362
363 #[must_use]
365 pub const fn multiply(&self, factor: i32) -> Self {
366 if factor == 1 {
367 *self
368 } else if factor == 0 {
369 Self::ZERO
370 } else {
371 Self::new(self.0.x * factor, self.0.y * factor, self.0.z * factor)
372 }
373 }
374
375 #[must_use]
377 pub fn get_center(&self) -> (f64, f64, f64) {
378 (
379 f64::from(self.0.x) + 0.5,
380 f64::from(self.0.y) + 0.5,
381 f64::from(self.0.z) + 0.5,
382 )
383 }
384
385 #[must_use]
387 pub fn get_bottom_center(&self) -> (f64, f64, f64) {
388 (
389 f64::from(self.0.x) + 0.5,
390 f64::from(self.0.y),
391 f64::from(self.0.z) + 0.5,
392 )
393 }
394
395 #[must_use]
397 pub const fn containing(x: f64, y: f64, z: f64) -> Self {
398 Self::new(x.floor() as i32, y.floor() as i32, z.floor() as i32)
399 }
400
401 #[must_use]
403 pub const fn min(a: BlockPos, b: BlockPos) -> Self {
404 Self::new(a.0.x.min(b.0.x), a.0.y.min(b.0.y), a.0.z.min(b.0.z))
405 }
406
407 #[must_use]
409 pub const fn max(a: BlockPos, b: BlockPos) -> Self {
410 Self::new(a.0.x.max(b.0.x), a.0.y.max(b.0.y), a.0.z.max(b.0.z))
411 }
412
413 #[must_use]
415 pub const fn within_manhattan(
416 self,
417 reach_x: i32,
418 reach_y: i32,
419 reach_z: i32,
420 ) -> BlockPosWithinManhattan {
421 BlockPosWithinManhattan {
422 origin: self,
423 reach_x,
424 reach_y,
425 reach_z,
426 max_depth: reach_x + reach_y + reach_z,
427 current_depth: 0,
428 max_x: 0,
429 max_y: 0,
430 x: 0,
431 y: 0,
432 pending_z_mirror: None,
433 done: false,
434 }
435 }
436
437 #[must_use]
439 pub fn find_closest_match(
440 self,
441 horizontal_search_radius: i32,
442 vertical_search_radius: i32,
443 mut predicate: impl FnMut(BlockPos) -> bool,
444 ) -> Option<BlockPos> {
445 self.within_manhattan(
446 horizontal_search_radius,
447 vertical_search_radius,
448 horizontal_search_radius,
449 )
450 .find(|pos| predicate(*pos))
451 }
452}
453
454#[derive(Debug, Clone)]
456pub struct BlockPosWithinManhattan {
457 origin: BlockPos,
458 reach_x: i32,
459 reach_y: i32,
460 reach_z: i32,
461 max_depth: i32,
462 current_depth: i32,
463 max_x: i32,
464 max_y: i32,
465 x: i32,
466 y: i32,
467 pending_z_mirror: Option<BlockPos>,
468 done: bool,
469}
470
471impl Iterator for BlockPosWithinManhattan {
472 type Item = BlockPos;
473
474 fn next(&mut self) -> Option<Self::Item> {
475 if let Some(pos) = self.pending_z_mirror.take() {
476 return Some(pos);
477 }
478 if self.done {
479 return None;
480 }
481
482 loop {
483 if self.y > self.max_y {
484 self.x += 1;
485 if self.x > self.max_x {
486 self.current_depth += 1;
487 if self.current_depth > self.max_depth {
488 self.done = true;
489 return None;
490 }
491
492 self.max_x = self.reach_x.min(self.current_depth);
493 self.x = -self.max_x;
494 }
495
496 self.max_y = self.reach_y.min(self.current_depth - self.x.abs());
497 self.y = -self.max_y;
498 }
499
500 let x = self.x;
501 let y = self.y;
502 let z = self.current_depth - x.abs() - y.abs();
503 self.y += 1;
504 if z > self.reach_z {
505 continue;
506 }
507
508 let pos = self.origin.offset(x, y, z);
509 if z != 0 {
510 self.pending_z_mirror = Some(self.origin.offset(x, y, -z));
511 }
512 return Some(pos);
513 }
514 }
515}
516
517impl ReadFrom for BlockPos {
518 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
519 let packed = <i64 as ReadFrom>::read(data)?;
520 Ok(PackedBlockPos::from_raw(packed).into())
521 }
522}
523
524#[derive(Debug, Clone, PartialEq, Eq, Hash)]
526pub struct GlobalPos {
527 pub dimension: Identifier,
529 pub pos: BlockPos,
531}
532
533impl GlobalPos {
534 #[must_use]
536 pub const fn new(dimension: Identifier, pos: BlockPos) -> Self {
537 Self { dimension, pos }
538 }
539}
540
541impl ReadFrom for GlobalPos {
542 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
543 Ok(Self {
544 dimension: <Identifier as ReadFrom>::read(data)?,
545 pos: BlockPos::read(data)?,
546 })
547 }
548}
549
550impl WriteTo for GlobalPos {
551 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
552 self.dimension.write(writer)?;
553 self.pos.write(writer)
554 }
555}
556
557#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
559pub struct SectionPos(pub IVec3);
560
561impl SectionPos {
562 const SECTION_BITS: i32 = 4;
563 const SECTION_SIZE: i32 = 1 << Self::SECTION_BITS; pub(super) const SECTION_MASK: i32 = Self::SECTION_SIZE - 1; #[must_use]
568 pub const fn new(x: i32, y: i32, z: i32) -> Self {
569 Self(IVec3::new(x, y, z))
570 }
571
572 #[must_use]
574 #[inline]
575 pub const fn block_to_section_coord(block_coord: i32) -> i32 {
576 block_coord >> Self::SECTION_BITS
577 }
578
579 #[must_use]
581 pub const fn from_block_pos(pos: BlockPos) -> Self {
582 Self::new(
583 Self::block_to_section_coord(pos.0.x),
584 Self::block_to_section_coord(pos.0.y),
585 Self::block_to_section_coord(pos.0.z),
586 )
587 }
588
589 #[must_use]
591 pub fn from_entity_pos(pos: DVec3) -> Self {
592 Self::from_block_pos(BlockPos::from(pos))
593 }
594
595 #[must_use]
597 pub const fn x(&self) -> i32 {
598 self.0.x
599 }
600
601 #[must_use]
603 pub const fn y(&self) -> i32 {
604 self.0.y
605 }
606
607 #[must_use]
609 pub const fn z(&self) -> i32 {
610 self.0.z
611 }
612
613 #[must_use]
615 pub const fn relative_to_block_x(&self, relative: PackedSectionBlockPos) -> i32 {
616 (self.0.x << Self::SECTION_BITS) + relative.x() as i32
617 }
618
619 #[must_use]
621 pub const fn relative_to_block_y(&self, relative: PackedSectionBlockPos) -> i32 {
622 (self.0.y << Self::SECTION_BITS) + relative.y() as i32
623 }
624
625 #[must_use]
627 pub const fn relative_to_block_z(&self, relative: PackedSectionBlockPos) -> i32 {
628 (self.0.z << Self::SECTION_BITS) + relative.z() as i32
629 }
630
631 #[must_use]
634 #[inline]
635 pub const fn section_relative_pos(pos: BlockPos) -> PackedSectionBlockPos {
636 PackedSectionBlockPos::from_block_pos(pos)
637 }
638
639 #[must_use]
641 pub const fn relative_to_block_pos(&self, relative: PackedSectionBlockPos) -> BlockPos {
642 BlockPos(IVec3::new(
643 self.relative_to_block_x(relative),
644 self.relative_to_block_y(relative),
645 self.relative_to_block_z(relative),
646 ))
647 }
648}
649
650impl ReadFrom for SectionPos {
651 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
652 Ok(<PackedSectionPos as ReadFrom>::read(data)?.into())
653 }
654}
655
656impl WriteTo for SectionPos {
657 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
658 PackedSectionPos::from(*self).write(writer)
659 }
660}