From 79c91a91bbeb40533d5e837fc094f96c3d086a08 Mon Sep 17 00:00:00 2001 From: Lizzy Fleckenstein Date: Wed, 8 Feb 2023 02:46:10 +0100 Subject: derive serialization for structs --- src/lib.rs | 315 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/to_clt.rs | 14 +-- src/to_clt/hud.rs | 16 +++ src/to_srv.rs | 20 +++- 4 files changed, 345 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/lib.rs b/src/lib.rs index 143b397..6258b34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub use enumset; +pub use flate2; #[cfg(feature = "random")] pub use generate_random; @@ -9,10 +10,18 @@ pub use rand; #[cfg(feature = "serde")] pub use serde; -use enumset::{EnumSet, EnumSetType}; +use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use enumset::{EnumSet, EnumSetType, EnumSetTypeWithRepr}; use mt_data_derive::mt_derive; pub use mt_data_derive::{MtDeserialize, MtSerialize}; -use std::{collections::HashMap, fmt, io}; +use paste::paste; +use std::{ + collections::{HashMap, HashSet}, + convert::Infallible, + fmt, + io::{self, Read, Write}, + num::TryFromIntError, +}; use thiserror::Error; #[cfg(feature = "serde")] @@ -21,28 +30,318 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "random")] use generate_random::GenerateRandom; +use crate as mt_data; + +#[derive(Error, Debug)] +#[error("variable length")] +pub struct VarLen; + #[derive(Error, Debug)] pub enum SerializeError { - #[error("{0}")] + #[error("io error: {0}")] IoError(#[from] io::Error), - #[error("serialization is not implemented")] + #[error("collection too big: {0}")] + TooBig(#[from] TryFromIntError), + #[error("unimplemented")] Unimplemented, } +impl From for DeserializeError { + fn from(_err: Infallible) -> Self { + unreachable!("infallible") + } +} + #[derive(Error, Debug)] pub enum DeserializeError { - #[error("{0}")] + #[error("io error: {0}")] IoError(#[from] io::Error), - #[error("deserialization is not implemented")] + #[error("variable length not supported")] + NoVarlen(#[from] VarLen), + #[error("collection too big: {0}")] + TooBig(#[from] TryFromIntError), + #[error("unimplemented")] Unimplemented, } +impl From for SerializeError { + fn from(_err: Infallible) -> Self { + unreachable!("infallible") + } +} + +pub trait MtCfg: + Sized + + MtSerialize + + MtDeserialize + + TryFrom + + TryInto +{ + type TryFromError: Into; + type TryIntoError: Into; + + #[inline] + fn utf16() -> bool { + false + } + + fn write_len(len: usize, writer: &mut impl Write) -> Result<(), SerializeError> { + Ok(Self::try_from(len) + .map_err(|e| e.into())? + .mt_serialize::(writer)?) + } +} + +pub type DefaultCfg = u16; + pub trait MtSerialize: Sized { - fn mt_serialize(&self, writer: &mut W) -> Result<(), SerializeError>; + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError>; } pub trait MtDeserialize: Sized { - fn mt_deserialize(reader: &mut R) -> Result; + fn mt_deserialize(reader: &mut impl Read) -> Result; +} + +impl MtCfg for u8 { + type TryFromError = TryFromIntError; + type TryIntoError = Infallible; +} + +impl MtCfg for u16 { + type TryFromError = TryFromIntError; + type TryIntoError = Infallible; +} + +impl MtCfg for u32 { + type TryFromError = TryFromIntError; + type TryIntoError = TryFromIntError; +} + +impl MtCfg for u64 { + type TryFromError = TryFromIntError; + type TryIntoError = TryFromIntError; +} + +pub struct NoLen; + +impl MtSerialize for NoLen { + fn mt_serialize(&self, _writer: &mut impl Write) -> Result<(), SerializeError> { + Ok(()) + } +} + +impl MtDeserialize for NoLen { + fn mt_deserialize(_reader: &mut impl Read) -> Result { + Ok(Self) + } +} + +impl TryFrom for NoLen { + type Error = Infallible; + + fn try_from(_x: usize) -> Result { + Ok(Self) + } +} + +impl TryInto for NoLen { + type Error = VarLen; + + fn try_into(self) -> Result { + Err(VarLen) + } +} + +impl MtCfg for NoLen { + type TryFromError = Infallible; + type TryIntoError = VarLen; +} + +pub struct Utf16(pub B); + +impl MtSerialize for Utf16 { + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + self.0.mt_serialize::(writer) + } +} + +impl MtDeserialize for Utf16 { + fn mt_deserialize(reader: &mut impl Read) -> Result { + Ok(Self(B::mt_deserialize::(reader)?)) + } +} + +impl TryFrom for Utf16 { + type Error = B::TryFromError; + + fn try_from(x: usize) -> Result { + Ok(Self(x.try_into()?)) + } +} + +impl TryInto for Utf16 { + type Error = B::TryIntoError; + + fn try_into(self) -> Result { + self.0.try_into() + } +} + +impl MtCfg for Utf16 { + type TryFromError = B::TryFromError; + type TryIntoError = B::TryIntoError; + + #[inline] + fn utf16() -> bool { + true + } +} + +impl MtSerialize for u8 { + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + writer.write_u8(*self)?; + Ok(()) + } +} + +impl MtDeserialize for u8 { + fn mt_deserialize(reader: &mut impl Read) -> Result { + Ok(reader.read_u8()?) + } +} + +impl MtSerialize for i8 { + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + writer.write_i8(*self)?; + Ok(()) + } +} + +impl MtDeserialize for i8 { + fn mt_deserialize(reader: &mut impl Read) -> Result { + Ok(reader.read_i8()?) + } +} + +macro_rules! impl_num { + ($T:ty) => { + impl MtSerialize for $T { + fn mt_serialize( + &self, + writer: &mut impl Write, + ) -> Result<(), SerializeError> { + paste! { + writer.[]::(*self)?; + } + Ok(()) + } + } + + impl MtDeserialize for $T { + fn mt_deserialize(reader: &mut impl Read) -> Result { + paste! { + Ok(reader.[]::()?) + } + } + } + }; +} + +impl_num!(u16); +impl_num!(i16); + +impl_num!(u32); +impl_num!(i32); +impl_num!(f32); + +impl_num!(u64); +impl_num!(i64); +impl_num!(f64); + +impl MtSerialize for () { + fn mt_serialize(&self, _writer: &mut impl Write) -> Result<(), SerializeError> { + Ok(()) + } +} + +impl MtSerialize for bool { + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + (*self as u8).mt_serialize::(writer) + } +} + +impl MtSerialize for [T; N] { + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + for item in self.iter() { + item.mt_serialize::(writer)?; + } + + Ok(()) + } +} + +impl> MtSerialize for EnumSet { + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + self.as_repr().mt_serialize::(writer) + } +} + +impl MtSerialize for Option { + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + match self { + Some(item) => item.mt_serialize::(writer), + None => Ok(()), + } + } +} + +impl MtSerialize for Vec { + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + C::write_len(self.len(), writer)?; + for item in self.iter() { + item.mt_serialize::(writer)?; + } + Ok(()) + } +} + +impl MtSerialize for HashSet { + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + C::write_len(self.len(), writer)?; + for item in self.iter() { + item.mt_serialize::(writer)?; + } + Ok(()) + } +} + +impl MtSerialize for HashMap +where + K: MtSerialize + std::cmp::Eq + std::hash::Hash, + V: MtSerialize, +{ + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + C::write_len(self.len(), writer)?; + for (key, value) in self.iter() { + key.mt_serialize::(writer)?; + value.mt_serialize::(writer)?; + } + Ok(()) + } +} + +impl MtSerialize for String { + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + if C::utf16() { + // TODO + Err(SerializeError::Unimplemented) + } else { + C::write_len(self.len(), writer)?; + writer.write_all(self.as_bytes())?; + + Ok(()) + } + } } mod to_clt; diff --git a/src/to_clt.rs b/src/to_clt.rs index 6a4b3c6..b93f7ee 100644 --- a/src/to_clt.rs +++ b/src/to_clt.rs @@ -127,13 +127,13 @@ pub enum ToCltPkt { Media { n: u16, i: u16, - files: Vec, + files: Vec, // FIXME: can we use a HashMap for this? } = 56, NodeDefs { defs: Vec, } = 58, AnnounceMedia { - files: Vec, + files: Vec, // FIXME: can we use a HashMap for this? url: String, } = 60, #[mt(size32, zlib)] @@ -159,7 +159,7 @@ pub enum ToCltPkt { id: u32, } = 64, Privs { - privs: Vec, + privs: HashSet, } = 65, InvFormspec { #[mt(size32)] @@ -283,7 +283,7 @@ pub enum ToCltPkt { } = 85, UpdatePlayerList { update_type: PlayerListUpdateType, - players: Vec, + players: HashSet, } = 86, ModChanMsg { channel: String, @@ -322,9 +322,5 @@ pub enum ToCltPkt { FormspecPrepend { prepend: String, } = 97, - MinimapModes { - #[mt(len = "modes")] - current: u16, - modes: Vec, - } = 98, + MinimapModes(MinimapModePkt) = 98, } diff --git a/src/to_clt/hud.rs b/src/to_clt/hud.rs index c7a206a..f0598a9 100644 --- a/src/to_clt/hud.rs +++ b/src/to_clt/hud.rs @@ -109,6 +109,22 @@ pub struct MinimapMode { pub scale: u16, } +#[mt_derive(to = "clt", custom)] +pub struct MinimapModePkt { + current: u16, + modes: Vec, +} + +impl MtSerialize for MinimapModePkt { + fn mt_serialize(&self, writer: &mut impl Write) -> Result<(), SerializeError> { + C::write_len(self.modes.len(), writer)?; + self.current.mt_serialize::(writer)?; + for item in self.modes.iter() { + item.mt_serialize::(writer)?; + } + Ok(()) + } +} /* TODO: rustify diff --git a/src/to_srv.rs b/src/to_srv.rs index 354bbb4..e83a8b8 100644 --- a/src/to_srv.rs +++ b/src/to_srv.rs @@ -1,12 +1,26 @@ use crate::*; +#[mt_derive(to = "srv", repr = "u32", enumset)] +pub enum Key { + Forward, + Backward, + Left, + Right, + Jump, + Special, + Sneak, + Dig, + Place, + Zoom, +} + #[mt_derive(to = "srv")] pub struct PlayerPos { - #[mt(const_u16 = 1)] // supported compression pub pos_100: [i32; 3], pub vel_100: [i32; 3], pub pitch_100: i32, pub yaw_100: i32, + pub keys: EnumSet, pub fov_80: u8, pub wanted_range: u8, } @@ -29,7 +43,7 @@ pub enum ToSrvPkt { Nil = 0, Init { serialize_version: u8, - #[mt(const_u16 = 1)] // supported compression + #[mt(const16 = 1)] // supported compression min_proto_version: u16, max_proto_version: u16, player_name: String, @@ -76,7 +90,7 @@ pub enum ToSrvPkt { Interact { action: Interaction, item_slot: u16, - #[mt(size_u32)] + #[mt(size32)] pointed: PointedThing, pos: PlayerPos, } = 57, -- cgit v1.2.3