From 5b8e64e398ce5cc3cd8067545836416485b1f7ea Mon Sep 17 00:00:00 2001 From: Joey Sacchini Date: Tue, 29 Sep 2020 15:36:00 -0400 Subject: init commit --- .gitignore | 4 + Cargo.toml | 20 + src/deserialize.rs | 106 ++ src/lib.rs | 19 + src/nbt.rs | 534 +++++++++++ src/protocol.rs | 349 +++++++ src/serialize.rs | 24 + src/status.rs | 115 +++ src/testdata/bigtest.nbt | Bin 0 -> 507 bytes src/types.rs | 1020 ++++++++++++++++++++ src/utils.rs | 132 +++ src/uuid.rs | 185 ++++ src/v1_15_2.rs | 2391 ++++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 4899 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 src/deserialize.rs create mode 100644 src/lib.rs create mode 100644 src/nbt.rs create mode 100644 src/protocol.rs create mode 100644 src/serialize.rs create mode 100644 src/status.rs create mode 100644 src/testdata/bigtest.nbt create mode 100644 src/types.rs create mode 100644 src/utils.rs create mode 100644 src/uuid.rs create mode 100644 src/v1_15_2.rs 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 "] +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), + NegativeLength(VarInt), + BadStringEncoding(FromUtf8Error), + InvalidBool(u8), + NbtUnknownTagType(u8), + NbtBadLength(isize), + NbtInvalidStartTag(u8), + CannotUnderstandValue(String), + FailedJsonDeserialize(String) +} + +impl<'b, R> Into> 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> 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(self, other: T) -> Deserialized<'b, T> { + Deserialized{ + value: other, + data: self.data, + } + } + + #[inline] + pub fn map(self, f: F) -> Deserialized<'b, T> where F: FnOnce(R) -> T { + Deserialized{ + value: f(self.value), + data: self.data, + } + } + + #[inline] + pub fn try_map(self, f: F) -> DeserializeResult<'b, T> where + F: FnOnce(R) -> Result + { + match f(self.value) { + Ok(new_value) => Ok(Deserialized{ + value: new_value, + data: self.data, + }), + Err(err) => Err(err) + } + } + + #[inline] + pub fn and_then(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; +} \ 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 { + 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), + String(String), + List(Vec), + Compound(Vec), + IntArray(Vec), + LongArray(Vec), + 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(contents: &Vec) -> 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::>()) + .map(move |line| " ".to_owned() + line.as_str()) + .collect::>() + .join("\n")) +} + +// deserialization first + +// reads from the root level +fn read_nbt_data(data: &[u8]) -> DeserializeResult { + 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 { + 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 { + 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 { + Ok(read_one_byte(data)?.map(move |byte| Tag::Byte(byte as i8))) +} + +#[inline] +fn read_tag_short(data: &[u8]) -> DeserializeResult { + Ok(read_short(data)?.map(move |i| Tag::Short(i as i16))) +} + +#[inline] +fn read_tag_int(data: &[u8]) -> DeserializeResult { + Ok(read_int(data)?.map(move |i| Tag::Int(i as i32))) +} + +#[inline] +fn read_tag_long(data: &[u8]) -> DeserializeResult { + Ok(read_long(data)?.map(move |i| Tag::Long(i as i64))) +} + +#[inline] +fn read_tag_float(data: &[u8]) -> DeserializeResult { + Ok(read_int(data)?.map(move |i| Tag::Float(f32::from_bits(i as u32)))) +} + +#[inline] +fn read_tag_double(data: &[u8]) -> DeserializeResult { + Ok(read_long(data)?.map(move |i| Tag::Double(f64::from_bits(i as u64)))) +} + +#[inline] +fn read_tag_byte_array(data: &[u8]) -> DeserializeResult { + 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 { + Ok(read_string(data)?.map(move |str| Tag::String(str))) +} + +fn read_tag_list(data: &[u8]) -> DeserializeResult { + 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 { + 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 { + 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 { + 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) -> 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 { + 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 { + 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 { + 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, 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 { + 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> { + 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, +} + +#[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, +} + +#[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 PacketIdentifier for T {} + +pub trait Packet: Serialize { + fn id(&self) -> I; + + fn mc_deserialize(raw: RawPacket) -> Result; +} + +#[derive(Debug)] +pub enum PacketErr { + UnknownId(i32), + DeserializeFailed(DeserializeErr) +} + +#[derive(Debug, Clone, PartialEq)] +pub struct RawPacket { + pub id: I, + pub data: Vec, +} + +pub trait ProtocolType: Serialize + Deserialize {} + +impl 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(&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(&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(&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 + { + 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(&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(&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 { + 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, from_int, as_int, "{}(0x{:02x})", $typname, $(($bval, $nam)),*); + } +} + +#[macro_export] +macro_rules! proto_byte_flag { + ($typname: ident, $($bval: literal :: $nam: ident),*) => { + #[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] + pub struct $typname(pub u8); + + impl $typname { + $(paste::paste! { + pub fn [](&self) -> bool { + self.0 & $bval != 0 + } + })* + + $(paste::paste! { + pub fn [](&mut self, value: bool) { + if value { + self.0 |= $bval; + } else { + self.0 ^= $bval; + } + } + })* + + $(paste::paste! { + pub fn [](mut self, value: bool) -> Self { + if value { + self.0 |= $bval; + } else { + self.0 ^= $bval; + } + + self + } + })* + } + + impl Serialize for $typname { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + to.serialize_byte(self.0) + } + } + + impl Deserialize for $typname { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + Ok(u8::mc_deserialize(data)?.map(move |b| $typname(b))) + } + } + } +} \ No newline at end of file diff --git a/src/serialize.rs b/src/serialize.rs new file mode 100644 index 0000000..5eb8f2a --- /dev/null +++ b/src/serialize.rs @@ -0,0 +1,24 @@ +#[derive(Debug)] +pub enum SerializeErr { + FailedJsonEncode(String), + InconsistentPlayerActions(String) +} + +pub type SerializeResult = Result<(), SerializeErr>; + +pub trait Serialize: Sized { + fn mc_serialize(&self, to: &mut S) -> SerializeResult; +} + +pub trait Serializer: Sized { + + fn serialize_bytes(&mut self, data: &[u8]) -> SerializeResult; + + fn serialize_byte(&mut self, byte: u8) -> SerializeResult { + self.serialize_bytes(vec!(byte).as_slice()) + } + + fn serialize_other(&mut self, other: &S) -> SerializeResult { + other.mc_serialize(self) + } +} \ No newline at end of file diff --git a/src/status.rs b/src/status.rs new file mode 100644 index 0000000..b8f9029 --- /dev/null +++ b/src/status.rs @@ -0,0 +1,115 @@ +use crate::types::Chat; +use crate::{SerializeResult, SerializeErr, Serialize as McSerialize, Deserialize as McDeserialize, DeserializeResult, DeserializeErr}; +use crate::uuid::UUID4; +use serde::{Serialize, Serializer, Deserialize, Deserializer}; +use std::fmt; + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct StatusSpec { + pub version: StatusVersionSpec, + pub players: StatusPlayersSpec, + pub description: Chat, + #[serde(skip_serializing_if = "Option::is_none")] + pub favicon: Option, +} + +impl McSerialize for StatusSpec { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + serde_json::to_string(self).map_err(move |err| { + SerializeErr::FailedJsonEncode(format!("failed to serialize json status {}", err)) + })?.mc_serialize(to) + } +} + +impl McDeserialize for StatusSpec { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + String::mc_deserialize(data)?.try_map(move |v| { + serde_json::from_str(v.as_str()).map_err(move |err| { + DeserializeErr::CannotUnderstandValue(format!("failed to deserialize json status {}", err)) + }) + }) + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct StatusVersionSpec { + pub name: String, + pub protocol: i32, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct StatusPlayersSpec { + pub max: i32, + pub online: i32, + #[serde(skip_serializing_if = "Vec::is_empty")] + #[serde(default = "Vec::default")] + pub sample: Vec, +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct StatusPlayerSampleSpec { + pub name: String, + pub id: UUID4, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct StatusFaviconSpec { + pub content_type: String, + pub data: Vec, +} + +impl Serialize for StatusFaviconSpec { + fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where + S: Serializer + { + let data_base64 = base64::encode(self.data.as_slice()); + let content = format!("data:{};base64,{}", self.content_type, data_base64); + serializer.serialize_str(content.as_str()) + } +} + +impl<'de> Deserialize<'de> for StatusFaviconSpec { + fn deserialize(deserializer: D) -> Result>::Error> where + D: Deserializer<'de> + { + struct Visitor; + impl serde::de::Visitor<'_> for Visitor { + type Value = StatusFaviconSpec; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "a string with base64 data for favicon") + } + + fn visit_str(self, v: &str) -> Result { + use lazy_static::lazy_static; + use regex::Regex; + // regex to parse valid base64 content + const PATTERN: &str = r"^data:([A-Za-z/]+);base64,([-A-Za-z0-9+/]*={0,3})$"; + lazy_static! { + static ref RE: Regex = Regex::new(PATTERN).expect("regex is valid"); + } + + // try to use regex on the input + // RE.captures_iter(v).next() means "try to get the first capture iterator" + // then we take that iterator, get(1), and if 1 exists, get(2), and if both exist, + // then we try to parse the base64, and drop the error if one occurs. We then + // wrap the content_type and parsed data in StatusFaviconSpec + // then we convert the option to a result using map and unwrap_or_else + let mut captures: regex::CaptureMatches<'_, '_> = RE.captures_iter(v); + captures.next().and_then(move |captures| + captures.get(1).and_then(move |content_type| + captures.get(2).and_then(move |raw_base64| + base64::decode(raw_base64.as_str().as_bytes()).map(move |data| { + StatusFaviconSpec { + content_type: content_type.as_str().to_owned(), + data, + } + }).ok()))) + .map(move |result| Ok(result)) + .unwrap_or_else(|| Err(serde::de::Error::invalid_value(serde::de::Unexpected::Str(v), &self))) + } + } + + deserializer.deserialize_str(Visitor {}) + } +} \ No newline at end of file diff --git a/src/testdata/bigtest.nbt b/src/testdata/bigtest.nbt new file mode 100644 index 0000000..dc3769b Binary files /dev/null and b/src/testdata/bigtest.nbt differ diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..f62ede9 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,1020 @@ +// ... PRIMITIVE TYPES ... + +use crate::*; +use crate::utils::*; +use crate::uuid::UUID4; + +// bool +impl Serialize for bool { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + to.serialize_byte(if *self { 1 } else { 0 }) + } +} + +impl Deserialize for bool { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + read_one_byte(data)?.try_map(move |b| { + match b { + 0x00 => Ok(false), + 0x01 => Ok(true), + other => Err(DeserializeErr::InvalidBool(other)) + } + }) + } +} + +// u8 +impl Serialize for u8 { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + to.serialize_byte(*self) + } +} + +impl Deserialize for u8 { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + read_one_byte(data) + } +} + +// i8 +impl Serialize for i8 { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + to.serialize_byte(*self as u8) + } +} + +impl Deserialize for i8 { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + Ok(read_one_byte(data)?.map(move |byte| byte as i8)) + } +} + +// u16 +impl Serialize for u16 { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + let data = write_short(*self); + to.serialize_bytes(&data[..]) + } +} + +impl Deserialize for u16 { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + read_short(data) + } +} + +// i16 +impl Serialize for i16 { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + (*self as u16).mc_serialize(to) + } +} + +impl Deserialize for i16 { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + u16::mc_deserialize(data)?.map(move |other| other as i16).into() + } +} + +// int +impl Serialize for i32 { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + let data = write_int(*self as u32); + to.serialize_bytes(&data[..]) + } +} + +impl Deserialize for i32 { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + Ok(read_int(data)?.map(move |v| v as i32)) + } +} + +// long +impl Serialize for i64 { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + let data = write_long(*self as u64); + to.serialize_bytes(&data[..]) + } +} + +impl Deserialize for i64 { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + Ok(read_long(data)?.map(move |v| v as i64)) + } +} + +// float +impl Serialize for f32 { + + //noinspection ALL + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + let data = (*self).to_be_bytes(); + to.serialize_bytes(&data[..]) + } +} + +impl Deserialize for f32 { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + i32::mc_deserialize(data)?.map(move |r| f32::from_bits(r as u32)).into() + } +} + +// double +impl Serialize for f64 { + //noinspection ALL + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + let data = (*self).to_be_bytes(); + to.serialize_bytes(&data[..]) + } +} + +impl Deserialize for f64 { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + i64::mc_deserialize(data)?.map(move |r| f64::from_bits(r as u64)).into() + } +} + +// VAR INT AND VAR LONG +const VAR_INT_BYTES: usize = 5; +const VAR_LONG_BYTES: usize = 10; + +const DESERIALIZE_VAR_INT: impl for<'b> Fn(&'b [u8]) -> DeserializeResult<'b, u64> = deserialize_var_num(VAR_INT_BYTES); +const DESERIALIZE_VAR_LONG: impl for<'b> Fn(&'b [u8]) -> DeserializeResult<'b, u64> = deserialize_var_num(VAR_LONG_BYTES); + +#[derive(Copy, Clone, PartialOrd, PartialEq, Debug, Default, Hash, Ord, Eq)] +pub struct VarInt(pub i32); + +impl Serialize for VarInt { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + let mut data = [0u8; VAR_INT_BYTES]; + to.serialize_bytes(serialize_var_num((self.0 as u32) as u64, &mut data)) + } +} + +impl Deserialize for VarInt { + fn mc_deserialize(orig_data: &[u8]) -> DeserializeResult { + Ok(DESERIALIZE_VAR_INT(orig_data)?.map(move |v| VarInt(v as i32))) + } +} + +impl Into for VarInt { + fn into(self) -> i32 { + self.0 + } +} + +impl From for VarInt { + fn from(v: i32) -> Self { + Self(v) + } +} + +impl Into for VarInt { + fn into(self) -> usize { + self.0 as usize + } +} + +impl From for VarInt { + fn from(v: usize) -> Self { + Self(v as i32) + } +} + +impl std::fmt::Display for VarInt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "VarInt({})", self.0) + } +} + +#[derive(Copy, Clone, PartialOrd, PartialEq, Debug, Default, Hash, Ord, Eq)] +pub struct VarLong(pub i64); + +impl Serialize for VarLong { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + let mut data = [0u8; VAR_LONG_BYTES]; + to.serialize_bytes(serialize_var_num(self.0 as u64, &mut data)) + } +} + +impl Deserialize for VarLong { + fn mc_deserialize(orig_data: &[u8]) -> DeserializeResult<'_, Self> { + Ok(DESERIALIZE_VAR_LONG(orig_data)?.map(move |v| VarLong(v as i64))) + } +} + +fn serialize_var_num(data: u64, out: &mut [u8]) -> &[u8] { + let mut v: u64 = data; + let mut byte_idx = 0; + let mut has_more = true; + while has_more { + if byte_idx == out.len() { + panic!("tried to write too much data for Var num"); + } + + let mut v_byte = (v & 0x7F) as u8; + v >>= 7; + has_more = v != 0; + if has_more { + v_byte |= 0x80; + } + + out[byte_idx] = v_byte; + byte_idx += 1; + } + + &out[..byte_idx] +} + +const fn deserialize_var_num(max_bytes: usize) -> impl for<'b> Fn(&'b [u8]) -> DeserializeResult<'b, u64> { + move |orig_data| { + let mut data = orig_data; + let mut v: u64 = 0; + let mut bit_place: usize = 0; + let mut i: usize = 0; + let mut has_more = true; + + while has_more { + if i == max_bytes { + return DeserializeErr::VarNumTooLong(Vec::from(&orig_data[..i])).into(); + } + let Deserialized { value: byte, data: rest } = read_one_byte(data)?; + data = rest; + has_more = byte & 0x80 != 0; + v |= ((byte as u64) & 0x7F) << bit_place; + bit_place += 7; + i += 1; + } + + Deserialized::ok(v, data) + } +} + +// STRING +impl Serialize for String { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + to.serialize_other(&VarInt(self.len() as i32))?; + to.serialize_bytes(self.as_bytes()) + } +} + +impl Deserialize for String { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + VarInt::mc_deserialize(data)?.and_then(move |length, rest| { + if length.0 < 0 { + Err(DeserializeErr::NegativeLength(length)) + } else { + take(length.0 as usize)(rest)?.try_map(move |taken| { + String::from_utf8(taken.to_vec()) + .map_err(DeserializeErr::BadStringEncoding) + }) + } + }) + } +} + +// position +#[derive(Clone, Copy, PartialEq, Hash, Debug)] +pub struct IntPosition { + pub x: i32, + pub y: i16, + pub z: i32, +} + +impl Serialize for IntPosition { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + let x_raw = if self.x < 0 { + (self.x + 0x2000000) as u64 | 0x2000000 + } else { + self.x as u64 + } & 0x3FFFFFF; + let z_raw = if self.z < 0 { + (self.z + 0x2000000) as u64 | 0x2000000 + } else { + self.z as u64 + } & 0x3FFFFFF; + let y_raw = if self.y < 0 { + (self.y + 0x800) as u64 | 0x800 + } else { + self.y as u64 + } & 0xFFF; + + let data_raw = ((x_raw << 38) | (z_raw << 12) | y_raw) as u64; + let data_i64 = data_raw as i64; + to.serialize_other(&data_i64) + } +} + +impl Deserialize for IntPosition { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + let Deserialized{ value: raw, data } = i64::mc_deserialize(data)?; + let raw_unsigned = raw as u64; + let mut x = ((raw_unsigned >> 38) as u32) & 0x3FFFFFF; + let mut z = ((raw_unsigned >> 12) & 0x3FFFFFF) as u32; + let mut y = ((raw_unsigned & 0xFFF) as u16) & 0xFFF; + + if (x & 0x2000000) != 0 { // is the 26th bit set + // if so, treat the rest as a positive integer, and treat 26th bit as -2^25 + // 2^25 == 0x2000000 + // 0x1FFFFFF == 2^26 - 1 (all places set to 1 except 26th place) + x = (((x & 0x1FFFFFF) as i32) - 0x2000000) as u32; + } + if (y & 0x800) != 0 { + y = (((y & 0x7FF) as i16) - 0x800) as u16; + } + if (z & 0x2000000) != 0 { + z = (((z & 0x1FFFFFF) as i32) - 0x2000000) as u32; + } + + Deserialized::ok(IntPosition{ + x: x as i32, + y: y as i16, + z: z as i32 + }, data) + } +} + +// angle +#[derive(Copy, Clone, PartialEq, Hash, Debug)] +pub struct Angle { + pub value: u8 +} + +impl Serialize for Angle { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + to.serialize_byte(self.value) + } +} + +impl Deserialize for Angle { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + Ok(read_one_byte(data)?.map(move |b| { + Angle { value: b } + })) + } +} + +// UUID + +impl Serialize for UUID4 { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + let bytes = self.to_u128().to_be_bytes(); + to.serialize_bytes(&bytes[..]) + } +} + +impl Deserialize for UUID4 { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + take(16)(data)?.map(move |bytes| { + let raw = (bytes[0] as u128) << 120 | + (bytes[1] as u128) << 112 | + (bytes[2] as u128) << 104 | + (bytes[3] as u128) << 96 | + (bytes[4] as u128) << 88 | + (bytes[5] as u128) << 80 | + (bytes[6] as u128) << 72 | + (bytes[7] as u128) << 64 | + (bytes[8] as u128) << 56 | + (bytes[9] as u128) << 48 | + (bytes[10] as u128) << 40 | + (bytes[11] as u128) << 32 | + (bytes[12] as u128) << 24 | + (bytes[13] as u128) << 16 | + (bytes[14] as u128) << 8 | + bytes[15] as u128; + UUID4::from(raw) + }).into() + } +} + +// NBT + +#[derive(Clone, PartialEq, Debug)] +pub struct NamedNbtTag { + pub root: nbt::NamedTag +} + +impl Serialize for NamedNbtTag { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + let bytes = self.root.bytes(); + to.serialize_bytes(bytes.as_slice()) + } +} + +impl Deserialize for NamedNbtTag { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + Ok(nbt::NamedTag::root_compound_tag_from_bytes(data)?.map(move |root| NamedNbtTag { root })) + } +} + +impl From for NamedNbtTag { + fn from(root: nbt::NamedTag) -> Self { + Self { root } + } +} + +impl Into for NamedNbtTag { + fn into(self) -> nbt::NamedTag { + self.root + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct FixedInt { + raw: i32 +} + +impl Serialize for FixedInt { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + to.serialize_other(&self.raw) + } +} + +impl Deserialize for FixedInt { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + Ok(i32::mc_deserialize(data)?.map(move |raw| { + FixedInt{ raw } + })) + } +} + +impl FixedInt { + pub fn new(data: f64, fractional_bytes: usize) -> Self { + Self { raw: (data * ((1 << fractional_bytes) as f64)) as i32 } + } + + pub fn into_float(self, fractional_bytes: usize) -> f64 { + (self.raw as f64) / ((1 << fractional_bytes) as f64) + } +} + +// chat +#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)] +pub struct Chat { + pub text: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub bold: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub italic: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub underlined: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub strikethrough: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub obfuscated: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub color: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub extra: Option> +} + +impl ToString for Chat { + fn to_string(&self) -> String { + self.extra.as_ref() + .into_iter() + .flat_map(|v| v.into_iter()) + .map(|item| item.to_string()) + .fold(self.text.clone(), |acc, v| acc + v.as_str()) + } +} + +const SECTION_SYMBOL: char = '§'; + +#[derive(PartialOrd, PartialEq, Debug, Copy, Clone)] +pub enum ColorCode { + Black, + DarkBlue, + DarkGreen, + DarkAqua, + DarkRed, + DarkPurple, + Gold, + Gray, + DarkGray, + Blue, + Green, + Aqua, + Red, + LightPurple, + Yellow, + White +} + +impl ColorCode { + pub fn from_code(i: &char) -> Option { + match i { + '0' => Some(ColorCode::Black), + '1' => Some(ColorCode::DarkBlue), + '2' => Some(ColorCode::DarkGreen), + '3' => Some(ColorCode::DarkAqua), + '4' => Some(ColorCode::DarkRed), + '5' => Some(ColorCode::DarkPurple), + '6' => Some(ColorCode::Gold), + '7' => Some(ColorCode::Gray), + '8' => Some(ColorCode::DarkGray), + '9' => Some(ColorCode::Blue), + 'a' => Some(ColorCode::Green), + 'b' => Some(ColorCode::Aqua), + 'c' => Some(ColorCode::Red), + 'd' => Some(ColorCode::LightPurple), + 'e' => Some(ColorCode::Yellow), + 'f' => Some(ColorCode::White), + _ => None + } + } + + pub fn to_code(&self) -> char { + match self { + ColorCode::Black => '0', + ColorCode::DarkBlue => '1', + ColorCode::DarkGreen => '2', + ColorCode::DarkAqua => '3', + ColorCode::DarkRed => '4', + ColorCode::DarkPurple => '5', + ColorCode::Gold => '6', + ColorCode::Gray => '7', + ColorCode::DarkGray => '8', + ColorCode::Blue => '9', + ColorCode::Green => 'a', + ColorCode::Aqua => 'b', + ColorCode::Red => 'c', + ColorCode::LightPurple => 'd', + ColorCode::Yellow => 'e', + ColorCode::White => 'f', + } + } + + pub fn from_name(name: &str) -> Option { + match name.to_ascii_lowercase().as_str() { + "black" => Some(ColorCode::Black), + "dark_blue" => Some(ColorCode::DarkBlue), + "dark_green" => Some(ColorCode::DarkGreen), + "dark_aqua" => Some(ColorCode::DarkAqua), + "dark_red" => Some(ColorCode::DarkRed), + "dark_purple" => Some(ColorCode::DarkPurple), + "gold" => Some(ColorCode::Gold), + "gray" => Some(ColorCode::Gray), + "dark_gray" => Some(ColorCode::DarkGray), + "blue" => Some(ColorCode::Blue), + "green" => Some(ColorCode::Green), + "aqua" => Some(ColorCode::Aqua), + "red" => Some(ColorCode::Red), + "light_purple" => Some(ColorCode::LightPurple), + "yellow" => Some(ColorCode::Yellow), + "white" => Some(ColorCode::White), + _ => None + } + } + + pub fn name(&self) -> &str { + match self { + ColorCode::Black => "black", + ColorCode::DarkBlue => "dark_blue", + ColorCode::DarkGreen => "dark_green", + ColorCode::DarkAqua => "dark_aqua", + ColorCode::DarkRed => "dark_red", + ColorCode::DarkPurple => "dark_purple", + ColorCode::Gold => "gold", + ColorCode::Gray => "gray", + ColorCode::DarkGray => "dark_gray", + ColorCode::Blue => "blue", + ColorCode::Green => "green", + ColorCode::Aqua => "aqua", + ColorCode::Red => "red", + ColorCode::LightPurple => "light_purple", + ColorCode::Yellow => "yellow", + ColorCode::White => "white", + } + } +} + +#[derive(PartialOrd, PartialEq, Debug, Copy, Clone)] +pub enum Formatter { + Color(ColorCode), + Obfuscated, + Bold, + Strikethrough, + Underline, + Italic, + Reset +} + +impl Formatter { + pub fn from_code(i: &char) -> Option { + match i.to_ascii_lowercase() { + 'k' => Some(Formatter::Obfuscated), + 'l' => Some(Formatter::Bold), + 'm' => Some(Formatter::Strikethrough), + 'n' => Some(Formatter::Underline), + 'o' => Some(Formatter::Italic), + 'r' => Some(Formatter::Reset), + _ => ColorCode::from_code(i).map(Formatter::Color) + } + } + + pub fn code(&self) -> char { + match self { + Formatter::Color(c) => c.to_code(), + Formatter::Obfuscated => 'k', + Formatter::Bold => 'l', + Formatter::Strikethrough => 'm', + Formatter::Underline => 'n', + Formatter::Italic => 'o', + Formatter::Reset => 'r' + } + } + + pub fn from_name(name: &str) -> Option { + match name.to_ascii_lowercase().as_str() { + "obfuscated" => Some(Formatter::Obfuscated), + "bold" => Some(Formatter::Bold), + "strikethrough" => Some(Formatter::Strikethrough), + "underline" => Some(Formatter::Underline), + "italic" => Some(Formatter::Italic), + "reset" => Some(Formatter::Reset), + _ => ColorCode::from_name(name).map(Formatter::Color) + } + } + + pub fn name(&self) -> &str { + match self { + Formatter::Obfuscated => "obfuscated", + Formatter::Bold => "bold", + Formatter::Strikethrough => "strikethrough", + Formatter::Underline => "underline", + Formatter::Italic => "italic", + Formatter::Reset => "reset", + Formatter::Color(c) => c.name(), + } + } +} + +impl ToString for Formatter { + fn to_string(&self) -> String { + vec!(SECTION_SYMBOL, self.code()).into_iter().collect() + } +} + +impl Chat { + pub fn to_traditional(&self) -> String { + self.to_traditional_parts(Vec::::new().as_ref(), None) + } + + fn to_traditional_parts(&self, formatters: &Vec, color: Option) -> String { + let mut own_formatters = formatters.clone(); + Self::update_formatter(&mut own_formatters, Formatter::Bold, &self.bold); + Self::update_formatter(&mut own_formatters, Formatter::Italic, &self.italic); + Self::update_formatter(&mut own_formatters, Formatter::Underline, &self.underlined); + Self::update_formatter(&mut own_formatters, Formatter::Strikethrough, &self.strikethrough); + Self::update_formatter(&mut own_formatters, Formatter::Obfuscated, &self.obfuscated); + + let own_color_option = self.color.as_ref() + .map(String::as_str) + .and_then(ColorCode::from_name) + .or(color); + + let own_color = own_color_option + .map(Formatter::Color) + .map(|f| f.to_string()); + + let own_formatter = + own_formatters + .clone() + .into_iter() + .map(|f| f.to_string()) + .fold(String::new(), |acc, v| acc + v.as_str()); + + let own_color_str = match own_color { + Some(v) => v, + None => String::new() + }; + + let own_out = own_formatter + own_color_str.as_str() + self.text.as_str(); + + self.extra.as_ref() + .into_iter() + .flat_map(|v| v.into_iter()) + .map(|child| child.to_traditional_parts(&own_formatters, own_color_option)) + .fold(own_out, |acc, next| acc + next.as_str()) + } + + fn update_formatter(to: &mut Vec, formatter: Formatter, v: &Option) { + if !to.contains(&formatter) && v.unwrap_or(false) { + to.push(formatter) + } + } +} + +impl Serialize for Chat { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + serde_json::to_string(self).map_err(move |err| { + SerializeErr::FailedJsonEncode(format!("failed to serialize chat {:?}", err)) + })?.mc_serialize(to) + } +} + +impl Deserialize for Chat { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + String::mc_deserialize(data)?.try_map(move |str| { + serde_json::from_str(str.as_str()).map_err(move |err| { + DeserializeErr::FailedJsonDeserialize(format!("failed to deserialize chat {:?}", err)) + }) + }) + } +} + +#[derive(Default)] +pub struct BytesSerializer { + data: Vec +} + +impl Serializer for BytesSerializer { + fn serialize_bytes(&mut self, data: &[u8]) -> SerializeResult { + self.data.extend_from_slice(data); + Ok(()) + } +} + +impl BytesSerializer { + pub fn with_capacity(cap: usize) -> Self { + BytesSerializer{ + data: Vec::with_capacity(cap), + } + } + + pub fn into_bytes(self) -> Vec { + self.data + } +} + +impl Serialize for Option where T: Serialize { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + match self { + Some(value) => { + to.serialize_other(&true)?; + to.serialize_other(value) + }, + None => { + to.serialize_other(&false) + } + } + } +} + +impl Deserialize for Option where T: Deserialize { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + bool::mc_deserialize(data)?.and_then(move |is_present, data| { + if is_present { + Ok(T::mc_deserialize(data)?.map(move |component| Some(component))) + } else { + Deserialized::ok(None, data) + } + }) + } +} + +// SLOT +#[derive(Debug, PartialEq, Clone)] +pub struct Slot { + pub item_id: VarInt, + pub item_count: i8, + pub nbt: Option, +} + +impl Serialize for Slot { + fn mc_serialize(&self, to: &mut S) -> SerializeResult { + to.serialize_other(&self.item_id)?; + to.serialize_other(&self.item_count)?; + match self.nbt.as_ref() { + Some(nbt) => to.serialize_bytes(nbt.bytes().as_slice()), + None => to.serialize_byte(nbt::Tag::End.id()), + } + } +} + +impl Deserialize for Slot { + fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { + let Deserialized{ value: item_