diff options
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | derive/.gitignore | 2 | ||||
-rw-r--r-- | derive/Cargo.toml | 6 | ||||
-rw-r--r-- | derive/src/lib.rs | 436 | ||||
-rw-r--r-- | src/lib.rs | 315 | ||||
-rw-r--r-- | src/to_clt.rs | 14 | ||||
-rw-r--r-- | src/to_clt/hud.rs | 16 | ||||
-rw-r--r-- | src/to_srv.rs | 20 |
8 files changed, 664 insertions, 148 deletions
@@ -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 index 0000000..4fffb2f --- /dev/null +++ b/derive/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/derive/Cargo.toml b/derive/Cargo.toml index 5597554..18b97c3 100644 --- a/derive/Cargo.toml +++ b/derive/Cargo.toml @@ -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"] } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 8daabf0..5534651 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -1,168 +1,354 @@ -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) } } @@ -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, |