a bare-bones limbo server in rust (mirror of https://github.com/xoogware/crawlspace)
1/*
2 * Copyright (c) 2024 Andrew Brower.
3 * This file is part of Crawlspace.
4 *
5 * Crawlspace is free software: you can redistribute it and/or
6 * modify it under the terms of the GNU Affero General Public
7 * License as published by the Free Software Foundation, either
8 * version 3 of the License, or (at your option) any later version.
9 *
10 * Crawlspace is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public
16 * License along with Crawlspace. If not, see
17 * <https://www.gnu.org/licenses/>.
18 */
19
20use bitfield_struct::bitfield;
21use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
22use thiserror::Error;
23
24use crate::{ErrorKind, Read, Write};
25
26#[derive(Debug)]
27pub struct Position {
28 pub x: i32,
29 pub y: i32,
30 pub z: i32,
31}
32
33#[bitfield(u64)]
34pub struct PackedPosition {
35 #[bits(26)]
36 pub x: i32,
37 #[bits(26)]
38 pub z: i32,
39 #[bits(12)]
40 pub y: i32,
41}
42
43impl Position {
44 #[must_use]
45 pub fn new(x: i32, y: i32, z: i32) -> Self {
46 Self { x, y, z }
47 }
48}
49
50#[derive(Debug, Error)]
51pub enum EncodingError {
52 #[error("value is out of bounds")]
53 OutOfBounds,
54}
55
56impl TryFrom<&Position> for PackedPosition {
57 type Error = EncodingError;
58
59 fn try_from(value: &Position) -> std::result::Result<Self, Self::Error> {
60 match (value.x, value.y, value.z) {
61 (-0x2000000..=0x1ffffff, -0x800..=0x7ff, -0x2000000..=0x1ffffff) => {
62 Ok(PackedPosition::new()
63 .with_x(value.x)
64 .with_y(value.y)
65 .with_z(value.z))
66 }
67 _ => Err(EncodingError::OutOfBounds),
68 }
69 }
70}
71
72impl From<PackedPosition> for Position {
73 fn from(value: PackedPosition) -> Self {
74 Self {
75 x: value.x(),
76 y: value.y(),
77 z: value.z(),
78 }
79 }
80}
81
82impl Write for Position {
83 fn write(&self, w: &mut impl std::io::Write) -> Result<(), ErrorKind> {
84 let encoded: PackedPosition = self
85 .try_into()
86 .map_err(|_| ErrorKind::InvalidData("Invalid packed position".to_string()))?;
87 encoded.write(w)
88 }
89}
90
91impl Read for Position {
92 fn read(r: &mut impl std::io::Read) -> Result<Self, ErrorKind> {
93 let bytes = r.read_i64::<BigEndian>()?;
94
95 Ok(Self {
96 x: (bytes >> 38) as i32,
97 y: (bytes << 52 >> 52) as i32,
98 z: (bytes << 26 >> 38) as i32,
99 })
100 }
101}
102
103impl Write for PackedPosition {
104 fn write(&self, w: &mut impl std::io::Write) -> Result<(), ErrorKind> {
105 Ok(w.write_u64::<BigEndian>(self.0)?)
106 }
107}