]> git.lizzy.rs Git - mt_ser.git/commitdiff
derive serialization for structs
authorLizzy Fleckenstein <eliasfleckenstein@web.de>
Wed, 8 Feb 2023 01:46:10 +0000 (02:46 +0100)
committerLizzy Fleckenstein <eliasfleckenstein@web.de>
Wed, 8 Feb 2023 01:46:10 +0000 (02:46 +0100)
Cargo.toml
derive/.gitignore [new file with mode: 0644]
derive/Cargo.toml
derive/src/lib.rs
src/lib.rs
src/to_clt.rs
src/to_clt/hud.rs
src/to_srv.rs

index b2f6f64ab4fa762afd4dd5057970e0f5b012b8ff..c735152ce5c8abd4ab7df0739573a66020a75da6 100644 (file)
@@ -10,9 +10,12 @@ serde = ["dep:serde", "dep:serde_arrays", "enumset/serde"]
 server = []
 
 [dependencies]
+byteorder = "1.4.3"
 enumset = { git = "https://github.com/Lymia/enumset" }
+flate2 = { version = "1.0.25", features = ["zlib"], default-features = false }
 generate-random = { git = "https://github.com/minetest-rust/generate-random", features = ["enumset"], optional = true }
 mt_data_derive = { path = "derive" }
+paste = "1.0.11"
 rand = { version = "0.8.5", optional = true }
 serde = { version = "1.0.152", features = ["derive"], optional = true }
 serde_arrays = { version = "0.1.0", optional = true }
diff --git a/derive/.gitignore b/derive/.gitignore
new file mode 100644 (file)
index 0000000..4fffb2f
--- /dev/null
@@ -0,0 +1,2 @@
+/target
+/Cargo.lock
index 5597554bebaea6cc7a1c545308cbf50e82ba05e9..18b97c394b77ee8c3680dd74694d9cb9186fad5e 100644 (file)
@@ -7,7 +7,7 @@ edition = "2021"
 proc-macro = true
 
 [dependencies]
-syn = { version = "1", features = ["full", "extra-traits", "printing"] }
-quote = "1"
-proc-macro2 = "1"
 darling = "0.14.2"
+proc-macro2 = "1"
+quote = "1"
+syn = { version = "1", features = ["full", "extra-traits"] }
index 8daabf0c04703ecb8d0d8a2bdc35829471f528a2..5534651bcbb6c0e53e0cc7abfd6d4e733007147c 100644 (file)
-use darling::FromMeta;
-use proc_macro::{self, TokenStream};
+use darling::{FromField, FromMeta};
+use proc_macro::TokenStream;
+use proc_macro2::TokenStream as TokStr;
 use quote::{quote, ToTokens};
 use syn::{parse_macro_input, parse_quote};
 
 #[derive(Debug, FromMeta, Copy, Clone, Eq, PartialEq)]
 #[darling(rename_all = "snake_case")]
 enum To {
-       Clt,
-       Srv,
+    Clt,
+    Srv,
 }
 
 #[derive(Debug, FromMeta)]
 struct MacroArgs {
-       to: To,
-       repr: Option<syn::Type>,
-       tag: Option<String>,
-       content: Option<String>,
-       #[darling(default)]
-       enumset: bool,
+    to: To,
+    repr: Option<syn::Type>,
+    tag: Option<String>,
+    content: Option<String>,
+    #[darling(default)]
+    custom: bool,
+    #[darling(default)]
+    enumset: bool,
 }
 
 fn wrap_attr(attr: &mut syn::Attribute) {
-       match attr.path.get_ident().map(|i| i.to_string()).as_deref() {
-               Some("mt") => {
-                       let path = attr.path.clone();
-                       let tokens = attr.tokens.clone();
+    match attr.path.get_ident().map(|i| i.to_string()).as_deref() {
+        Some("mt") => {
+            let path = attr.path.clone();
+            let tokens = attr.tokens.clone();
 
-                       *attr = parse_quote! {
-                               #[cfg_attr(any(feature = "client", feature = "server"), #path #tokens)]
-                       };
-               }
-               Some("serde") => {
-                       let path = attr.path.clone();
-                       let tokens = attr.tokens.clone();
+            *attr = parse_quote! {
+                #[cfg_attr(any(feature = "client", feature = "server"), #path #tokens)]
+            };
+        }
+        Some("serde") => {
+            let path = attr.path.clone();
+            let tokens = attr.tokens.clone();
 
-                       *attr = parse_quote! {
-                               #[cfg_attr(feature = "serde", #path #tokens)]
-                       };
-               }
-               _ => {}
-       }
+            *attr = parse_quote! {
+                #[cfg_attr(feature = "serde", #path #tokens)]
+            };
+        }
+        _ => {}
+    }
 }
 
 #[proc_macro_attribute]
 pub fn mt_derive(attr: TokenStream, item: TokenStream) -> TokenStream {
-       let item2 = item.clone();
+    let item2 = item.clone();
 
-       let attr_args = parse_macro_input!(attr as syn::AttributeArgs);
-       let mut input = parse_macro_input!(item2 as syn::Item);
+    let attr_args = parse_macro_input!(attr as syn::AttributeArgs);
+    let mut input = parse_macro_input!(item2 as syn::Item);
 
-       let args = match MacroArgs::from_list(&attr_args) {
-               Ok(v) => v,
-               Err(e) => {
-                       return TokenStream::from(e.write_errors());
-               }
-       };
-
-       let (serializer, deserializer) = match args.to {
-               To::Clt => ("server", "client"),
-               To::Srv => ("client", "server"),
-       };
-
-       let mut out = quote! {
-               #[derive(Debug)]
-               #[cfg_attr(feature = "random", derive(GenerateRandom))]
-               #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-       };
-
-       macro_rules! iter {
-               ($t:expr, $f:expr) => {
-                       $t.iter_mut().for_each($f)
+    let args = match MacroArgs::from_list(&attr_args) {
+        Ok(v) => v,
+        Err(e) => {
+            return TokenStream::from(e.write_errors());
+        }
+    };
+
+    let (serializer, deserializer) = match args.to {
+        To::Clt => ("server", "client"),
+        To::Srv => ("client", "server"),
+    };
+
+    let mut out = quote! {
+        #[derive(Debug)]
+        #[cfg_attr(feature = "random", derive(GenerateRandom))]
+        #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+    };
+
+    macro_rules! iter {
+        ($t:expr, $f:expr) => {
+            $t.iter_mut().for_each($f)
+        };
+    }
+
+    match &mut input {
+        syn::Item::Enum(e) => {
+            iter!(e.attrs, wrap_attr);
+            iter!(e.variants, |v| {
+                iter!(v.attrs, wrap_attr);
+                iter!(v.fields, |f| iter!(f.attrs, wrap_attr));
+            });
+
+            let repr = args.repr.expect("missing repr for enum");
+
+            if args.enumset {
+                let repr_str = repr.to_token_stream().to_string();
+
+                out.extend(quote! {
+                    #[derive(EnumSetType)]
+                    #[enumset(repr = #repr_str, serialize_as_map)]
+                })
+            } else {
+                let has_payload = e
+                    .variants
+                    .iter()
+                    .find_map(|v| if v.fields.is_empty() { None } else { Some(()) })
+                    .is_some();
+
+                if has_payload {
+                    let tag = args.tag.expect("missing tag for enum with payload");
+
+                    out.extend(quote! {
+                        #[cfg_attr(feature = "serde", serde(tag = #tag))]
+                    });
+
+                    if let Some(content) = args.content {
+                        out.extend(quote! {
+                            #[cfg_attr(feature = "serde", serde(content = #content))]
+                        });
+                    }
+                } else {
+                    out.extend(quote! {
+                        #[derive(Copy, Eq)]
+                    });
+                }
+
+                out.extend(quote! {
+                    #[repr(#repr)]
+                    #[derive(Clone, PartialEq)]
+                });
+
+                if !args.custom {
+                    out.extend(quote! {
+                        #[cfg_attr(feature = #serializer, derive(MtSerialize))]
+                        #[cfg_attr(feature = #deserializer, derive(MtDeserialize))]
+                    });
+                }
+            }
+
+            out.extend(quote! {
+                #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
+            });
+        }
+        syn::Item::Struct(s) => {
+            iter!(s.attrs, wrap_attr);
+            iter!(s.fields, |f| iter!(f.attrs, wrap_attr));
+
+            out.extend(quote! {
+                #[derive(Clone, PartialEq)]
+            });
+
+            if !args.custom {
+                out.extend(quote! {
+                    #[cfg_attr(feature = #serializer, derive(MtSerialize))]
+                    #[cfg_attr(feature = #deserializer, derive(MtDeserialize))]
+                });
+            }
+        }
+        _ => panic!("only enum and struct supported"),
+    }
+
+    out.extend(input.to_token_stream());
+    out.into()
+}
+
+#[derive(Debug, Default, FromField)]
+#[darling(attributes(mt))]
+#[darling(default)]
+struct MtArgs {
+    const8: Option<u8>,
+    const16: Option<u16>,
+    const32: Option<u32>,
+    const64: Option<u64>,
+    size8: bool,
+    size16: bool,
+    size32: bool,
+    size64: bool,
+    len0: bool,
+    len8: bool,
+    len16: bool,
+    len32: bool,
+    len64: bool,
+    utf16: bool, // TODO
+    zlib: bool,
+    default: bool,
+}
+
+fn get_cfg(args: &MtArgs) -> syn::Type {
+    let mut ty: syn::Type = parse_quote! { mt_data::DefaultCfg  };
+
+    if args.len0 {
+        ty = parse_quote! { mt_data::NoLen };
+    }
+
+    macro_rules! impl_len {
+        ($name:ident, $T:ty) => {
+            if args.$name {
+                ty = parse_quote! { $T  };
+            }
+        };
+    }
+
+    impl_len!(len8, u8);
+    impl_len!(len16, u16);
+    impl_len!(len32, u32);
+    impl_len!(len64, u64);
+
+    if args.utf16 {
+        ty = parse_quote! { mt_data::Utf16<#ty> };
+    }
+
+    ty
+}
+
+/*
+fn is_ident(path: &syn::Path, ident: &str) -> bool {
+    matches!(path.segments.first().map(|p| &p.ident), Some(idt) if idt == ident)
+}
+
+fn get_type_generics<const N: usize>(path: &syn::Path) -> Option<[&syn::Type; N]> {
+    use syn::{AngleBracketedGenericArguments as Args, PathArguments::AngleBracketed};
+
+    path.segments
+        .first()
+        .map(|seg| match &seg.arguments {
+            AngleBracketed(Args { args, .. }) => args
+                .iter()
+                .flat_map(|arg| match arg {
+                    syn::GenericArgument::Type(t) => Some(t),
+                    _ => None,
+                })
+                .collect::<Vec<_>>()
+                .try_into()
+                .ok(),
+            _ => None,
+        })
+        .flatten()
+}
+*/
+
+fn serialize_fields(fields: &syn::Fields) -> TokStr {
+    let mut code: TokStr = (match fields {
+               syn::Fields::Named(fs) => fs
+                       .named
+                       .iter()
+                       .map(|f| (f.ident.as_ref().unwrap().to_token_stream(), f))
+                       .collect(),
+               syn::Fields::Unnamed(fs) => fs
+                       .unnamed
+                       .iter()
+                       .enumerate()
+                       .map(|(i, f)| (i.to_token_stream(), f))
+                       .collect(),
+               syn::Fields::Unit => Vec::new(),
+       }).into_iter().map(|(ident, field)| {
+               let args = match MtArgs::from_field(field) {
+                       Ok(v) => v,
+                       Err(e) => return e.write_errors(),
                };
-       }
-
-       match &mut input {
-               syn::Item::Enum(e) => {
-                       iter!(e.attrs, wrap_attr);
-                       iter!(e.variants, |v| {
-                               iter!(v.attrs, wrap_attr);
-                               iter!(v.fields, |f| iter!(f.attrs, wrap_attr));
-                       });
-
-                       let repr = args.repr.expect("missing repr for enum");
-
-                       if args.enumset {
-                               let repr_str = repr.to_token_stream().to_string();
-
-                               out.extend(quote! {
-                                       #[derive(EnumSetType)]
-                                       #[enumset(repr = #repr_str, serialize_as_map)]
-                               })
-                       } else {
-                               let has_payload = e
-                                       .variants
-                                       .iter()
-                                       .find_map(|v| if v.fields.is_empty() { None } else { Some(()) })
-                                       .is_some();
-
-                               if has_payload {
-                                       let tag = args.tag.expect("missing tag for enum with payload");
-
-                                       out.extend(quote! {
-                                               #[derive(Clone)]
-                                               #[cfg_attr(feature = "serde", serde(tag = #tag))]
-                                       });
 
-                                       if let Some(content) = args.content {
-                                               out.extend(quote! {
-                                                       #[cfg_attr(feature = "serde", serde(content = #content))]
-                                               });
-                                       }
-                               } else {
-                                       out.extend(quote! {
-                                               #[derive(Copy, Clone, PartialEq, Eq)]
+               let mut code = TokStr::new();
+
+               macro_rules! impl_const {
+                       ($name:ident) => {
+                               if let Some(x) = args.$name {
+                                       code.extend(quote! {
+                                               #x.mt_serialize::<mt_data::DefaultCfg>(writer)?;
                                        });
                                }
+                       };
+               }
 
-                               out.extend(quote! {
-                                       #[repr(#repr)]
-                                       #[cfg_attr(feature = #serializer, derive(MtSerialize))]
-                                       #[cfg_attr(feature = #deserializer, derive(MtDeserialize))]
-                               });
-                       }
+               impl_const!(const8);
+               impl_const!(const16);
+               impl_const!(const32);
+               impl_const!(const64);
+
+               let cfg = get_cfg(&args);
+               code.extend(quote! {
+                       mt_data::MtSerialize::mt_serialize::<#cfg>(&self.#ident, writer)?;
+               });
 
-                       out.extend(quote! {
-                               #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
-                       });
+               if args.zlib {
+                       code = quote! {
+                               let mut writer = {
+                                       let mut writer = mt_data::flate2::write::ZlibEncoder(writer, flate2::Compression::default());
+                                       #code
+                                       writer.finish()?
+                               };
+                       };
                }
-               syn::Item::Struct(s) => {
-                       iter!(s.attrs, wrap_attr);
-                       iter!(s.fields, |f| iter!(f.attrs, wrap_attr));
-
-                       out.extend(quote! {
-                               #[derive(Clone)]
-                               #[cfg_attr(feature = #serializer, derive(MtSerialize))]
-                               #[cfg_attr(feature = #deserializer, derive(MtDeserialize))]
-                       });
+
+               macro_rules! impl_size {
+                       ($name:ident, $T:ty) => {
+                               if args.$name {
+                                       code = quote! {
+                                               {
+                                                       let buf = {
+                                                               let mut writer = Vec::new();
+                                                               #code
+                                                               writer
+                                                       };
+
+                                                       TryInto::<$T>::try_into(buf.len())?.mt_serialize::<mt_data::DefaultCfg>();
+                                               }
+                                       };
+                               }
+                       };
                }
-               _ => panic!("only enum and struct supported"),
-       }
 
-       out.extend(input.to_token_stream());
-       out.into()
+               impl_size!(size8, u8);
+               impl_size!(size16, u16);
+               impl_size!(size32, u32);
+               impl_size!(size64, u64);
+
+               code
+       }).collect();
+
+    code.extend(quote! {
+        Ok(())
+    });
+
+    code
 }
 
 #[proc_macro_derive(MtSerialize, attributes(mt))]
 pub fn derive_serialize(input: TokenStream) -> TokenStream {
-       let syn::DeriveInput { ident, .. } = parse_macro_input!(input);
-       let output = quote! {
-               impl MtSerialize for #ident {
-                       fn mt_serialize<W: std::io::Write>(&self, writer: &mut W) -> Result<(), mt_data::SerializeError> {
-                               Err(mt_data::SerializeError::Unimplemented)
+    let input = parse_macro_input!(input as syn::DeriveInput);
+    let ident = &input.ident;
+
+    let code = match &input.data {
+        syn::Data::Enum(_e) => quote! {
+            Err(mt_data::SerializeError::Unimplemented)
+        },
+        syn::Data::Struct(s) => serialize_fields(&s.fields),
+        _ => {
+            panic!("only enum and struct supported");
+        }
+    };
+
+    quote! {
+               #[automatically_derived]
+               impl mt_data::MtSerialize for #ident {
+                       fn mt_serialize<C: MtCfg>(&self, writer: &mut impl std::io::Write) -> Result<(), mt_data::SerializeError> {
+                               #code
                        }
                }
-       };
-       output.into()
+       }.into()
 }
 
 #[proc_macro_derive(MtDeserialize, attributes(mt))]
 pub fn derive_deserialize(input: TokenStream) -> TokenStream {
-       let syn::DeriveInput { ident, .. } = parse_macro_input!(input);
-       quote! {
-               impl MtDeserialize for #ident {
-                       fn mt_deserialize<R: std::io::Read>(reader: &mut R) -> Result<Self, mt_data::DeserializeError> {
+    let syn::DeriveInput { ident, .. } = parse_macro_input!(input);
+    quote! {
+               #[automatically_derived]
+               impl mt_data::MtDeserialize for #ident {
+                       fn mt_deserialize<C: MtCfg>(reader: &mut impl std::io::Read) -> Result<Self, mt_data::DeserializeError> {
                                Err(mt_data::DeserializeError::Unimplemented)
                        }
                }
index 143b397b3be213005dc4eb5a89c81f802ce73ffa..6258b3457e2f01bd8fa5d1a28a18e69ad864067a 100644 (file)
@@ -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;
index 6a4b3c68a59af6122da4901407789b59cdb3e373..b93f7ee4c63d218f41398ab677d772561ea89b6b 100644 (file)
@@ -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,
 }
index c7a206ad0c423b50a8df1bd836353e3207567b7e..f0598a9f65a845b347f958bd77723de10425b290 100644 (file)
@@ -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
 
index 354bbb42673a3dec0f1a4c3499ae528f64e3f8ff..e83a8b88d732b2d37de01823f0b4fa06cebd56cf 100644 (file)
@@ -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,