summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLizzy Fleckenstein <eliasfleckenstein@web.de>2023-02-08 02:46:10 +0100
committerLizzy Fleckenstein <eliasfleckenstein@web.de>2023-02-08 02:46:10 +0100
commit79c91a91bbeb40533d5e837fc094f96c3d086a08 (patch)
treeededea2bcc941f9fd94237a3964d8b7ef3b6bead /src
parent7b96b333d786c17d6d3093ffa8886ae145bfa9a6 (diff)
downloadmt_ser-79c91a91bbeb40533d5e837fc094f96c3d086a08.tar.xz
derive serialization for structs
Diffstat (limited to 'src')
-rw-r--r--src/lib.rs315
-rw-r--r--src/to_clt.rs14
-rw-r--r--src/to_clt/hud.rs16
-rw-r--r--src/to_srv.rs20
4 files changed, 345 insertions, 20 deletions
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<Infallible> 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<Infallible> for SerializeError {
+ fn from(_err: Infallible) -> Self {
+ unreachable!("infallible")
+ }
+}
+
+pub trait MtCfg:
+ Sized
+ + MtSerialize
+ + MtDeserialize
+ + TryFrom<usize, Error = Self::TryFromError>
+ + TryInto<usize, Error = Self::TryIntoError>
+{
+ type TryFromError: Into<SerializeError>;
+ type TryIntoError: Into<DeserializeError>;
+
+ #[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::<DefaultCfg>(writer)?)
+ }
+}
+
+pub type DefaultCfg = u16;
+
pub trait MtSerialize: Sized {
- fn mt_serialize<W: io::Write>(&self, writer: &mut W) -> Result<(), SerializeError>;
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError>;
}
pub trait MtDeserialize: Sized {
- fn mt_deserialize<R: io::Read>(reader: &mut R) -> Result<Self, DeserializeError>;
+ fn mt_deserialize<C: MtCfg>(reader: &mut impl Read) -> Result<Self, DeserializeError>;
+}
+
+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<C: MtCfg>(&self, _writer: &mut impl Write) -> Result<(), SerializeError> {
+ Ok(())
+ }
+}
+
+impl MtDeserialize for NoLen {
+ fn mt_deserialize<C: MtCfg>(_reader: &mut impl Read) -> Result<Self, DeserializeError> {
+ Ok(Self)
+ }
+}
+
+impl TryFrom<usize> for NoLen {
+ type Error = Infallible;
+
+ fn try_from(_x: usize) -> Result<Self, Self::Error> {
+ Ok(Self)
+ }
+}
+
+impl TryInto<usize> for NoLen {
+ type Error = VarLen;
+
+ fn try_into(self) -> Result<usize, Self::Error> {
+ Err(VarLen)
+ }
+}
+
+impl MtCfg for NoLen {
+ type TryFromError = Infallible;
+ type TryIntoError = VarLen;
+}
+
+pub struct Utf16<B: MtCfg>(pub B);
+
+impl<B: MtCfg> MtSerialize for Utf16<B> {
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError> {
+ self.0.mt_serialize::<DefaultCfg>(writer)
+ }
+}
+
+impl<B: MtCfg> MtDeserialize for Utf16<B> {
+ fn mt_deserialize<C: MtCfg>(reader: &mut impl Read) -> Result<Self, DeserializeError> {
+ Ok(Self(B::mt_deserialize::<DefaultCfg>(reader)?))
+ }
+}
+
+impl<B: MtCfg> TryFrom<usize> for Utf16<B> {
+ type Error = B::TryFromError;
+
+ fn try_from(x: usize) -> Result<Self, Self::Error> {
+ Ok(Self(x.try_into()?))
+ }
+}
+
+impl<B: MtCfg> TryInto<usize> for Utf16<B> {
+ type Error = B::TryIntoError;
+
+ fn try_into(self) -> Result<usize, Self::Error> {
+ self.0.try_into()
+ }
+}
+
+impl<B: MtCfg> MtCfg for Utf16<B> {
+ type TryFromError = B::TryFromError;
+ type TryIntoError = B::TryIntoError;
+
+ #[inline]
+ fn utf16() -> bool {
+ true
+ }
+}
+
+impl MtSerialize for u8 {
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError> {
+ writer.write_u8(*self)?;
+ Ok(())
+ }
+}
+
+impl MtDeserialize for u8 {
+ fn mt_deserialize<C: MtCfg>(reader: &mut impl Read) -> Result<Self, DeserializeError> {
+ Ok(reader.read_u8()?)
+ }
+}
+
+impl MtSerialize for i8 {
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError> {
+ writer.write_i8(*self)?;
+ Ok(())
+ }
+}
+
+impl MtDeserialize for i8 {
+ fn mt_deserialize<C: MtCfg>(reader: &mut impl Read) -> Result<Self, DeserializeError> {
+ Ok(reader.read_i8()?)
+ }
+}
+
+macro_rules! impl_num {
+ ($T:ty) => {
+ impl MtSerialize for $T {
+ fn mt_serialize<C: MtCfg>(
+ &self,
+ writer: &mut impl Write,
+ ) -> Result<(), SerializeError> {
+ paste! {
+ writer.[<write_ $T>]::<BigEndian>(*self)?;
+ }
+ Ok(())
+ }
+ }
+
+ impl MtDeserialize for $T {
+ fn mt_deserialize<C: MtCfg>(reader: &mut impl Read) -> Result<Self, DeserializeError> {
+ paste! {
+ Ok(reader.[<read_ $T>]::<BigEndian>()?)
+ }
+ }
+ }
+ };
+}
+
+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<C: MtCfg>(&self, _writer: &mut impl Write) -> Result<(), SerializeError> {
+ Ok(())
+ }
+}
+
+impl MtSerialize for bool {
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError> {
+ (*self as u8).mt_serialize::<DefaultCfg>(writer)
+ }
+}
+
+impl<T: MtSerialize, const N: usize> MtSerialize for [T; N] {
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError> {
+ for item in self.iter() {
+ item.mt_serialize::<DefaultCfg>(writer)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl<T: MtSerialize, E: EnumSetTypeWithRepr<Repr = T>> MtSerialize for EnumSet<E> {
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError> {
+ self.as_repr().mt_serialize::<DefaultCfg>(writer)
+ }
+}
+
+impl<T: MtSerialize> MtSerialize for Option<T> {
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError> {
+ match self {
+ Some(item) => item.mt_serialize::<C>(writer),
+ None => Ok(()),
+ }
+ }
+}
+
+impl<T: MtSerialize> MtSerialize for Vec<T> {
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError> {
+ C::write_len(self.len(), writer)?;
+ for item in self.iter() {
+ item.mt_serialize::<DefaultCfg>(writer)?;
+ }
+ Ok(())
+ }
+}
+
+impl<T: MtSerialize> MtSerialize for HashSet<T> {
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError> {
+ C::write_len(self.len(), writer)?;
+ for item in self.iter() {
+ item.mt_serialize::<DefaultCfg>(writer)?;
+ }
+ Ok(())
+ }
+}
+
+impl<K, V> MtSerialize for HashMap<K, V>
+where
+ K: MtSerialize + std::cmp::Eq + std::hash::Hash,
+ V: MtSerialize,
+{
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError> {
+ C::write_len(self.len(), writer)?;
+ for (key, value) in self.iter() {
+ key.mt_serialize::<DefaultCfg>(writer)?;
+ value.mt_serialize::<DefaultCfg>(writer)?;
+ }
+ Ok(())
+ }
+}
+
+impl MtSerialize for String {
+ fn mt_serialize<C: MtCfg>(&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<MediaPayload>,
+ files: Vec<MediaPayload>, // FIXME: can we use a HashMap for this?
} = 56,
NodeDefs {
defs: Vec<NodeDef>,
} = 58,
AnnounceMedia {
- files: Vec<MediaAnnounce>,
+ files: Vec<MediaAnnounce>, // 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<String>,
+ privs: HashSet<String>,
} = 65,
InvFormspec {
#[mt(size32)]
@@ -283,7 +283,7 @@ pub enum ToCltPkt {
} = 85,
UpdatePlayerList {
update_type: PlayerListUpdateType,
- players: Vec<String>,
+ players: HashSet<String>,
} = 86,
ModChanMsg {
channel: String,
@@ -322,9 +322,5 @@ pub enum ToCltPkt {
FormspecPrepend {
prepend: String,
} = 97,
- MinimapModes {
- #[mt(len = "modes")]
- current: u16,
- modes: Vec<MinimapMode>,
- } = 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<MinimapMode>,
+}
+
+impl MtSerialize for MinimapModePkt {
+ fn mt_serialize<C: MtCfg>(&self, writer: &mut impl Write) -> Result<(), SerializeError> {
+ C::write_len(self.modes.len(), writer)?;
+ self.current.mt_serialize::<DefaultCfg>(writer)?;
+ for item in self.modes.iter() {
+ item.mt_serialize::<DefaultCfg>(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<Key>,
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,