diff options
| -rw-r--r-- | .gitignore | 4 | ||||
| -rw-r--r-- | Cargo.toml | 20 | ||||
| -rw-r--r-- | src/deserialize.rs | 106 | ||||
| -rw-r--r-- | src/lib.rs | 19 | ||||
| -rw-r--r-- | src/nbt.rs | 534 | ||||
| -rw-r--r-- | src/protocol.rs | 349 | ||||
| -rw-r--r-- | src/serialize.rs | 24 | ||||
| -rw-r--r-- | src/status.rs | 115 | ||||
| -rw-r--r-- | src/testdata/bigtest.nbt | bin | 0 -> 507 bytes | |||
| -rw-r--r-- | src/types.rs | 1020 | ||||
| -rw-r--r-- | src/utils.rs | 132 | ||||
| -rw-r--r-- | src/uuid.rs | 185 | ||||
| -rw-r--r-- | src/v1_15_2.rs | 2391 |
13 files changed, 4899 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f859244 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +Cargo.lock +/.idea +*.iml
\ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..064313f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "mc-proto-rs" +version = "0.1.0" +authors = ["Joey Sacchini <joey@sacchini.net>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde_json = "1.0" +regex = "1" +lazy_static = "1.4" +rand = "0.7" +flate2 = "1.0.17" +base64 = "0.12.3" +paste = "1.0.1" + +[dependencies.serde] +version = "1.0.116" +features = [ "derive" ]
\ No newline at end of file diff --git a/src/deserialize.rs b/src/deserialize.rs new file mode 100644 index 0000000..694c6e5 --- /dev/null +++ b/src/deserialize.rs @@ -0,0 +1,106 @@ +use crate::types::VarInt; +use std::string::FromUtf8Error; + +#[derive(Debug)] +pub enum DeserializeErr { + Eof, + VarNumTooLong(Vec<u8>), + NegativeLength(VarInt), + BadStringEncoding(FromUtf8Error), + InvalidBool(u8), + NbtUnknownTagType(u8), + NbtBadLength(isize), + NbtInvalidStartTag(u8), + CannotUnderstandValue(String), + FailedJsonDeserialize(String) +} + +impl<'b, R> Into<DeserializeResult<'b, R>> for DeserializeErr { + #[inline] + fn into(self) -> DeserializeResult<'b, R> { + Err(self) + } +} + +pub struct Deserialized<'b, R> { + pub value: R, + pub data: &'b [u8], +} + +impl<'b, R> Into<DeserializeResult<'b, R>> for Deserialized<'b, R> { + #[inline] + fn into(self) -> DeserializeResult<'b, R> { + Ok(self) + } +} + +impl<'b, R> Deserialized<'b, R> { + #[inline] + pub fn create(value: R, data: &'b [u8]) -> Self { + Deserialized { + value, + data, + } + } + + #[inline] + pub fn ok(value: R, rest: &'b [u8]) -> DeserializeResult<'b, R> { + Self::create(value, rest).into() + } + + #[inline] + pub fn replace<T>(self, other: T) -> Deserialized<'b, T> { + Deserialized{ + value: other, + data: self.data, + } + } + + #[inline] + pub fn map<F, T>(self, f: F) -> Deserialized<'b, T> where F: FnOnce(R) -> T { + Deserialized{ + value: f(self.value), + data: self.data, + } + } + + #[inline] + pub fn try_map<F, T>(self, f: F) -> DeserializeResult<'b, T> where + F: FnOnce(R) -> Result<T, DeserializeErr> + { + match f(self.value) { + Ok(new_value) => Ok(Deserialized{ + value: new_value, + data: self.data, + }), + Err(err) => Err(err) + } + } + + #[inline] + pub fn and_then<F, T>(self, f: F) -> DeserializeResult<'b, T> where + F: FnOnce(R, &'b[u8]) -> DeserializeResult<'b, T> + { + f(self.value, self.data) + } +} + + +impl<'b, R> From<(R, &'b [u8])> for Deserialized<'b, R> { + fn from(v: (R, &'b [u8])) -> Self { + let (value, data) = v; + Deserialized { + value, + data, + } + } +} + +pub type DeserializeResult<'b, R> += Result< + Deserialized<'b, R>, + DeserializeErr>; + +pub trait Deserialize: Sized { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<Self>; +}
\ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..b04090f --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,19 @@ +#![feature(impl_trait_in_bindings)] +#![feature(const_fn)] +#![feature(test)] + +#[cfg(test)] +extern crate test; + +mod serialize; +mod deserialize; +pub mod utils; +pub mod protocol; +pub mod uuid; +pub mod nbt; +pub mod types; +pub mod v1_15_2; +pub mod status; + +pub use serialize::*; +pub use deserialize::*;
\ No newline at end of file diff --git a/src/nbt.rs b/src/nbt.rs new file mode 100644 index 0000000..ac2d39c --- /dev/null +++ b/src/nbt.rs @@ -0,0 +1,534 @@ +use std::fmt; +use crate::{DeserializeResult, DeserializeErr, Deserialized}; +use crate::utils::{read_short, take, read_int, read_long, read_one_byte, write_long, write_int, write_short}; + +#[derive(Clone, Debug, PartialEq)] +pub struct NamedTag { + pub name: String, + pub payload: Tag, +} + +impl NamedTag { + pub fn root_compound_tag_from_bytes(data: &[u8]) -> DeserializeResult<NamedTag> { + read_nbt_data(data) + } + + pub fn is_end(&self) -> bool { + match self.payload { + Tag::End => true, + _ => false, + } + } +} + +impl fmt::Display for NamedTag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("TAG_{}('{}'): ", self.payload.tag_type_name(), self.name))?; + self.payload.write_contents(f) + } +} + + +#[derive(Clone, Debug, PartialEq)] +pub enum Tag { + Byte(i8), + Short(i16), + Int(i32), + Long(i64), + Float(f32), + Double(f64), + ByteArray(Vec<u8>), + String(String), + List(Vec<Tag>), + Compound(Vec<NamedTag>), + IntArray(Vec<i32>), + LongArray(Vec<i64>), + End, +} + +impl fmt::Display for Tag { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_fmt(format_args!("TAG_{}: ", self.tag_type_name()))?; + self.write_contents(f) + } +} + +impl Tag { + pub fn with_name(self, name: &str) -> NamedTag { + NamedTag { + name: name.into(), + payload: self, + } + } + + pub fn tag_type_name(&self) -> &str { + match self { + Tag::Byte(_) => "Byte", + Tag::Short(_) => "Short", + Tag::Int(_) => "Int", + Tag::Long(_) => "Long", + Tag::Float(_) => "Float", + Tag::Double(_) => "Double", + Tag::ByteArray(_) => "Byte_Array", + Tag::String(_) => "String", + Tag::List(_) => "List", + Tag::Compound(_) => "Compound", + Tag::IntArray(_) => "Int_Array", + Tag::LongArray(_) => "Long_Array", + Tag::End => "END", + } + } + + fn write_contents(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Tag::Byte(v) => f.write_fmt(format_args!("{}", *v)), + Tag::Short(v) => f.write_fmt(format_args!("{}", *v)), + Tag::Int(v) => f.write_fmt(format_args!("{}", *v)), + Tag::Long(v) => f.write_fmt(format_args!("{}L", *v)), + Tag::Float(v) => f.write_fmt(format_args!("{}", *v)), + Tag::Double(v) => f.write_fmt(format_args!("{}", *v)), + Tag::ByteArray(v) => f.write_fmt(format_args!("[{} bytes]", v.len())), + Tag::String(v) => f.write_fmt(format_args!("\"{}\"", v)), + Tag::List(v) => { + let out = write_contents(v); + f.write_str(out.as_str()) + } + Tag::Compound(v) => { + let out = write_contents(v); + f.write_str(out.as_str()) + } + Tag::IntArray(v) => f.write_fmt(format_args!("[{} ints]", v.len())), + Tag::LongArray(v) => f.write_fmt(format_args!("[{} longs]", v.len())), + Tag::End => f.write_str("END"), + } + } +} + +#[inline] +fn write_contents<F>(contents: &Vec<F>) -> String where F: fmt::Display { + format!("{} entries\n{{\n{}\n}}", contents.len(), contents.iter() + .flat_map(move |elem| elem.to_string().split("\n").map(String::from).collect::<Vec<String>>()) + .map(move |line| " ".to_owned() + line.as_str()) + .collect::<Vec<String>>() + .join("\n")) +} + +// deserialization first + +// reads from the root level +fn read_nbt_data(data: &[u8]) -> DeserializeResult<NamedTag> { + let Deserialized { value: tag_type_id, data: _ } = read_one_byte(data)?; + match tag_type_id { + 0x0A => read_named_tag(data), + other => Err(DeserializeErr::NbtInvalidStartTag(other)), + } +} + +// reads any named tag: read id -> read name -> read tag with id -> name tag with name +#[inline] +pub fn read_named_tag(data: &[u8]) -> DeserializeResult<NamedTag> { + let Deserialized { value: tag_type_id, data } = read_one_byte(data)?; + if tag_type_id == 0x00 { // tag end + Deserialized::ok(Tag::End.with_name(""), data) + } else { + let Deserialized { value: name, data } = read_string(data)?; + Ok(read_tag(tag_type_id, data)?.map(move |payload| NamedTag { name, payload })) + } +} + +// reads any tag (given it's id) +#[inline] +pub fn read_tag(tag_type_id: u8, data: &[u8]) -> DeserializeResult<Tag> { + match tag_type_id { + 0x00 => Deserialized::ok(Tag::End, data), + 0x01 => read_tag_byte(data), + 0x02 => read_tag_short(data), + 0x03 => read_tag_int(data), + 0x04 => read_tag_long(data), + 0x05 => read_tag_float(data), + 0x06 => read_tag_double(data), + 0x07 => read_tag_byte_array(data), + 0x08 => read_tag_string(data), + 0x09 => read_tag_list(data), + 0x0A => read_tag_compound(data), + 0x0B => read_tag_int_array(data), + 0x0C => read_tag_long_array(data), + other => Err(DeserializeErr::NbtUnknownTagType(other)), + } +} + +#[inline] +fn read_tag_byte(data: &[u8]) -> DeserializeResult<Tag> { + Ok(read_one_byte(data)?.map(move |byte| Tag::Byte(byte as i8))) +} + +#[inline] +fn read_tag_short(data: &[u8]) -> DeserializeResult<Tag> { + Ok(read_short(data)?.map(move |i| Tag::Short(i as i16))) +} + +#[inline] +fn read_tag_int(data: &[u8]) -> DeserializeResult<Tag> { + Ok(read_int(data)?.map(move |i| Tag::Int(i as i32))) +} + +#[inline] +fn read_tag_long(data: &[u8]) -> DeserializeResult<Tag> { + Ok(read_long(data)?.map(move |i| Tag::Long(i as i64))) +} + +#[inline] +fn read_tag_float(data: &[u8]) -> DeserializeResult<Tag> { + Ok(read_int(data)?.map(move |i| Tag::Float(f32::from_bits(i as u32)))) +} + +#[inline] +fn read_tag_double(data: &[u8]) -> DeserializeResult<Tag> { + Ok(read_long(data)?.map(move |i| Tag::Double(f64::from_bits(i as u64)))) +} + +#[inline] +fn read_tag_byte_array(data: &[u8]) -> DeserializeResult<Tag> { + Ok(read_int(data)?.and_then(move |size, rest| take(size as usize)(rest))? + .map(move |arr| Tag::ByteArray(Vec::from(arr)))) +} + +#[inline] +fn read_tag_string(data: &[u8]) -> DeserializeResult<Tag> { + Ok(read_string(data)?.map(move |str| Tag::String(str))) +} + +fn read_tag_list(data: &[u8]) -> DeserializeResult<Tag> { + let Deserialized { value: contents_tag_type_id, data } = read_one_byte(data)?; + let Deserialized { value: list_length, data } = read_int(data)?; + if list_length <= 0 { + if contents_tag_type_id != 0x00 { + Err(DeserializeErr::NbtBadLength(list_length as isize)) + } else { + Deserialized::ok(Tag::List(vec!()), data) + } + } else { + let mut out_vec = Vec::with_capacity(list_length as usize); + let mut remaining_data = data; + for _ in 0..list_length { + let Deserialized { value: element, data: rest } = read_tag(contents_tag_type_id, &remaining_data)?; + out_vec.push(element); + remaining_data = rest; + } + + Deserialized::ok(Tag::List(out_vec), remaining_data) + } +} + +fn read_tag_compound(data: &[u8]) -> DeserializeResult<Tag> { + let mut out = Vec::new(); + let mut remaining_data = data; + loop { + let Deserialized { value: elem, data: rest } = read_named_tag(remaining_data)?; + remaining_data = rest; + if elem.is_end() { + break; + } + out.push(elem); + } + + Deserialized::ok(Tag::Compound(out), remaining_data) +} + +#[inline] +fn read_tag_int_array(data: &[u8]) -> DeserializeResult<Tag> { + read_array_tag( + data, + move |data| Ok(read_int(data)?.map(move |r| r as i32)), + Tag::IntArray) +} + +#[inline] +fn read_tag_long_array(data: &[u8]) -> DeserializeResult<Tag> { + read_array_tag( + data, + move |data| Ok(read_long(data)?.map(move |r| r as i64)), + Tag::LongArray) +} + +#[inline] +fn read_array_tag<'a, R, F, M>(data: &'a [u8], parser: F, finalizer: M) -> DeserializeResult<'a, Tag> where + F: Fn(&'a [u8]) -> DeserializeResult<'a, R>, + M: Fn(Vec<R>) -> Tag +{ + let Deserialized { value: count, data } = read_int(data)?.map(move |v| v as i32); + if count < 0 { + Err(DeserializeErr::NbtBadLength(count as isize)) + } else { + let mut out = Vec::with_capacity(count as usize); + let mut data_remaining = data; + for _ in 0..count { + let Deserialized { value: elem, data: rest } = parser(data_remaining)?; + data_remaining = rest; + out.push(elem); + } + + Deserialized::ok(finalizer(out), data_remaining) + } +} + +#[inline] +fn read_string(data: &[u8]) -> DeserializeResult<String> { + read_short(data)? + .and_then(move |length, data| + take(length as usize)(data))? + .try_map(move |bytes| + String::from_utf8(Vec::from(bytes)).map_err(move |err| { + DeserializeErr::BadStringEncoding(err) + })) +} + +// serialize +impl NamedTag { + pub fn bytes(&self) -> Vec<u8> { + let type_id = self.payload.id(); + if type_id == 0x00 { + vec!(0x00) + } else { + let payload_bytes = self.payload.bytes(); + let name_len = self.name.len(); + let name_len_bytes = write_short(name_len as u16); + let mut out = Vec::with_capacity(1 + name_len_bytes.len() + name_len + payload_bytes.len()); + out.push(type_id); + out.extend_from_slice(&name_len_bytes); + out.extend(self.name.bytes()); + out.extend(payload_bytes); + out + } + } +} + +impl Tag { + pub fn id(&self) -> u8 { + match self { + Tag::Byte(_) => 0x01, + Tag::Short(_) => 0x02, + Tag::Int(_) => 0x03, + Tag::Long(_) => 0x04, + Tag::Float(_) => 0x05, + Tag::Double(_) => 0x06, + Tag::ByteArray(_) => 0x07, + Tag::String(_) => 0x08, + Tag::List(_) => 0x09, + Tag::Compound(_) => 0x0A, + Tag::IntArray(_) => 0x0B, + Tag::LongArray(_) => 0x0C, + Tag::End => 0x00, + } + } + + pub fn bytes(&self) -> Vec<u8> { + match self { + Tag::Byte(b) => vec!(*b as u8), + Tag::Short(v) => Vec::from(write_short(*v as u16)), + Tag::Int(v) => Vec::from(write_int(*v as u32)), + Tag::Long(v) => Vec::from(write_long(*v as u64)), + Tag::Float(v) => Vec::from(write_int(v.to_bits())), + Tag::Double(v) => Vec::from(write_long(v.to_bits())), + Tag::ByteArray(v) => { + let n = v.len(); + let mut out = Vec::with_capacity(n + 4); + let size_bytes = write_int(n as u32); + out.extend_from_slice(&size_bytes); + out.extend(v); + out + } + Tag::String(v) => { + let n = v.len(); + let mut out = Vec::with_capacity(n + 2); + let size_bytes = write_short(n as u16); + out.extend_from_slice(&size_bytes); + out.extend(v.bytes()); + out + } + Tag::List(v) => { + let count = v.len(); + let elem_id = { + if count == 0 { + 0x00 + } else { + let mut id = None; + for elem in v { + let elem_id = elem.id(); + if let Some(old_id) = id.replace(elem_id) { + if old_id != elem_id { + panic!("list contains tags of different types, cannot serialize"); + } + } + } + + id.expect("there must be some elements in the list") + } + }; + + let mut out = Vec::new(); + out.push(elem_id); + let count_bytes = write_int(count as u32); + out.extend_from_slice(&count_bytes); + out.extend(v.iter().flat_map(move |elem| elem.bytes().into_iter())); + out + } + Tag::Compound(v) => { + let mut out = Vec::new(); + for elem in v { + out.extend(elem.bytes()); + } + out.extend(Tag::End.with_name("").bytes()); + out + } + Tag::IntArray(v) => { + let n = v.len(); + let mut out = Vec::with_capacity(4 + (4 * n)); + let n_bytes = write_int(n as u32); + out.extend_from_slice(&n_bytes); + for value in v { + let bytes = write_int(*value as u32); + out.extend_from_slice(&bytes); + } + out + } + Tag::LongArray(v) => { + let n = v.len(); + let mut out = Vec::with_capacity(4 + (8 * n)); + let n_bytes = write_int(n as u32); + out.extend_from_slice(&n_bytes); + for value in v { + let bytes = write_long(*value as u64); + out.extend_from_slice(&bytes); + } + out + } + Tag::End => Vec::default(), + } + } +} + +// test +#[cfg(test)] +mod tests { + use super::*; + use std::io::Read; + use flate2::read::GzDecoder; + use std::fs::File; + + #[test] + fn test_read_bignbt_example() { + let actual = read_bigtest(); + + let expected = Tag::Compound(vec!( + Tag::Long(9223372036854775807).with_name("longTest"), + Tag::Short(32767).with_name("shortTest"), + Tag::String("HELLO WORLD THIS IS A TEST STRING ÅÄÖ!".into()).with_name("stringTest"), + Tag::Float(0.49823147).with_name("floatTest"), + Tag::Int(2147483647).with_name("intTest"), + Tag::Compound(vec!( + Tag::Compound(vec!( + Tag::String("Hampus".into()).with_name("name"), + Tag::Float(0.75).with_name("value"), + )).with_name("ham"), + Tag::Compound(vec!( + Tag::String("Eggbert".into()).with_name("name"), + Tag::Float(0.5).with_name("value"), + )).with_name("egg") + )).with_name("nested compound test"), + Tag::List(vec!( + Tag::Long(11), + Tag::Long(12), + Tag::Long(13), + Tag::Long(14), + Tag::Long(15), + )).with_name("listTest (long)"), + Tag::List(vec!( + Tag::Compound(vec!( + Tag::String("Compound tag #0".into()).with_name("name"), + Tag::Long(1264099775885).with_name("created-on"), + )), + Tag::Compound(vec!( + Tag::String("Compound tag #1".into()).with_name("name"), + Tag::Long(1264099775885).with_name("created-on"), + )) + )).with_name("listTest (compound)"), + Tag::Byte(127).with_name("byteTest"), + Tag::ByteArray(bigtest_generate_byte_array()).with_name("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))"), + Tag::Double(0.4931287132182315).with_name("doubleTest") + )).with_name("Level"); + + assert_eq!(actual, expected); + } + + #[test] + fn test_serialize_bigtest() { + let (unzipped, result) = read_bigtest_with_bytes(); + let serialized = result.bytes(); + assert_eq!(unzipped, serialized); + let Deserialized{value: unserialized, data: _} = NamedTag::root_compound_tag_from_bytes(serialized.as_slice()).expect("deserialize serialized nbt"); + assert_eq!(unserialized, result); + } + + #[test] + fn test_int_array() { + let original = Tag::Compound(vec!( + Tag::IntArray(vec!(1, 2, -5, 123127, -12373, 0, 0, 4, 2)).with_name("test ints") + )).with_name("test"); + + let bytes = original.bytes(); + let Deserialized{value: unserialized, data: _} = NamedTag::root_compound_tag_from_bytes(bytes.as_slice()).expect("deserialize int array"); + assert_eq!(original, unserialized); + } + + #[test] + fn test_long_array() { + let original = Tag::Compound(vec!( + Tag::LongArray(vec!(1, 2, -5, 123127999999, -1237399999, 0, 0, 4, 2)).with_name("test ints") + )).with_name("test"); + + let bytes = original.bytes(); + let Deserialized{value: unserialized, data: _} = NamedTag::root_compound_tag_from_bytes(bytes.as_slice()).expect("deserialize int array"); + assert_eq!(original, unserialized); + } + + #[test] + fn test_display() { + println!("{}", read_bigtest()); + } + + #[test] + fn test_debug() { + println!("{:?}", read_bigtest()); + } + + fn read_bigtest_with_bytes() -> (Vec<u8>, NamedTag) { + let unzipped = read_compressed_file("src/testdata/bigtest.nbt").expect("read nbt data"); + let Deserialized{value: result, data: rest} = NamedTag::root_compound_tag_from_bytes(unzipped.as_slice()).expect("deserialize nbt"); + assert_eq!(rest.len(), 0); + + (unzipped, result) + } + + fn read_bigtest() -> NamedTag { + let (_, result) = read_bigtest_with_bytes(); + result + } + + fn bigtest_generate_byte_array() -> Vec<u8> { + const COUNT: usize = 1000; + let mut out = Vec::with_capacity(COUNT); + for i in 0..COUNT { + out.push((((i * i * 255) + (i * 7)) % 100) as u8); + } + out + } + + fn read_compressed_file(at: &str) -> std::io::Result<Vec<u8>> { + let file = File::open(at)?; + let mut gz = GzDecoder::new(file); + let mut out = Vec::new(); + gz.read_to_end(&mut out)?; + Ok(out) + } +}
\ No newline at end of file diff --git a/src/protocol.rs b/src/protocol.rs new file mode 100644 index 0000000..01265df --- /dev/null +++ b/src/protocol.rs @@ -0,0 +1,349 @@ +use crate::{Serialize, Deserialize, DeserializeErr}; +use std::fmt::Debug; + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct ProtocolSpec { + pub name: String, + pub packets: Vec<ProtocolPacketSpec>, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct ProtocolPacketSpec { + pub state: String, + pub direction: String, + pub id: i32, + pub name: String, + pub fields: Vec<ProtocolPacketField>, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq)] +pub struct ProtocolPacketField { + pub name: String, + pub kind: String, +} + +pub trait PacketIdentifier: Clone + Debug + PartialEq + Serialize {} + +impl<T: Clone + Debug + PartialEq + Serialize> PacketIdentifier for T {} + +pub trait Packet<I: PacketIdentifier>: Serialize { + fn id(&self) -> I; + + fn mc_deserialize(raw: RawPacket<I>) -> Result<Self, PacketErr>; +} + +#[derive(Debug)] +pub enum PacketErr { + UnknownId(i32), + DeserializeFailed(DeserializeErr) +} + +#[derive(Debug, Clone, PartialEq)] +pub struct RawPacket<I> { + pub id: I, + pub data: Vec<u8>, +} + +pub trait ProtocolType: Serialize + Deserialize {} + +impl<T: Serialize + Deserialize> ProtocolType for T {} + +#[macro_export] +macro_rules! as_item { + ($i:item) => { $i }; +} + +#[macro_export] +macro_rules! __protocol_body_serialize_def_helper { + ($to: ident, $slf: ident, $fieldn: ident, $($field_rest: ident),+) => { + $to.serialize_other(&$slf.$fieldn)?; + $crate::__protocol_body_serialize_def_helper!($to, $slf, $($field_rest),+); + }; + + ( $to: ident, $slf: ident, $fieldn: ident ) => { + $to.serialize_other(&$slf.$fieldn) + }; +} + +#[macro_export] +macro_rules! __protocol_body_def_helper { + ($bodyt: ident { }) => { + #[derive(Debug, Clone, PartialEq, Default)] + pub struct $bodyt; + + impl Serialize for $bodyt { + fn mc_serialize<S: Serializer>(&self, _: &mut S) -> SerializeResult { + Ok(()) + } + } + + impl Deserialize for $bodyt { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + Deserialized::ok(Self::default(), data) + } + } + }; + ($bodyt: ident { $($fname: ident: $ftyp: ty ),+ }) => { + $crate::as_item! { + #[derive(Debug, Clone, PartialEq)] + pub struct $bodyt { + $(pub $fname: $ftyp),+ + } + } + + impl Serialize for $bodyt { + fn mc_serialize<S: Serializer>(&self, to: &mut S) -> SerializeResult { + $( + to.serialize_other(&self.$fname)?; + )+ + Ok(()) + } + } + + impl Deserialize for $bodyt { + fn mc_deserialize(_rest: &[u8]) -> DeserializeResult<'_, Self> { + $(let Deserialized{ value: $fname, data: _rest } = <$ftyp>::mc_deserialize(_rest)?;)+ + + Deserialized::ok(Self{ $($fname),+ }, _rest) + } + } + } +} + +#[macro_export] +macro_rules! define_protocol { + ($packett: ident, $directiont: ident, $statet: ident, $idt: ident, $idi: ident => { $($nam: ident, $id: literal, $state: ident, $direction: ident => $body: ident { $($fnam: ident: $ftyp: ty),* }),*}) => { + #[derive(Debug, PartialEq, Eq, Clone, Copy)] + pub struct $idi { + pub id: $idt, + pub state: $statet, + pub direction: $directiont + } + + impl crate::Serialize for $idi { + fn mc_serialize<S: Serializer>(&self, to: &mut S) -> SerializeResult { + VarInt(self.id).mc_serialize(to) + } + } + + impl From<($idt, $statet, $directiont)> for $idi { + fn from(tuple: ($idt, $statet, $directiont)) -> Self { + let (id, state, direction) = tuple; + Id { id, state, direction } + } + } + + $crate::as_item! { + #[derive(Debug, PartialEq, Clone)] + pub enum $packett { + $($nam($body)),*, + } + } + + impl crate::protocol::Packet<$idi> for $packett { + fn id(&self) -> $idi { + use self::$packett::*; + use self::$statet::*; + use self::$directiont::*; + + match self { + $($nam(_) => ($id, $state, $direction)),* + }.into() + } + + fn mc_deserialize(raw: crate::protocol::RawPacket<$idi>) -> + Result<Self, crate::protocol::PacketErr> + { + use self::$packett::*; + use self::$statet::*; + use self::$directiont::*; + use crate::protocol::PacketErr::*; + use crate::Deserialize; + + let id = raw.id; + let data = raw.data.as_slice(); + + match (id.id, id.state, id.direction) { + $(($id, $state, $direction) => Ok($nam($body::mc_deserialize(data).map_err(DeserializeFailed)?.value))),*, + other => Err(UnknownId(other.0)), + } + } + } + + impl crate::Serialize for $packett { + fn mc_serialize<S: crate::Serializer>(&self, to: &mut S) -> crate::SerializeResult { + use self::$packett::*; + match self { + $($nam(body) => to.serialize_other(body)),+ + } + } + } + + impl $packett { + pub fn describe() -> crate::protocol::ProtocolSpec { + crate::protocol::ProtocolSpec { + name: stringify!($packett).to_owned(), + packets: vec!( + $(crate::protocol::ProtocolPacketSpec{ + state: stringify!($state).to_owned(), + direction: stringify!($direction).to_owned(), + id: $id, + name: stringify!($nam).to_owned(), + fields: vec!( + $(crate::protocol::ProtocolPacketField{ + name: stringify!($fnam).to_owned(), + kind: stringify!($ftyp).to_owned(), + }),* + ) + }),*, + ) + } + } + } + + $($crate::__protocol_body_def_helper!($body { $($fnam: $ftyp),* });)* + }; +} + +#[macro_export] +macro_rules! proto_enum_with_type { + ($typ: ty, $from_nam: ident, $as_nam: ident, $fmt: literal, $typname: ident, $(($bval: literal, $nam: ident)),*) => { + $crate::as_item! { + #[derive(PartialEq, Clone, Copy)] + pub enum $typname { + $($nam),* + } + } + + impl Serialize for $typname { + fn mc_serialize<S: Serializer>(&self, to: &mut S) -> SerializeResult { + to.serialize_other(&self.$as_nam()) + } + } + + impl Deserialize for $typname { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + <$typ>::mc_deserialize(data)?.and_then(move |id, rest| { + Self::$from_nam(id).map(move |val| { + Deserialized::ok(val, rest) + }).unwrap_or_else(|| Err(DeserializeErr::CannotUnderstandValue(format!("invalid {} {}", stringify!($typname), id)))) + }) + } + } + + impl Into<$typ> for $typname { + fn into(self) -> $typ { + use $typname::*; + match self { + $($nam => $bval.into()),*, + } + } + } + + impl std::fmt::Display for $typname { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, $fmt, self.name(), self.$as_nam())?; + Ok(()) + } + } + + impl std::fmt::Debug for $typname { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, $fmt, self.name(), self.$as_nam())?; + Ok(()) + } + } + + impl $typname { + pub fn $from_nam(b: $typ) -> Option<Self> { + use $typname::*; + match b.into() { + $($bval => Some($nam)),*, + _ => None + } + } + + pub fn name(&self) -> &str { + use $typname::*; + match self { + $($nam => stringify!($nam)),+, + } + } + + pub fn $as_nam(&self) -> $typ { + (*self).into() + } + } + } +} + +#[macro_export] +macro_rules! proto_byte_enum { + ($typname: ident, $($bval: literal :: $nam: ident),*) => { + proto_enum_with_type!(u8, from_byte, as_byte, "{}(0x{:02x})", $typname, $(($bval, $nam)),*); + } +} + +#[macro_export] +macro_rules! proto_varint_enum { + ($typname: ident, $($bval: literal :: $nam: ident),*) => { + proto_enum_with_type!(VarInt, from_varint, as_varint, "{}({:?})", $typname, $(($bval, $nam)),*); + } +} + +#[macro_export] +macro_rules! proto_int_enum { + ($typname: ident, $($bval: literal :: $nam: ident),*) => { + proto_enum_with_type!(i32, fro |
