a bare-bones limbo server in rust (mirror of https://github.com/xoogware/crawlspace)
at protocol-breakout 107 lines 2.8 kB view raw
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}