1use std::{
2 error::Error,
3 fmt::{self, Display, Formatter},
4 io::{self, Cursor, Write},
5};
6
7use glam::{IVec2, IVec3};
8use wincode::{SchemaRead, SchemaWrite};
9
10use crate::serial::{ReadFrom, WriteTo};
11
12use super::position::{BlockPos, ChunkPos, SectionPos};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SchemaWrite, SchemaRead)]
16pub struct PackedChunkPos(i64);
17
18impl PackedChunkPos {
19 #[must_use]
21 pub const fn from_raw(raw: i64) -> Self {
22 Self(raw)
23 }
24
25 #[must_use]
27 pub const fn as_raw(self) -> i64 {
28 self.0
29 }
30
31 #[must_use]
33 pub const fn to_chunk_pos(self) -> ChunkPos {
34 ChunkPos(IVec2::new(
35 (self.0 & 0xFFFF_FFFF) as i32,
36 (self.0 >> 32) as i32,
37 ))
38 }
39}
40
41impl From<ChunkPos> for PackedChunkPos {
42 fn from(pos: ChunkPos) -> Self {
43 Self((i64::from(pos.0.x) & 0xFFFF_FFFF) | ((i64::from(pos.0.y) & 0xFFFF_FFFF) << 32))
44 }
45}
46
47impl From<PackedChunkPos> for ChunkPos {
48 fn from(pos: PackedChunkPos) -> Self {
49 pos.to_chunk_pos()
50 }
51}
52
53impl ReadFrom for PackedChunkPos {
54 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
55 Ok(Self::from_raw(<i64 as ReadFrom>::read(data)?))
56 }
57}
58
59impl WriteTo for PackedChunkPos {
60 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
61 self.0.write(writer)
62 }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SchemaWrite, SchemaRead)]
67pub struct PackedBlockPos(i64);
68
69impl PackedBlockPos {
70 pub(super) const HORIZONTAL_BITS: u32 = 26;
71 const Y_BITS: u32 = 12;
72 const X_OFFSET: u32 = Self::HORIZONTAL_BITS + Self::Y_BITS;
73 const Z_OFFSET: u32 = Self::Y_BITS;
74 const XZ_MASK: i64 = (1i64 << Self::HORIZONTAL_BITS) - 1;
75 const Y_MASK: i64 = (1i64 << Self::Y_BITS) - 1;
76
77 #[must_use]
79 pub const fn from_raw(raw: i64) -> Self {
80 Self(raw)
81 }
82
83 #[must_use]
85 pub const fn as_raw(self) -> i64 {
86 self.0
87 }
88
89 #[must_use]
91 pub const fn to_block_pos(self) -> BlockPos {
92 let x = self.0 >> Self::X_OFFSET;
93 let y = self.0 & Self::Y_MASK;
94 let z = (self.0 >> Self::Z_OFFSET) & Self::XZ_MASK;
95
96 let x = (x << (64 - Self::HORIZONTAL_BITS)) >> (64 - Self::HORIZONTAL_BITS);
97 let y = (y << (64 - Self::Y_BITS)) >> (64 - Self::Y_BITS);
98 let z = (z << (64 - Self::HORIZONTAL_BITS)) >> (64 - Self::HORIZONTAL_BITS);
99
100 BlockPos(IVec3::new(x as i32, y as i32, z as i32))
101 }
102}
103
104impl From<BlockPos> for PackedBlockPos {
105 fn from(pos: BlockPos) -> Self {
106 let x = i64::from(pos.0.x);
107 let y = i64::from(pos.0.y);
108 let z = i64::from(pos.0.z);
109 Self(
110 ((x & Self::XZ_MASK) << Self::X_OFFSET)
111 | ((z & Self::XZ_MASK) << Self::Z_OFFSET)
112 | (y & Self::Y_MASK),
113 )
114 }
115}
116
117impl From<PackedBlockPos> for BlockPos {
118 fn from(pos: PackedBlockPos) -> Self {
119 pos.to_block_pos()
120 }
121}
122
123impl ReadFrom for PackedBlockPos {
124 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
125 Ok(Self::from_raw(<i64 as ReadFrom>::read(data)?))
126 }
127}
128
129impl WriteTo for PackedBlockPos {
130 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
131 self.0.write(writer)
132 }
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SchemaWrite, SchemaRead)]
137pub struct PackedSectionPos(i64);
138
139impl PackedSectionPos {
140 const XZ_BITS: u32 = 22;
141 const Y_BITS: u32 = 20;
142 const X_OFFSET: u32 = Self::XZ_BITS + Self::Y_BITS;
143 const Z_OFFSET: u32 = Self::Y_BITS;
144 const XZ_MASK: i64 = (1i64 << Self::XZ_BITS) - 1;
145 const Y_MASK: i64 = (1i64 << Self::Y_BITS) - 1;
146
147 #[must_use]
149 pub const fn from_raw(raw: i64) -> Self {
150 Self(raw)
151 }
152
153 #[must_use]
155 pub const fn as_raw(self) -> i64 {
156 self.0
157 }
158
159 #[must_use]
161 pub const fn to_section_pos(self) -> SectionPos {
162 let x = self.0 >> Self::X_OFFSET;
163 let z = (self.0 >> Self::Z_OFFSET) & Self::XZ_MASK;
164 let y = self.0 & Self::Y_MASK;
165
166 let x = (x << (64 - Self::XZ_BITS)) >> (64 - Self::XZ_BITS);
167 let y = (y << (64 - Self::Y_BITS)) >> (64 - Self::Y_BITS);
168 let z = (z << (64 - Self::XZ_BITS)) >> (64 - Self::XZ_BITS);
169
170 SectionPos(IVec3::new(x as i32, y as i32, z as i32))
171 }
172}
173
174impl From<SectionPos> for PackedSectionPos {
175 fn from(pos: SectionPos) -> Self {
176 let x = i64::from(pos.0.x);
177 let y = i64::from(pos.0.y);
178 let z = i64::from(pos.0.z);
179 Self(
180 ((x & Self::XZ_MASK) << Self::X_OFFSET)
181 | ((z & Self::XZ_MASK) << Self::Z_OFFSET)
182 | (y & Self::Y_MASK),
183 )
184 }
185}
186
187impl From<PackedSectionPos> for SectionPos {
188 fn from(pos: PackedSectionPos) -> Self {
189 pos.to_section_pos()
190 }
191}
192
193impl ReadFrom for PackedSectionPos {
194 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
195 Ok(Self::from_raw(<i64 as ReadFrom>::read(data)?))
196 }
197}
198
199impl WriteTo for PackedSectionPos {
200 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
201 self.0.write(writer)
202 }
203}
204
205#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SchemaWrite, SchemaRead)]
209pub struct PackedChunkLocalXZ(u8);
210
211impl PackedChunkLocalXZ {
212 const COORD_MASK: u8 = 0x0f;
213
214 #[must_use]
216 pub const fn from_block_pos(pos: BlockPos) -> Self {
217 Self::from_local_unchecked(
218 (pos.0.x & SectionPos::SECTION_MASK) as u8,
219 (pos.0.z & SectionPos::SECTION_MASK) as u8,
220 )
221 }
222
223 #[must_use]
225 pub const fn from_local_xz(x: u8, z: u8) -> Option<Self> {
226 if x < 16 && z < 16 {
227 Some(Self::from_local_unchecked(x, z))
228 } else {
229 None
230 }
231 }
232
233 #[must_use]
235 pub const fn from_raw(raw: u8) -> Self {
236 Self(raw)
237 }
238
239 #[must_use]
241 pub const fn as_u8(self) -> u8 {
242 self.0
243 }
244
245 #[must_use]
247 pub const fn x(self) -> u8 {
248 (self.0 >> 4) & Self::COORD_MASK
249 }
250
251 #[must_use]
253 pub const fn z(self) -> u8 {
254 self.0 & Self::COORD_MASK
255 }
256
257 const fn from_local_unchecked(x: u8, z: u8) -> Self {
258 Self((x << 4) | z)
259 }
260}
261
262impl From<BlockPos> for PackedChunkLocalXZ {
263 fn from(pos: BlockPos) -> Self {
264 Self::from_block_pos(pos)
265 }
266}
267
268impl ReadFrom for PackedChunkLocalXZ {
269 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
270 Ok(Self::from_raw(<u8 as ReadFrom>::read(data)?))
271 }
272}
273
274impl WriteTo for PackedChunkLocalXZ {
275 fn write(&self, writer: &mut impl Write) -> io::Result<()> {
276 self.0.write(writer)
277 }
278}
279
280#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, SchemaWrite, SchemaRead)]
284pub struct PackedSectionBlockPos(u16);
285
286impl PackedSectionBlockPos {
287 const COORD_MASK: u16 = 0x0f;
288 const RAW_MASK: u16 = 0x0fff;
289
290 #[must_use]
292 #[inline]
293 pub const fn from_block_pos(pos: BlockPos) -> Self {
294 Self::from_local_unchecked(
295 (pos.0.x & SectionPos::SECTION_MASK) as u8,
296 (pos.0.y & SectionPos::SECTION_MASK) as u8,
297 (pos.0.z & SectionPos::SECTION_MASK) as u8,
298 )
299 }
300
301 #[must_use]
303 pub const fn from_local_xyz(x: u8, y: u8, z: u8) -> Option<Self> {
304 if x < 16 && y < 16 && z < 16 {
305 Some(Self::from_local_unchecked(x, y, z))
306 } else {
307 None
308 }
309 }
310
311 #[must_use]
313 pub const fn from_raw(raw: u16) -> Option<Self> {
314 if raw & !Self::RAW_MASK == 0 {
315 Some(Self(raw))
316 } else {
317 None
318 }
319 }
320
321 #[must_use]
323 pub const fn as_u16(self) -> u16 {
324 self.0
325 }
326
327 #[must_use]
329 pub const fn x(self) -> u8 {
330 ((self.0 >> 8) & Self::COORD_MASK) as u8
331 }
332
333 #[must_use]
335 pub const fn y(self) -> u8 {
336 (self.0 & Self::COORD_MASK) as u8
337 }
338
339 #[must_use]
341 pub const fn z(self) -> u8 {
342 ((self.0 >> 4) & Self::COORD_MASK) as u8
343 }
344
345 #[must_use]
347 pub const fn to_block_pos(self, section_pos: SectionPos) -> BlockPos {
348 section_pos.relative_to_block_pos(self)
349 }
350
351 const fn from_local_unchecked(x: u8, y: u8, z: u8) -> Self {
352 Self(((x as u16) << 8) | ((z as u16) << 4) | y as u16)
353 }
354}
355
356impl From<BlockPos> for PackedSectionBlockPos {
357 fn from(pos: BlockPos) -> Self {
358 Self::from_block_pos(pos)
359 }
360}
361
362impl TryFrom<u16> for PackedSectionBlockPos {
363 type Error = InvalidPackedSectionBlockPos;
364
365 fn try_from(raw: u16) -> Result<Self, Self::Error> {
366 Self::from_raw(raw).ok_or(InvalidPackedSectionBlockPos { raw })
367 }
368}
369
370#[derive(Debug, Clone, Copy, PartialEq, Eq)]
372pub struct InvalidPackedSectionBlockPos {
373 raw: u16,
374}
375
376impl InvalidPackedSectionBlockPos {
377 #[must_use]
379 pub const fn raw(self) -> u16 {
380 self.raw
381 }
382}
383
384impl Display for InvalidPackedSectionBlockPos {
385 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
386 write!(
387 f,
388 "packed section block position {:#06x} uses reserved bits",
389 self.raw
390 )
391 }
392}
393
394impl Error for InvalidPackedSectionBlockPos {}