1#![expect(missing_docs, reason = "self-explanatory utility types")]
2
3use std::{
4 borrow::Cow,
5 error::Error,
6 fmt::{self, Debug, Display, Formatter},
7 hash::{Hash, Hasher},
8 io::{self, Cursor, Write},
9 mem::MaybeUninit,
10 str::FromStr,
11};
12
13use bitflags::bitflags;
14use glam::{DVec3, IVec2, IVec3};
15use serde::{Deserialize, Serialize, de::Error as _};
16use simdnbt::owned::{NbtCompound, NbtTag};
17use wincode::{SchemaRead, SchemaWrite, config::Config, io::Reader, io::Writer};
18
19use crate::{
20 axis::Axis,
21 codec::VarInt,
22 direction::Direction,
23 hash::{ComponentHasher, HashComponent},
24 serial::{ReadFrom, WriteTo},
25};
26
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
30pub struct Todo;
31
32impl WriteTo for Todo {
33 fn write(&self, _writer: &mut impl Write) -> io::Result<()> {
34 Ok(())
36 }
37}
38
39impl ReadFrom for Todo {
40 fn read(_data: &mut Cursor<&[u8]>) -> io::Result<Self> {
41 Ok(Todo)
43 }
44}
45
46impl HashComponent for Todo {
47 fn hash_component(&self, hasher: &mut ComponentHasher) {
48 hasher.put_empty();
50 }
51}
52
53impl simdnbt::ToNbtTag for Todo {
54 fn to_nbt_tag(self) -> NbtTag {
55 NbtTag::Compound(NbtCompound::new())
57 }
58}
59
60impl simdnbt::FromNbtTag for Todo {
61 fn from_nbt_tag(_tag: simdnbt::borrow::NbtTag) -> Option<Self> {
62 Some(Todo)
64 }
65}
66
67impl HashComponent for Identifier {
68 fn hash_component(&self, hasher: &mut ComponentHasher) {
69 hasher.put_string(&self.to_string());
71 }
72}
73
74impl simdnbt::ToNbtTag for Identifier {
75 fn to_nbt_tag(self) -> NbtTag {
76 NbtTag::String(self.to_string().into())
77 }
78}
79
80impl simdnbt::FromNbtTag for Identifier {
81 fn from_nbt_tag(tag: simdnbt::borrow::NbtTag) -> Option<Self> {
82 let s = tag.string()?.to_str();
83 s.parse().ok()
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
89pub struct BlockStateId(pub u16);
90
91impl WriteTo for BlockStateId {
92 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
93 VarInt(i32::from(self.0)).write(writer)
94 }
95}
96
97impl ReadFrom for BlockStateId {
98 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
99 let id = VarInt::read(data)?.0;
100 #[expect(
101 clippy::cast_sign_loss,
102 reason = "VarInt is validated upstream; block state IDs are non-negative"
103 )]
104 Ok(Self(id as u16))
105 }
106}
107
108#[derive(Debug, Clone, Copy, PartialEq, Eq)]
110pub struct ChunkPos(pub IVec2);
111
112impl Hash for ChunkPos {
113 fn hash<H: Hasher>(&self, state: &mut H) {
114 state.write_u64(PackedChunkPos::from(*self).as_raw() as u64);
115 }
116}
117
118impl ChunkPos {
119 const OFFSETS: [(i32, i32); 8] = [
120 (-1, -1),
121 (0, -1),
122 (1, -1),
123 (-1, 0),
124 (1, 0),
125 (-1, 1),
126 (0, 1),
127 (1, 1),
128 ];
129
130 const SAFETY_MARGIN_CHUNKS: i32 = (32 + 12 + 1) * 2;
134
135 pub const MAX_COORDINATE_VALUE: i32 =
138 SectionPos::block_to_section_coord(BlockPos::MAX_HORIZONTAL_COORDINATE)
139 - Self::SAFETY_MARGIN_CHUNKS;
140
141 #[must_use]
143 pub fn neighbors(self) -> [ChunkPos; 8] {
144 Self::OFFSETS.map(|(dx, dy)| ChunkPos::new(self.0.x + dx, self.0.y + dy))
145 }
146
147 #[must_use]
148 #[inline]
149 pub const fn new(x: i32, y: i32) -> Self {
151 Self(IVec2::new(x, y))
152 }
153
154 #[must_use]
156 pub const fn from_block_pos(pos: BlockPos) -> Self {
157 Self::new(
158 SectionPos::block_to_section_coord(pos.0.x),
159 SectionPos::block_to_section_coord(pos.0.z),
160 )
161 }
162
163 #[must_use]
165 pub fn from_entity_pos(pos: DVec3) -> Self {
166 Self::from_block_pos(BlockPos::from(pos))
167 }
168
169 #[must_use]
172 #[inline]
173 pub const fn is_valid(x: i32, z: i32) -> bool {
174 x.abs().max(z.abs()) <= Self::MAX_COORDINATE_VALUE
175 }
176}
177
178impl WriteTo for ChunkPos {
179 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
180 self.0.write(writer)
181 }
182}
183
184impl ReadFrom for ChunkPos {
185 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
186 Ok(Self(IVec2::read(data)?))
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
192pub struct BlockPos(pub IVec3);
193
194impl From<DVec3> for BlockPos {
195 fn from(value: DVec3) -> Self {
196 BlockPos(IVec3 {
197 x: value.x.floor() as i32,
198 y: value.y.floor() as i32,
199 z: value.z.floor() as i32,
200 })
201 }
202}
203
204impl BlockPos {
205 pub const ZERO: BlockPos = BlockPos(IVec3::new(0, 0, 0));
206
207 pub const MAX_HORIZONTAL_COORDINATE: i32 = (1 << PackedBlockPos::HORIZONTAL_BITS) / 2 - 1;
209
210 #[must_use]
212 pub const fn new(x: i32, y: i32, z: i32) -> Self {
213 Self(IVec3::new(x, y, z))
214 }
215
216 #[must_use]
218 pub const fn offset(&self, dx: i32, dy: i32, dz: i32) -> Self {
219 Self(IVec3::new(self.0.x + dx, self.0.y + dy, self.0.z + dz))
220 }
221
222 #[must_use]
224 pub const fn x(&self) -> i32 {
225 self.0.x
226 }
227
228 #[must_use]
230 pub const fn y(&self) -> i32 {
231 self.0.y
232 }
233
234 #[must_use]
236 pub const fn z(&self) -> i32 {
237 self.0.z
238 }
239
240 #[must_use]
242 pub const fn above(&self) -> Self {
243 self.offset(0, 1, 0)
244 }
245
246 #[must_use]
248 pub const fn above_n(&self, n: i32) -> Self {
249 self.offset(0, n, 0)
250 }
251
252 #[must_use]
254 pub const fn below(&self) -> Self {
255 self.offset(0, -1, 0)
256 }
257
258 #[must_use]
260 pub const fn below_n(&self, n: i32) -> Self {
261 self.offset(0, -n, 0)
262 }
263
264 #[must_use]
266 pub const fn north(&self) -> Self {
267 self.offset(0, 0, -1)
268 }
269
270 #[must_use]
272 pub const fn north_n(&self, n: i32) -> Self {
273 self.offset(0, 0, -n)
274 }
275
276 #[must_use]
278 pub const fn south(&self) -> Self {
279 self.offset(0, 0, 1)
280 }
281
282 #[must_use]
284 pub const fn south_n(&self, n: i32) -> Self {
285 self.offset(0, 0, n)
286 }
287
288 #[must_use]
290 pub const fn west(&self) -> Self {
291 self.offset(-1, 0, 0)
292 }
293
294 #[must_use]
296 pub const fn west_n(&self, n: i32) -> Self {
297 self.offset(-n, 0, 0)
298 }
299
300 #[must_use]
302 pub const fn east(&self) -> Self {
303 self.offset(1, 0, 0)
304 }
305
306 #[must_use]
308 pub const fn east_n(&self, n: i32) -> Self {
309 self.offset(n, 0, 0)
310 }
311
312 #[must_use]
314 pub const fn relative(self, direction: Direction) -> Self {
315 let (dx, dy, dz) = direction.offset();
316 self.offset(dx, dy, dz)
317 }
318
319 #[must_use]
321 pub const fn relative_n(&self, direction: Direction, n: i32) -> Self {
322 if n == 0 {
323 *self
324 } else {
325 let (dx, dy, dz) = direction.offset();
326 self.offset(dx * n, dy * n, dz * n)
327 }
328 }
329
330 #[must_use]
332 pub const fn relative_axis(&self, axis: Axis, n: i32) -> Self {
333 if n == 0 {
334 *self
335 } else {
336 match axis {
337 Axis::X => self.offset(n, 0, 0),
338 Axis::Y => self.offset(0, n, 0),
339 Axis::Z => self.offset(0, 0, n),
340 }
341 }
342 }
343
344 #[must_use]
346 pub const fn at_y(&self, y: i32) -> Self {
347 Self::new(self.0.x, y, self.0.z)
348 }
349
350 #[must_use]
352 pub const fn multiply(&self, factor: i32) -> Self {
353 if factor == 1 {
354 *self
355 } else if factor == 0 {
356 Self::ZERO
357 } else {
358 Self::new(self.0.x * factor, self.0.y * factor, self.0.z * factor)
359 }
360 }
361
362 #[must_use]
364 pub fn get_center(&self) -> (f64, f64, f64) {
365 (
366 f64::from(self.0.x) + 0.5,
367 f64::from(self.0.y) + 0.5,
368 f64::from(self.0.z) + 0.5,
369 )
370 }
371
372 #[must_use]
374 pub fn get_bottom_center(&self) -> (f64, f64, f64) {
375 (
376 f64::from(self.0.x) + 0.5,
377 f64::from(self.0.y),
378 f64::from(self.0.z) + 0.5,
379 )
380 }
381
382 #[must_use]
384 pub const fn containing(x: f64, y: f64, z: f64) -> Self {
385 Self::new(x.floor() as i32, y.floor() as i32, z.floor() as i32)
386 }
387
388 #[must_use]
390 pub const fn min(a: BlockPos, b: BlockPos) -> Self {
391 Self::new(a.0.x.min(b.0.x), a.0.y.min(b.0.y), a.0.z.min(b.0.z))
392 }
393
394 #[must_use]
396 pub const fn max(a: BlockPos, b: BlockPos) -> Self {
397 Self::new(a.0.x.max(b.0.x), a.0.y.max(b.0.y), a.0.z.max(b.0.z))
398 }
399}
400
401impl ReadFrom for BlockPos {
402 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
403 let packed = <i64 as ReadFrom>::read(data)?;
404 Ok(PackedBlockPos::from_raw(packed).into())
405 }
406}
407
408#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
410pub struct SectionPos(pub IVec3);
411
412impl SectionPos {
413 const SECTION_BITS: i32 = 4;
414 const SECTION_SIZE: i32 = 1 << Self::SECTION_BITS; const SECTION_MASK: i32 = Self::SECTION_SIZE - 1; #[must_use]
419 pub const fn new(x: i32, y: i32, z: i32) -> Self {
420 Self(IVec3::new(x, y, z))
421 }
422
423 #[must_use]
425 #[inline]
426 pub const fn block_to_section_coord(block_coord: i32) -> i32 {
427 block_coord >> Self::SECTION_BITS
428 }
429
430 #[must_use]
432 pub const fn from_block_pos(pos: BlockPos) -> Self {
433 Self::new(
434 Self::block_to_section_coord(pos.0.x),
435 Self::block_to_section_coord(pos.0.y),
436 Self::block_to_section_coord(pos.0.z),
437 )
438 }
439
440 #[must_use]
442 pub fn from_entity_pos(pos: DVec3) -> Self {
443 Self::from_block_pos(BlockPos::from(pos))
444 }
445
446 #[must_use]
448 pub const fn x(&self) -> i32 {
449 self.0.x
450 }
451
452 #[must_use]
454 pub const fn y(&self) -> i32 {
455 self.0.y
456 }
457
458 #[must_use]
460 pub const fn z(&self) -> i32 {
461 self.0.z
462 }
463
464 #[must_use]
466 pub const fn relative_to_block_x(&self, relative: PackedSectionBlockPos) -> i32 {
467 (self.0.x << Self::SECTION_BITS) + relative.x() as i32
468 }
469
470 #[must_use]
472 pub const fn relative_to_block_y(&self, relative: PackedSectionBlockPos) -> i32 {
473 (self.0.y << Self::SECTION_BITS) + relative.y() as i32
474 }
475
476 #[must_use]
478 pub const fn relative_to_block_z(&self, relative: PackedSectionBlockPos) -> i32 {
479 (self.0.z << Self::SECTION_BITS) + relative.z() as i32
480 }
481
482 #[must_use]
485 #[inline]
486 pub const fn section_relative_pos(pos: BlockPos) -> PackedSectionBlockPos {
487 PackedSectionBlockPos::from_block_pos(pos)
488 }
489
490 #[must_use]
492 pub const fn relative_to_block_pos(&self, relative: PackedSectionBlockPos) -> BlockPos {
493 BlockPos(IVec3::new(
494 self.relative_to_block_x(relative),
495 self.relative_to_block_y(relative),
496 self.relative_to_block_z(relative),
497 ))
498 }
499}
500
501#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SchemaWrite, SchemaRead)]
503pub struct PackedChunkPos(i64);
504
505impl PackedChunkPos {
506 #[must_use]
508 pub const fn from_raw(raw: i64) -> Self {
509 Self(raw)
510 }
511
512 #[must_use]
514 pub const fn as_raw(self) -> i64 {
515 self.0
516 }
517
518 #[must_use]
520 pub const fn to_chunk_pos(self) -> ChunkPos {
521 ChunkPos(IVec2::new(
522 (self.0 & 0xFFFF_FFFF) as i32,
523 (self.0 >> 32) as i32,
524 ))
525 }
526}
527
528impl From<ChunkPos> for PackedChunkPos {
529 fn from(pos: ChunkPos) -> Self {
530 Self((i64::from(pos.0.x) & 0xFFFF_FFFF) | ((i64::from(pos.0.y) & 0xFFFF_FFFF) << 32))
531 }
532}
533
534impl From<PackedChunkPos> for ChunkPos {
535 fn from(pos: PackedChunkPos) -> Self {
536 pos.to_chunk_pos()
537 }
538}
539
540impl ReadFrom for PackedChunkPos {
541 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
542 Ok(Self::from_raw(<i64 as ReadFrom>::read(data)?))
543 }
544}
545
546impl WriteTo for PackedChunkPos {
547 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
548 self.0.write(writer)
549 }
550}
551
552#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SchemaWrite, SchemaRead)]
554pub struct PackedBlockPos(i64);
555
556impl PackedBlockPos {
557 const HORIZONTAL_BITS: u32 = 26;
558 const Y_BITS: u32 = 12;
559 const X_OFFSET: u32 = Self::HORIZONTAL_BITS + Self::Y_BITS;
560 const Z_OFFSET: u32 = Self::Y_BITS;
561 const XZ_MASK: i64 = (1i64 << Self::HORIZONTAL_BITS) - 1;
562 const Y_MASK: i64 = (1i64 << Self::Y_BITS) - 1;
563
564 #[must_use]
566 pub const fn from_raw(raw: i64) -> Self {
567 Self(raw)
568 }
569
570 #[must_use]
572 pub const fn as_raw(self) -> i64 {
573 self.0
574 }
575
576 #[must_use]
578 pub const fn to_block_pos(self) -> BlockPos {
579 let x = self.0 >> Self::X_OFFSET;
580 let y = self.0 & Self::Y_MASK;
581 let z = (self.0 >> Self::Z_OFFSET) & Self::XZ_MASK;
582
583 let x = (x << (64 - Self::HORIZONTAL_BITS)) >> (64 - Self::HORIZONTAL_BITS);
584 let y = (y << (64 - Self::Y_BITS)) >> (64 - Self::Y_BITS);
585 let z = (z << (64 - Self::HORIZONTAL_BITS)) >> (64 - Self::HORIZONTAL_BITS);
586
587 BlockPos(IVec3::new(x as i32, y as i32, z as i32))
588 }
589}
590
591impl From<BlockPos> for PackedBlockPos {
592 fn from(pos: BlockPos) -> Self {
593 let x = i64::from(pos.0.x);
594 let y = i64::from(pos.0.y);
595 let z = i64::from(pos.0.z);
596 Self(
597 ((x & Self::XZ_MASK) << Self::X_OFFSET)
598 | ((z & Self::XZ_MASK) << Self::Z_OFFSET)
599 | (y & Self::Y_MASK),
600 )
601 }
602}
603
604impl From<PackedBlockPos> for BlockPos {
605 fn from(pos: PackedBlockPos) -> Self {
606 pos.to_block_pos()
607 }
608}
609
610impl ReadFrom for PackedBlockPos {
611 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
612 Ok(Self::from_raw(<i64 as ReadFrom>::read(data)?))
613 }
614}
615
616impl WriteTo for PackedBlockPos {
617 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
618 self.0.write(writer)
619 }
620}
621
622#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SchemaWrite, SchemaRead)]
624pub struct PackedSectionPos(i64);
625
626impl PackedSectionPos {
627 const XZ_BITS: u32 = 22;
628 const Y_BITS: u32 = 20;
629 const X_OFFSET: u32 = Self::XZ_BITS + Self::Y_BITS;
630 const Z_OFFSET: u32 = Self::Y_BITS;
631 const XZ_MASK: i64 = (1i64 << Self::XZ_BITS) - 1;
632 const Y_MASK: i64 = (1i64 << Self::Y_BITS) - 1;
633
634 #[must_use]
636 pub const fn from_raw(raw: i64) -> Self {
637 Self(raw)
638 }
639
640 #[must_use]
642 pub const fn as_raw(self) -> i64 {
643 self.0
644 }
645
646 #[must_use]
648 pub const fn to_section_pos(self) -> SectionPos {
649 let x = self.0 >> Self::X_OFFSET;
650 let z = (self.0 >> Self::Z_OFFSET) & Self::XZ_MASK;
651 let y = self.0 & Self::Y_MASK;
652
653 let x = (x << (64 - Self::XZ_BITS)) >> (64 - Self::XZ_BITS);
654 let y = (y << (64 - Self::Y_BITS)) >> (64 - Self::Y_BITS);
655 let z = (z << (64 - Self::XZ_BITS)) >> (64 - Self::XZ_BITS);
656
657 SectionPos(IVec3::new(x as i32, y as i32, z as i32))
658 }
659}
660
661impl From<SectionPos> for PackedSectionPos {
662 fn from(pos: SectionPos) -> Self {
663 let x = i64::from(pos.0.x);
664 let y = i64::from(pos.0.y);
665 let z = i64::from(pos.0.z);
666 Self(
667 ((x & Self::XZ_MASK) << Self::X_OFFSET)
668 | ((z & Self::XZ_MASK) << Self::Z_OFFSET)
669 | (y & Self::Y_MASK),
670 )
671 }
672}
673
674impl From<PackedSectionPos> for SectionPos {
675 fn from(pos: PackedSectionPos) -> Self {
676 pos.to_section_pos()
677 }
678}
679
680impl ReadFrom for PackedSectionPos {
681 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
682 Ok(Self::from_raw(<i64 as ReadFrom>::read(data)?))
683 }
684}
685
686impl WriteTo for PackedSectionPos {
687 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
688 self.0.write(writer)
689 }
690}
691
692#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SchemaWrite, SchemaRead)]
696pub struct PackedChunkLocalXZ(u8);
697
698impl PackedChunkLocalXZ {
699 const COORD_MASK: u8 = 0x0f;
700
701 #[must_use]
703 pub const fn from_block_pos(pos: BlockPos) -> Self {
704 Self::from_local_unchecked(
705 (pos.0.x & SectionPos::SECTION_MASK) as u8,
706 (pos.0.z & SectionPos::SECTION_MASK) as u8,
707 )
708 }
709
710 #[must_use]
712 pub const fn from_local_xz(x: u8, z: u8) -> Option<Self> {
713 if x < 16 && z < 16 {
714 Some(Self::from_local_unchecked(x, z))
715 } else {
716 None
717 }
718 }
719
720 #[must_use]
722 pub const fn from_raw(raw: u8) -> Self {
723 Self(raw)
724 }
725
726 #[must_use]
728 pub const fn as_u8(self) -> u8 {
729 self.0
730 }
731
732 #[must_use]
734 pub const fn x(self) -> u8 {
735 (self.0 >> 4) & Self::COORD_MASK
736 }
737
738 #[must_use]
740 pub const fn z(self) -> u8 {
741 self.0 & Self::COORD_MASK
742 }
743
744 const fn from_local_unchecked(x: u8, z: u8) -> Self {
745 Self((x << 4) | z)
746 }
747}
748
749impl From<BlockPos> for PackedChunkLocalXZ {
750 fn from(pos: BlockPos) -> Self {
751 Self::from_block_pos(pos)
752 }
753}
754
755impl ReadFrom for PackedChunkLocalXZ {
756 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
757 Ok(Self::from_raw(<u8 as ReadFrom>::read(data)?))
758 }
759}
760
761impl WriteTo for PackedChunkLocalXZ {
762 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
763 self.0.write(writer)
764 }
765}
766
767#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SchemaWrite, SchemaRead)]
771pub struct PackedSectionBlockPos(u16);
772
773impl PackedSectionBlockPos {
774 const COORD_MASK: u16 = 0x0f;
775 const RAW_MASK: u16 = 0x0fff;
776
777 #[must_use]
779 #[inline]
780 pub const fn from_block_pos(pos: BlockPos) -> Self {
781 Self::from_local_unchecked(
782 (pos.0.x & SectionPos::SECTION_MASK) as u8,
783 (pos.0.y & SectionPos::SECTION_MASK) as u8,
784 (pos.0.z & SectionPos::SECTION_MASK) as u8,
785 )
786 }
787
788 #[must_use]
790 pub const fn from_local_xyz(x: u8, y: u8, z: u8) -> Option<Self> {
791 if x < 16 && y < 16 && z < 16 {
792 Some(Self::from_local_unchecked(x, y, z))
793 } else {
794 None
795 }
796 }
797
798 #[must_use]
800 pub const fn from_raw(raw: u16) -> Option<Self> {
801 if raw & !Self::RAW_MASK == 0 {
802 Some(Self(raw))
803 } else {
804 None
805 }
806 }
807
808 #[must_use]
810 pub const fn as_u16(self) -> u16 {
811 self.0
812 }
813
814 #[must_use]
816 pub const fn x(self) -> u8 {
817 ((self.0 >> 8) & Self::COORD_MASK) as u8
818 }
819
820 #[must_use]
822 pub const fn y(self) -> u8 {
823 (self.0 & Self::COORD_MASK) as u8
824 }
825
826 #[must_use]
828 pub const fn z(self) -> u8 {
829 ((self.0 >> 4) & Self::COORD_MASK) as u8
830 }
831
832 #[must_use]
834 pub const fn to_block_pos(self, section_pos: SectionPos) -> BlockPos {
835 section_pos.relative_to_block_pos(self)
836 }
837
838 const fn from_local_unchecked(x: u8, y: u8, z: u8) -> Self {
839 Self(((x as u16) << 8) | ((z as u16) << 4) | y as u16)
840 }
841}
842
843impl From<BlockPos> for PackedSectionBlockPos {
844 fn from(pos: BlockPos) -> Self {
845 Self::from_block_pos(pos)
846 }
847}
848
849impl TryFrom<u16> for PackedSectionBlockPos {
850 type Error = InvalidPackedSectionBlockPos;
851
852 fn try_from(raw: u16) -> Result<Self, Self::Error> {
853 Self::from_raw(raw).ok_or(InvalidPackedSectionBlockPos { raw })
854 }
855}
856
857#[derive(Debug, Clone, Copy, PartialEq, Eq)]
859pub struct InvalidPackedSectionBlockPos {
860 raw: u16,
861}
862
863impl InvalidPackedSectionBlockPos {
864 #[must_use]
866 pub const fn raw(self) -> u16 {
867 self.raw
868 }
869}
870
871impl Display for InvalidPackedSectionBlockPos {
872 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
873 write!(
874 f,
875 "packed section block position {:#06x} uses reserved bits",
876 self.raw
877 )
878 }
879}
880
881impl Error for InvalidPackedSectionBlockPos {}
882
883impl ReadFrom for SectionPos {
884 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
885 Ok(<PackedSectionPos as ReadFrom>::read(data)?.into())
886 }
887}
888
889impl WriteTo for SectionPos {
890 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
891 PackedSectionPos::from(*self).write(writer)
892 }
893}
894
895#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SchemaWrite, SchemaRead)]
900pub struct BoundingBox {
901 pub min_x: i32,
903 pub min_y: i32,
905 pub min_z: i32,
907 pub max_x: i32,
909 pub max_y: i32,
911 pub max_z: i32,
913}
914
915impl BoundingBox {
916 #[must_use]
918 pub const fn new(x1: i32, y1: i32, z1: i32, x2: i32, y2: i32, z2: i32) -> Self {
919 Self {
920 min_x: x1.min(x2),
921 min_y: y1.min(y2),
922 min_z: z1.min(z2),
923 max_x: x1.max(x2),
924 max_y: y1.max(y2),
925 max_z: z1.max(z2),
926 }
927 }
928
929 #[must_use]
931 pub const fn from_corners(a: BlockPos, b: BlockPos) -> Self {
932 Self::new(a.0.x, a.0.y, a.0.z, b.0.x, b.0.y, b.0.z)
933 }
934
935 #[must_use]
937 pub const fn intersects(&self, other: &Self) -> bool {
938 self.max_x >= other.min_x
939 && self.min_x <= other.max_x
940 && self.max_z >= other.min_z
941 && self.min_z <= other.max_z
942 && self.max_y >= other.min_y
943 && self.min_y <= other.max_y
944 }
945
946 #[must_use]
948 pub const fn intersects_xz(&self, min_x: i32, min_z: i32, max_x: i32, max_z: i32) -> bool {
949 self.max_x >= min_x && self.min_x <= max_x && self.max_z >= min_z && self.min_z <= max_z
950 }
951
952 #[must_use]
954 pub const fn is_inside(&self, pos: BlockPos) -> bool {
955 self.contains_xyz(pos.0.x, pos.0.y, pos.0.z)
956 }
957
958 #[must_use]
960 pub const fn contains_xyz(&self, x: i32, y: i32, z: i32) -> bool {
961 x >= self.min_x
962 && x <= self.max_x
963 && z >= self.min_z
964 && z <= self.max_z
965 && y >= self.min_y
966 && y <= self.max_y
967 }
968
969 #[must_use]
971 pub const fn get_center(&self) -> BlockPos {
972 BlockPos(IVec3::new(
973 self.min_x + (self.max_x - self.min_x + 1) / 2,
974 self.min_y + (self.max_y - self.min_y + 1) / 2,
975 self.min_z + (self.max_z - self.min_z + 1) / 2,
976 ))
977 }
978
979 #[must_use]
981 pub const fn get_x_span(&self) -> i32 {
982 self.max_x - self.min_x + 1
983 }
984
985 #[must_use]
987 pub const fn get_y_span(&self) -> i32 {
988 self.max_y - self.min_y + 1
989 }
990
991 #[must_use]
993 pub const fn get_z_span(&self) -> i32 {
994 self.max_z - self.min_z + 1
995 }
996
997 #[must_use]
999 pub const fn encapsulating(a: &Self, b: &Self) -> Self {
1000 Self {
1001 min_x: a.min_x.min(b.min_x),
1002 min_y: a.min_y.min(b.min_y),
1003 min_z: a.min_z.min(b.min_z),
1004 max_x: a.max_x.max(b.max_x),
1005 max_y: a.max_y.max(b.max_y),
1006 max_z: a.max_z.max(b.max_z),
1007 }
1008 }
1009
1010 #[must_use]
1012 pub const fn moved(&self, dx: i32, dy: i32, dz: i32) -> Self {
1013 Self {
1014 min_x: self.min_x + dx,
1015 min_y: self.min_y + dy,
1016 min_z: self.min_z + dz,
1017 max_x: self.max_x + dx,
1018 max_y: self.max_y + dy,
1019 max_z: self.max_z + dz,
1020 }
1021 }
1022
1023 #[must_use]
1025 pub const fn inflated_by(&self, x: i32, y: i32, z: i32) -> Self {
1026 Self {
1027 min_x: self.min_x - x,
1028 min_y: self.min_y - y,
1029 min_z: self.min_z - z,
1030 max_x: self.max_x + x,
1031 max_y: self.max_y + y,
1032 max_z: self.max_z + z,
1033 }
1034 }
1035}
1036
1037#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1039#[expect(missing_docs, reason = "variant names are self-explanatory")]
1040pub enum GameType {
1041 Survival = 0,
1042 Creative = 1,
1043 Adventure = 2,
1044 Spectator = 3,
1045}
1046
1047impl GameType {
1048 #[must_use]
1050 pub const fn name(self) -> &'static str {
1051 match self {
1052 GameType::Survival => "survival",
1053 GameType::Creative => "creative",
1054 GameType::Adventure => "adventure",
1055 GameType::Spectator => "spectator",
1056 }
1057 }
1058}
1059
1060impl ReadFrom for GameType {
1061 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
1062 let value = VarInt::read(data)?.0;
1063 match value {
1064 0 => Ok(GameType::Survival),
1065 1 => Ok(GameType::Creative),
1066 2 => Ok(GameType::Adventure),
1067 3 => Ok(GameType::Spectator),
1068 _ => Err(io::Error::new(
1069 io::ErrorKind::InvalidData,
1070 "Invalid GameType",
1071 )),
1072 }
1073 }
1074}
1075
1076impl From<GameType> for i8 {
1077 fn from(value: GameType) -> Self {
1078 value as i8
1079 }
1080}
1081
1082impl From<GameType> for i32 {
1083 fn from(value: GameType) -> Self {
1084 value as i32
1085 }
1086}
1087
1088impl From<GameType> for f32 {
1089 fn from(value: GameType) -> Self {
1090 f32::from(value as i8)
1091 }
1092}
1093
1094impl From<i8> for GameType {
1095 fn from(value: i8) -> Self {
1096 match value {
1097 1 => GameType::Creative,
1098 2 => GameType::Adventure,
1099 3 => GameType::Spectator,
1100 _ => GameType::Survival,
1101 }
1102 }
1103}
1104
1105impl From<i32> for GameType {
1106 fn from(value: i32) -> Self {
1107 match value {
1108 1 => GameType::Creative,
1109 2 => GameType::Adventure,
1110 3 => GameType::Spectator,
1111 _ => GameType::Survival,
1112 }
1113 }
1114}
1115
1116impl From<f32> for GameType {
1117 fn from(value: f32) -> Self {
1118 match value {
1119 1. => GameType::Creative,
1120 2. => GameType::Adventure,
1121 3. => GameType::Spectator,
1122 _ => GameType::Survival,
1123 }
1124 }
1125}
1126
1127#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
1132#[repr(u8)]
1133pub enum Difficulty {
1134 Peaceful = 0,
1136 Easy = 1,
1138 #[default]
1140 Normal = 2,
1141 Hard = 3,
1143}
1144
1145#[expect(clippy::match_same_arms, reason = "cause it looks better")]
1146impl From<u8> for Difficulty {
1147 fn from(value: u8) -> Self {
1148 match value {
1149 0 => Difficulty::Peaceful,
1150 1 => Difficulty::Easy,
1151 2 => Difficulty::Normal,
1152 3 => Difficulty::Hard,
1153 _ => Difficulty::Normal,
1154 }
1155 }
1156}
1157
1158impl From<Difficulty> for u8 {
1159 fn from(value: Difficulty) -> Self {
1160 value as u8
1161 }
1162}
1163
1164impl ReadFrom for Difficulty {
1165 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
1166 let value = <u8 as ReadFrom>::read(data)?;
1167 match value {
1168 0 => Ok(Difficulty::Peaceful),
1169 1 => Ok(Difficulty::Easy),
1170 2 => Ok(Difficulty::Normal),
1171 3 => Ok(Difficulty::Hard),
1172 _ => Err(io::Error::new(
1173 io::ErrorKind::InvalidData,
1174 format!("Invalid Difficulty: {value}"),
1175 )),
1176 }
1177 }
1178}
1179
1180impl WriteTo for Difficulty {
1181 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
1182 (*self as u8).write(writer)
1183 }
1184}
1185
1186impl Serialize for Difficulty {
1187 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1188 where
1189 S: serde::Serializer,
1190 {
1191 serializer.serialize_u8(*self as u8)
1192 }
1193}
1194
1195impl<'de> Deserialize<'de> for Difficulty {
1196 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1197 where
1198 D: serde::Deserializer<'de>,
1199 {
1200 let id = u8::deserialize(deserializer)?;
1201 Ok(Self::from(id))
1202 }
1203}
1204
1205#[derive(Clone, PartialEq, Eq, Hash, Default)]
1207pub struct Identifier {
1208 pub namespace: Cow<'static, str>,
1210 pub path: Cow<'static, str>,
1212}
1213
1214impl Debug for Identifier {
1215 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
1216 f.write_str(&format!("{}:{}", self.namespace, self.path))
1217 }
1218}
1219
1220impl Identifier {
1221 pub const VANILLA_NAMESPACE: &'static str = "minecraft";
1223
1224 #[must_use]
1226 pub fn new(
1227 namespace: impl Into<Cow<'static, str>>,
1228 path: impl Into<Cow<'static, str>>,
1229 ) -> Self {
1230 Identifier {
1231 namespace: namespace.into(),
1232 path: path.into(),
1233 }
1234 }
1235 #[must_use]
1236 pub const fn new_static(namespace: &'static str, path: &'static str) -> Self {
1237 Identifier {
1238 namespace: Cow::Borrowed(namespace),
1239 path: Cow::Borrowed(path),
1240 }
1241 }
1242
1243 #[must_use]
1245 pub const fn vanilla(path: String) -> Self {
1246 Identifier {
1247 namespace: Cow::Borrowed(Self::VANILLA_NAMESPACE),
1248 path: Cow::Owned(path),
1249 }
1250 }
1251
1252 #[must_use]
1254 pub const fn vanilla_static(path: &'static str) -> Self {
1255 Identifier {
1256 namespace: Cow::Borrowed(Self::VANILLA_NAMESPACE),
1257 path: Cow::Borrowed(path),
1258 }
1259 }
1260
1261 #[must_use]
1263 pub const fn valid_namespace_char(char: char) -> bool {
1264 char == '_'
1265 || char == '-'
1266 || char.is_ascii_lowercase()
1267 || char.is_ascii_digit()
1268 || char == '.'
1269 }
1270
1271 #[must_use]
1273 pub const fn valid_char(char: char) -> bool {
1274 Self::valid_namespace_char(char) || char == '/'
1275 }
1276
1277 pub fn validate_namespace(namespace: &str) -> bool {
1279 namespace.chars().all(Self::valid_namespace_char)
1280 }
1281
1282 pub fn validate_path(path: &str) -> bool {
1284 path.chars().all(Self::valid_char)
1285 }
1286
1287 #[must_use]
1289 pub fn validate(namespace: &str, path: &str) -> bool {
1290 Self::validate_namespace(namespace) && Self::validate_path(path)
1291 }
1292}
1293
1294impl Display for Identifier {
1295 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1296 write!(f, "{}:{}", self.namespace, self.path)
1297 }
1298}
1299
1300impl FromStr for Identifier {
1301 type Err = &'static str;
1302
1303 fn from_str(s: &str) -> Result<Self, Self::Err> {
1304 let parts: Vec<&str> = s.split(':').collect();
1305 if parts.len() != 2 {
1306 return Err("Invalid resource location");
1307 }
1308
1309 if !Identifier::validate_namespace(parts[0]) {
1310 return Err("Invalid namespace");
1311 }
1312
1313 if !Identifier::validate_path(parts[1]) {
1314 return Err("Invalid path");
1315 }
1316
1317 Ok(Identifier {
1318 namespace: Cow::Owned(parts[0].to_string()),
1319 path: Cow::Owned(parts[1].to_string()),
1320 })
1321 }
1322}
1323impl Serialize for Identifier {
1324 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1325 where
1326 S: serde::Serializer,
1327 {
1328 serializer.serialize_str(&self.to_string())
1329 }
1330}
1331
1332impl<'de> Deserialize<'de> for Identifier {
1333 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1334 where
1335 D: serde::Deserializer<'de>,
1336 {
1337 let s = String::deserialize(deserializer)?;
1338 Identifier::from_str(&s).map_err(D::Error::custom)
1339 }
1340}
1341
1342unsafe impl<C: Config> SchemaWrite<C> for Identifier {
1347 type Src = Identifier;
1348
1349 fn size_of(src: &Self::Src) -> wincode::WriteResult<usize> {
1350 <str as SchemaWrite<C>>::size_of(&src.to_string())
1351 }
1352
1353 fn write(writer: impl Writer, src: &Self::Src) -> wincode::WriteResult<()> {
1354 <str as SchemaWrite<C>>::write(writer, &src.to_string())
1355 }
1356}
1357
1358unsafe impl<'de, C: Config> SchemaRead<'de, C> for Identifier {
1362 type Dst = Identifier;
1363
1364 fn read(reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> wincode::ReadResult<()> {
1365 let mut s = MaybeUninit::<String>::uninit();
1366 <String as SchemaRead<'de, C>>::read(reader, &mut s)?;
1367
1368 let s = unsafe { s.assume_init() };
1370
1371 dst.write(Identifier::from_str(&s).map_err(wincode::ReadError::Custom)?);
1372 Ok(())
1373 }
1374}
1375
1376#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1378pub enum InteractionHand {
1379 MainHand,
1381 OffHand,
1383}
1384
1385impl ReadFrom for InteractionHand {
1386 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
1387 let id = VarInt::read(data)?.0;
1388 match id {
1389 0 => Ok(InteractionHand::MainHand),
1390 1 => Ok(InteractionHand::OffHand),
1391 _ => Err(io::Error::other("Invalid InteractionHand id")),
1392 }
1393 }
1394}
1395
1396#[cfg(test)]
1397mod tests {
1398 use super::*;
1399
1400 #[test]
1401 fn test_block_pos_roundtrip() {
1402 let positions = vec![
1403 BlockPos(IVec3::new(0, -61, -2)),
1404 BlockPos(IVec3::new(0, 0, 0)),
1405 BlockPos(IVec3::new(100, 64, -100)),
1406 BlockPos(IVec3::new(-1000, -64, 1000)),
1407 BlockPos(IVec3::new(33_554_431, 2047, 33_554_431)), BlockPos(IVec3::new(-33_554_432, -2048, -33_554_432)), ];
1410
1411 for pos in positions {
1412 let encoded = PackedBlockPos::from(pos);
1413 let decoded = encoded.to_block_pos();
1414 assert_eq!(
1415 pos, decoded,
1416 "Roundtrip failed for {pos:?}: encoded={encoded:?}, decoded={decoded:?}"
1417 );
1418 }
1419 }
1420
1421 #[test]
1422 fn test_block_pos_specific_case() {
1423 let pos = BlockPos(IVec3::new(0, -61, -2));
1425 let encoded = PackedBlockPos::from(pos);
1426 let decoded = encoded.to_block_pos();
1427 assert_eq!(pos, decoded, "Position 0, -61, -2 failed roundtrip");
1428 }
1429
1430 #[test]
1431 fn packed_chunk_local_xz_masks_absolute_coordinates() {
1432 let packed = PackedChunkLocalXZ::from_block_pos(BlockPos::new(17, 64, 18));
1433
1434 assert_eq!(packed.as_u8(), 0x12);
1435 assert_eq!(packed.x(), 1);
1436 assert_eq!(packed.z(), 2);
1437 }
1438
1439 #[test]
1440 fn packed_chunk_local_xz_rejects_invalid_local_coordinates() {
1441 assert!(PackedChunkLocalXZ::from_local_xz(15, 15).is_some());
1442 assert!(PackedChunkLocalXZ::from_local_xz(16, 0).is_none());
1443 assert!(PackedChunkLocalXZ::from_local_xz(0, 16).is_none());
1444 }
1445
1446 #[test]
1447 fn entity_positions_floor_before_chunk_and_section_conversion() {
1448 let pos = DVec3::new(-4352.5, -16.5, -4405.5);
1449
1450 assert_eq!(BlockPos::from(pos), BlockPos::new(-4353, -17, -4406));
1451 assert_eq!(ChunkPos::from_entity_pos(pos), ChunkPos::new(-273, -276));
1452 assert_eq!(
1453 SectionPos::from_entity_pos(pos),
1454 SectionPos::new(-273, -2, -276)
1455 );
1456 }
1457
1458 #[test]
1459 fn packed_section_block_pos_masks_absolute_coordinates() {
1460 let packed = PackedSectionBlockPos::from_block_pos(BlockPos::new(17, -1, 18));
1461
1462 assert_eq!(packed.as_u16(), 0x12f);
1463 assert_eq!(packed.x(), 1);
1464 assert_eq!(packed.y(), 15);
1465 assert_eq!(packed.z(), 2);
1466 }
1467
1468 #[test]
1469 fn packed_section_block_pos_rejects_invalid_raw_bits() {
1470 assert!(PackedSectionBlockPos::from_raw(0x0fff).is_some());
1471 assert!(PackedSectionBlockPos::from_raw(0x1000).is_none());
1472 }
1473
1474 #[test]
1475 fn packed_section_block_pos_rejects_invalid_local_coordinates() {
1476 assert!(PackedSectionBlockPos::from_local_xyz(15, 15, 15).is_some());
1477 assert!(PackedSectionBlockPos::from_local_xyz(16, 0, 0).is_none());
1478 assert!(PackedSectionBlockPos::from_local_xyz(0, 16, 0).is_none());
1479 assert!(PackedSectionBlockPos::from_local_xyz(0, 0, 16).is_none());
1480 }
1481
1482 #[test]
1483 fn packed_section_block_pos_converts_to_absolute_block_pos() {
1484 let section = SectionPos::new(2, -4, -3);
1485 let Some(packed) = PackedSectionBlockPos::from_local_xyz(1, 15, 2) else {
1486 panic!("valid local packed section block position was rejected");
1487 };
1488
1489 assert_eq!(packed.to_block_pos(section), BlockPos::new(33, -49, -46));
1490 assert_eq!(
1491 section.relative_to_block_pos(packed),
1492 BlockPos::new(33, -49, -46)
1493 );
1494 }
1495
1496 #[test]
1497 fn packed_position_newtypes_roundtrip() {
1498 let chunk = ChunkPos::new(-12, 34);
1499 assert_eq!(PackedChunkPos::from(chunk).to_chunk_pos(), chunk);
1500
1501 let block = BlockPos::new(-1024, 64, 2048);
1502 assert_eq!(PackedBlockPos::from(block).to_block_pos(), block);
1503
1504 let section = SectionPos::new(-8, -4, 12);
1505 assert_eq!(PackedSectionPos::from(section).to_section_pos(), section);
1506 }
1507}
1508
1509#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1511pub struct UpdateFlags(u16);
1512
1513bitflags! {
1514 impl UpdateFlags: u16 {
1515 const UPDATE_NEIGHBORS = 1;
1516 const UPDATE_CLIENTS = 1 << 1;
1517 const UPDATE_INVISIBLE = 1 << 2;
1518 const UPDATE_IMMEDIATE = 1 << 3;
1519 const UPDATE_KNOWN_SHAPE = 1 << 4;
1520 const UPDATE_SUPPRESS_DROPS = 1 << 5;
1521 const UPDATE_MOVE_BY_PISTON = 1 << 6;
1522 const UPDATE_SKIP_SHAPE_UPDATE_ON_WIRE = 1 << 7;
1523 const UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS = 1 << 8;
1524 const UPDATE_SKIP_ON_PLACE = 1 << 9;
1525
1526 const UPDATE_NONE = Self::UPDATE_INVISIBLE.bits() | Self::UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS.bits();
1527 const UPDATE_ALL = Self::UPDATE_NEIGHBORS.bits() | Self::UPDATE_CLIENTS.bits();
1528 const UPDATE_ALL_IMMEDIATE = Self::UPDATE_ALL.bits() | Self::UPDATE_IMMEDIATE.bits();
1529 }
1530}