diff --git a/src/api/mod.rs b/src/api/mod.rs index 9d3ddcd..2ab9838 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -21,9 +21,11 @@ mod entities; mod foods; mod items; mod loot; +mod protocol; mod recipes; mod versions; +use crate::api::protocol::Protocol; pub use biomes::*; pub use blocks::*; pub use enchantments::*; @@ -47,6 +49,7 @@ pub struct Api { pub foods: Foods, pub biomes: Biomes, pub entities: Entities, + pub protocols: Protocol, } impl Api { @@ -68,6 +71,7 @@ impl Api { foods: Foods::new(Arc::clone(&version)), biomes: Biomes::new(Arc::clone(&version)), entities: Entities::new(Arc::clone(&version)), + protocols: Protocol::new(Arc::clone(&version)), } } } diff --git a/src/api/protocol.rs b/src/api/protocol.rs new file mode 100644 index 0000000..65358c6 --- /dev/null +++ b/src/api/protocol.rs @@ -0,0 +1,22 @@ +use crate::data::{get_version_specific_file, PROTOCOL_FILE}; +use crate::models::version::Version; +use crate::{DataError, DataResult}; +use std::sync::Arc; + + +/// Will not parse versions `21w07a`, `20w14a`, and `20w13b` +/// These snapshot versions have incompatible data types for the tags packet. + +pub struct Protocol { + version: Arc, +} + +impl Protocol { + pub fn new(version: Arc) -> Self { + Self { version } + } + pub fn get_protocol(&self) -> DataResult { + let content = get_version_specific_file(&self.version, PROTOCOL_FILE)?; + serde_json::from_str(&content).map_err(DataError::from) + } +} diff --git a/src/api/tests/mod.rs b/src/api/tests/mod.rs index 25646b0..55b4e36 100644 --- a/src/api/tests/mod.rs +++ b/src/api/tests/mod.rs @@ -10,6 +10,7 @@ mod entities; mod foods; mod items; mod loot; +mod protocol; mod recipes; mod versions; diff --git a/src/api/tests/protocol.rs b/src/api/tests/protocol.rs new file mode 100644 index 0000000..7eda49c --- /dev/null +++ b/src/api/tests/protocol.rs @@ -0,0 +1,29 @@ +use std::collections::HashMap; +use std::convert::TryInto; +use crate::api::protocol::Protocol; +use crate::api::tests::get_test_versions; +use crate::models::protocol::{PacketDataType}; +use std::sync::Arc; + +pub const VERSIONS_TO_SKIP: [&str; 3] = ["21w07a", "20w14a", "20w13b"]; + +#[test] +pub fn simple_test() { + let versions = get_test_versions(); + for x in versions { + if VERSIONS_TO_SKIP.contains(&x.minecraft_version.as_str()) { + continue; + } + let arc = Arc::new(x); + let protocol = Protocol::new(arc.clone()); + let protocol1 = protocol.get_protocol(); + match protocol1 { + Ok(data) => { + + } + Err(error) => { + panic!("Minecraft Version {} could not be parsed into a Protocol object: {}", arc.minecraft_version, error); + } + } + } +} \ No newline at end of file diff --git a/src/models/mod.rs b/src/models/mod.rs index 5e61955..a5eed0f 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -7,5 +7,6 @@ pub mod entity; pub mod entity_loot; pub mod food; pub mod item; +pub mod protocol; pub mod recipe; pub mod version; diff --git a/src/models/protocol/mod.rs b/src/models/protocol/mod.rs new file mode 100644 index 0000000..8eab803 --- /dev/null +++ b/src/models/protocol/mod.rs @@ -0,0 +1,136 @@ +pub mod types; +pub mod packet_mapper; + +use std::borrow::Cow; +use std::fmt; +use serde::{de, Deserialize, Deserializer}; +use serde::de::{MapAccess, Visitor}; +use serde_json::Value; +pub use packet_mapper::{PacketMapper, PacketSwitch, PacketMapperSwitch}; +pub use types::{BitField, NativeType, PacketDataType, PacketDataTypes}; + +#[derive(Deserialize, Debug, Clone)] +pub struct Protocol { + pub types: PacketDataTypes, + pub handshaking: PacketGrouping, + pub status: PacketGrouping, + pub login: PacketGrouping, + pub play: PacketGrouping, +} + +#[derive(Deserialize, Debug, Clone)] +pub struct PacketGrouping { + #[serde(rename = "toServer")] + pub to_server: PacketTypes, + #[serde(rename = "toClient")] + pub to_client: PacketTypes, +} + +#[derive(Debug, Clone)] +pub enum DataTypeReference { + Simple(String), + Complex { + name: String, + properties: Value, + }, +} + +impl Into for DataTypeReference { + fn into(self) -> PacketDataType { + let (name, properties) = match self { + DataTypeReference::Simple(simple) => { + (simple, Value::Null) + } + DataTypeReference::Complex { name, properties } => { + (name, properties) + } + }; + + PacketDataType::new(name.as_str(), Cow::Borrowed(&properties)). + or_else(|| { + let option = NativeType::new(name.as_str(), Cow::Borrowed(&properties)); + option.map(PacketDataType::Native) + }) + .unwrap_or_else(|| { + PacketDataType::Other { + name: Some(name.into()), + value: properties, + } + }) + } +} + +#[derive(Debug, Clone)] +pub struct Packet { + pub name: String, + pub data: PacketDataType, +} + +#[derive(Debug, Clone)] +pub struct PacketTypes { + pub packet_mapper: PacketMapperSwitch, + pub types: Vec, +} + + +impl<'de> Deserialize<'de> for PacketTypes { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PacketTypesVisitor; + + impl<'de> Visitor<'de> for PacketTypesVisitor { + type Value = PacketTypes; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Expected a map") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + while let Some(key) = map.next_key::()? { + if key.eq("types") { + let mut packets = Vec::new(); + let mut packet_mapper = None; + let value = map.next_value::()?; + if let Value::Object(obj) = value { + for (key, value) in obj.into_iter() { + if key.eq("packet") { + if let Value::Array(mut array) = value { + let value = array.pop().ok_or_else(|| de::Error::missing_field("missing content"))?; + let value: PacketMapperSwitch = serde_json::from_value(value).map_err(de::Error::custom)?; + packet_mapper = Some(value); + } else { + return Err(de::Error::custom("Invalid Packet Mapper")); + } + } else if let Value::Array( array) = value { + let value1 = Value::Array(vec![Value::String(key.clone()), Value::Array(array)]); + let inner_type = types::build_inner_type(value1); + packets.push(Packet { + name: key, + data: *inner_type, + }); + + } else { + return Err(de::Error::custom(format!("Invalid Packet Expected Array {}", key))); + } + } + } + + let packet_mapper = packet_mapper.ok_or_else(|| de::Error::missing_field("packet_mapper"))?; + return Ok(PacketTypes { + packet_mapper, + types: packets, + }); + } + } + Err(de::Error::custom("Expected a types")) + } + } + + deserializer.deserialize_map(PacketTypesVisitor) + } +} diff --git a/src/models/protocol/packet_mapper.rs b/src/models/protocol/packet_mapper.rs new file mode 100644 index 0000000..0649fe6 --- /dev/null +++ b/src/models/protocol/packet_mapper.rs @@ -0,0 +1,103 @@ +use std::collections::HashMap; +use std::convert::TryInto; +use std::fmt; +use std::iter::Map; +use std::num::ParseIntError; + +use serde::de::{SeqAccess, Visitor}; +use serde::{de, Deserialize, Deserializer}; +use serde_json::Value; + +#[derive(Deserialize, Debug, Clone)] +pub struct PacketMapper { + /// A Type + #[serde(rename = "type")] + pub map_type: String, + /// The first Value is a Hex value of the packet id. That is a string in the JSON. You can convert the map with the `i32::from_str_radix` (The ids do start with 0x) function. You can also just do try_into::() on the PacketMapper + /// The second Value is the name of the packet + pub mappings: HashMap, +} + +impl TryInto> for PacketMapper { + type Error = ParseIntError; + + fn try_into(self) -> Result, Self::Error> { + let mut map = HashMap::with_capacity(self.mappings.len()); + for (key, value) in self.mappings.into_iter() { + let key = i32::from_str_radix(key.trim_start_matches("0x"), 16)?; + map.insert(key, value); + } + Ok(map) + } +} + +#[derive(Deserialize, Debug, Clone)] +pub struct PacketSwitch { + #[serde(rename = "compareTo")] + pub compare_to: String, + /// First value is the name of the packet. Second is the name of the JSON object for the packet. + pub fields: HashMap, +} + +#[derive(Debug, Clone)] +pub struct PacketMapperSwitch { + pub mapper: PacketMapper, + pub switch: PacketSwitch, +} + + +impl<'de> Deserialize<'de> for PacketMapperSwitch { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PacketMapperSwitchVisitor; + + impl<'de> Visitor<'de> for PacketMapperSwitchVisitor { + type Value = PacketMapperSwitch; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Expected a sequence") + } + fn visit_seq(self, mut seq: A) -> Result + where + A: SeqAccess<'de>, + { + let mut mapper = None; + let mut switch = None; + while let Some(value) = seq.next_element::()? { + if let Value::Object(mut value) = value { + let value = value.remove("type").ok_or_else(|| de::Error::missing_field("type"))?; + if let Value::Array(mut array) = value { + let value = array.pop().ok_or_else(|| de::Error::missing_field("missing content"))?; + let key = array.pop().ok_or_else(|| de::Error::missing_field("missing key"))?; + if let Value::String(key) = key { + if key.eq("mapper") { + let value: PacketMapper = serde_json::from_value(value).map_err(de::Error::custom)?; + mapper = Some(value); + } else if key.eq("switch") { + let value: PacketSwitch = serde_json::from_value(value).map_err(de::Error::custom)?; + switch = Some(value); + } else { + return Err(de::Error::custom("unknown key")); + } + } else { + return Err(de::Error::custom("unknown key")); + } + } else { + return Err(de::Error::custom("Expected an array")); + } + } + } + let map_type = mapper.ok_or_else(|| de::Error::missing_field("mapper"))?; + let switch = switch.ok_or_else(|| de::Error::missing_field("switch"))?; + Ok(PacketMapperSwitch { + mapper: map_type, + switch, + }) + } + } + + deserializer.deserialize_seq(PacketMapperSwitchVisitor) + } +} diff --git a/src/models/protocol/types.rs b/src/models/protocol/types.rs new file mode 100644 index 0000000..86214f7 --- /dev/null +++ b/src/models/protocol/types.rs @@ -0,0 +1,537 @@ +use serde::de::Visitor; +use serde::{Deserialize, Deserializer}; +use serde_json::Value; +use std::borrow::Cow; +use std::collections::HashMap; + +#[derive(Deserialize, Debug, Clone)] +pub struct BitField { + pub name: String, + pub size: i64, + pub signed: bool, +} + +#[derive(Debug, Clone)] +pub enum SwitchType { + Packet(String), + Type(Box), + Unknown(Value), +} + +#[derive(Debug, Clone)] +pub enum TypeName { + Anonymous, + Named(String), +} + +impl From for TypeName { + fn from(value: String) -> Self { + Self::Named(value) + } +} + +impl Display for TypeName { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + TypeName::Anonymous => f.write_str("Anonymous"), + TypeName::Named(name) => f.write_str(name.as_str()), + } + } +} + +impl PartialEq for TypeName { + fn eq(&self, other: &String) -> bool { + if let TypeName::Named(name) = self { + name == other + } else { + false + } + } +} + +/// These data types should be available in every version. +/// However, they won't break anything if not present +/// This can also be known as the Native Types +#[derive(Debug, Clone)] +#[non_exhaustive] +pub enum NativeType { + /// Please read the following link for information on parsing https://wiki.vg/Protocol#VarInt_and_VarLong + VarInt, + PString { + count_type: Box, + }, + Buffer { + count_type: Box, + }, + Bool, + U8, + U16, + U32, + U64, + I8, + I16, + I32, + I64, + F32, + F64, + Uuid, + // Optional + Option(Box), + EntityMetadataLoop { + end_val: i64, + metadata_type: Box, + }, + TopBitSetTerminatedArray(Box), + BitField(Vec), + // A set of Name and The Type + Container(Vec<(TypeName, Box)>), + Switch { + compare_to: String, + fields: HashMap, + default: Option, + }, + Void, + Array { + count_type: Box, + array_type: Box, + }, + RestBuffer, + NBT, + OptionalNBT, +} + +impl Display for NativeType { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + let value = match self { + NativeType::Bool => "bool", + NativeType::U8 => "u8", + NativeType::U16 => "u16", + NativeType::U32 => "u32", + NativeType::U64 => "u64", + NativeType::I8 => "i8", + NativeType::I16 => "i16", + NativeType::I32 => "i32", + NativeType::I64 => "i64", + NativeType::F32 => "f32", + NativeType::F64 => "f64", + NativeType::Uuid => "uuid", + NativeType::Option(_) => "option", + NativeType::EntityMetadataLoop { .. } => "entityMetadataLoop", + NativeType::TopBitSetTerminatedArray(_) => "topbitsetterminatedarray", + NativeType::BitField(_) => "bitfield", + NativeType::Container(_) => "container", + NativeType::Switch { .. } => "switch", + NativeType::Array { .. } => "array", + NativeType::Void => "void", + NativeType::RestBuffer => "restbuffer", + NativeType::NBT => "nbt", + NativeType::OptionalNBT => "optionalnbt", + NativeType::VarInt => "varint", + NativeType::PString { .. } => "pstring", + NativeType::Buffer { .. } => "buffer", + }; + write!(f, "{}", value) + } +} + +impl NativeType { + pub fn contains_type(name: &str) -> bool { + matches!( + name, + "varint" + | "pstring" + | "buffer" + | "bool" + | "u8" + | "u16" + | "u32" + | "u64" + | "i8" + | "i16" + | "i32" + | "i64" + | "f32" + | "f64" + | "uuid" + | "option" + | "entityMetadataLoop" + | "topbitsetterminatedarray" + | "bitfield" + | "container" + | "switch" + | "void" + | "array" + | "restbuffer" + | "nbt" + | "optionalnbt" + ) + } + pub fn new(name: &str, layout: Cow<'_, Value>) -> Option { + match name { + "varint" => Some(NativeType::VarInt), + "pstring" => Self::new_pstring(layout), + "buffer" => Self::new_buffer(layout), + "bool" => Some(NativeType::Bool), + "u8" => Some(NativeType::U8), + "u16" => Some(NativeType::U16), + "u32" => Some(NativeType::U32), + "u64" => Some(NativeType::U64), + "i8" => Some(NativeType::I8), + "i16" => Some(NativeType::I16), + "i32" => Some(NativeType::I32), + "i64" => Some(NativeType::I64), + "f32" => Some(NativeType::F32), + "f64" => Some(NativeType::F64), + "uuid" => Some(NativeType::Uuid), + "option" => Some(NativeType::Option(build_inner_type(layout.into_owned()))), + "entityMetadataLoop" => Self::new_entity_metadata_loop(layout), + "topbitsetterminatedarray" => Self::new_top_bitsetterminated_array(layout), + "bitfield" => Self::new_bitfield(layout), + "container" => Self::new_container(layout), + + "switch" => Self::new_switch(layout), + "void" => Some(NativeType::Void), + "array" => Self::new_array(layout), + + "restbuffer" => Some(NativeType::RestBuffer), + "nbt" => Some(NativeType::NBT), + "optionalnbt" => Some(NativeType::OptionalNBT), + _ => None, + } + } + + pub fn new_pstring(layout: Cow) -> Option { + if let Value::Object(mut obj) = layout.into_owned() { + if let Value::String(count_type) = obj.remove("countType").unwrap_or_default() { + if let Some(count_type) = NativeType::new(&count_type, Cow::Owned(Value::Null)) { + return Some(NativeType::PString { + count_type: Box::new(count_type), + }); + } + } + } + None + } + + fn new_top_bitsetterminated_array(layout: Cow) -> Option { + if let Value::Object(mut layout) = layout.into_owned() { + let inner_type = layout.remove("type").unwrap_or_default(); + let inner_type = build_inner_type(inner_type); + return Some(NativeType::TopBitSetTerminatedArray(inner_type)); + } + None + } + + pub fn new_bitfield(layout: Cow) -> Option { + if let Value::Array(bit_fields) = layout.into_owned() { + let bit_fields_vec = bit_fields + .into_iter() + .map(|v| serde_json::from_value(v).unwrap()) + .collect(); + + Some(NativeType::BitField(bit_fields_vec)) + } else { + None + } + } + + pub fn new_switch(layout: Cow) -> Option { + if let Value::Object(mut layout) = layout.into_owned() { + return Some(NativeType::Switch { + compare_to: layout + .remove("compareTo") + .unwrap() + .as_str() + .unwrap_or_default() + .to_string(), + fields: layout + .remove("fields") + .and_then(|v| { + if let Value::Object(fields) = v { + Some( + fields + .into_iter() + .map(|(k, v)| { + if let Value::String(value) = v { + if value.starts_with("packet") { + (k, SwitchType::Packet(value)) + } else { + ( + k, + SwitchType::Type(build_inner_type( + Value::String(value), + )), + ) + } + } else if let Value::Array(array) = v { + ( + k, + SwitchType::Type(build_inner_type(Value::Array( + array, + ))), + ) + } else { + (k, SwitchType::Unknown(v)) + } + }) + .collect(), + ) + } else { + None + } + }) + .unwrap_or_default(), + default: layout + .remove("default") + .map(|v| v.as_str().unwrap_or_default().to_string()), + }); + } + None + } + + pub fn new_array(layout: Cow) -> Option { + if let Value::Object(mut obj) = layout.into_owned() { + let value = NativeType::new( + obj.remove("countType") + .unwrap_or_default() + .as_str() + .unwrap_or_default(), + Cow::Owned(Value::Null), + ).unwrap_or(NativeType::VarInt); + let inner_type = build_inner_type(obj.remove("type").unwrap_or_default()); + return Some(NativeType::Array { + count_type: Box::new(value), + array_type: inner_type, + }); + } + None + } + + pub fn new_container(layout: Cow) -> Option { + if let Value::Array(containers) = layout.into_owned() { + let containers_vec = containers + .into_iter() + .map(|v| { + if let Value::Object(mut obj) = v { + if let Some(name) = obj.remove("name") { + let name = name.as_str().unwrap().to_string(); + let inner_type = obj.remove("type").unwrap_or_default(); + let inner_type = build_inner_type(inner_type); + (TypeName::Named(name), inner_type) + } else { + let inner_type = obj.remove("type").unwrap_or_default(); + let inner_type = build_inner_type(inner_type); + (TypeName::Anonymous, inner_type) + } + } else { + panic!("Container is not an object"); + } + }) + .collect(); + + Some(NativeType::Container(containers_vec)) + } else { + None + } + } + + pub fn new_entity_metadata_loop(layout: Cow) -> Option { + match layout.into_owned() { + Value::Object(mut layout) => { + let end_val = layout + .remove("endVal") + .and_then(|v| v.as_i64()) + .unwrap_or_default(); + let inner_type = layout.remove("type").unwrap_or_default(); + let inner_type = build_inner_type(inner_type); + Some(NativeType::EntityMetadataLoop { + end_val, + metadata_type: inner_type, + }) + } + _ => None, + } + } + + pub fn new_buffer(layout: Cow) -> Option { + if let Value::Object(mut obj) = layout.into_owned() { + if let Value::String(count_type) = obj.remove("countType").unwrap_or_default() { + if let Some(count_type) = NativeType::new(&count_type, Cow::Owned(Value::Null)) { + return Some(NativeType::PString { + count_type: Box::new(count_type), + }); + } + } + } + None + } +} + +#[inline] +pub(crate) fn build_inner_type(value: Value) -> Box { + match value { + Value::String(simple_type) => { + return if let Some(simple_type) = NativeType::new(&simple_type, Cow::Owned(Value::Null)) + { + Box::new(PacketDataType::Native(simple_type)) + } else { + // Probably a reference to a built type + Box::new(PacketDataType::Other { + name: Some(simple_type.into()), + value: Value::Null, + }) + }; + } + Value::Array(mut array) => { + if array.len() != 2 { + return Box::new(PacketDataType::Other { + name: None, + value: Value::Array(array), + }); + } + let inner_value = Cow::Owned(array.pop().unwrap_or_default()); + let key = array.pop().unwrap(); + if let Value::String(key) = key { + let value = PacketDataType::new(&key, Cow::clone(&inner_value)).or_else(|| { + let option = NativeType::new(&key, inner_value.clone()); + option.map(PacketDataType::Native) + }); + if let Some(value) = value { + Box::new(value) + } else { + Box::new(PacketDataType::Other { + name: Some(key.into()), + value: inner_value.into_owned(), + }) + } + } else { + Box::new(PacketDataType::Other { + name: None, + value: Value::Array(vec![key, inner_value.into_owned()]), + }) + } + } + v => Box::new(PacketDataType::Other { + name: None, + value: v, + }), + } +} + +#[derive(Debug, Clone)] +pub enum PacketDataType { + // Just a pure native type + Native(NativeType), + // It was marked as "native" however, this enum does not have it + UnknownNativeType(String), + // This type is built from a native type + Built { + // The name of the built type + name: TypeName, + // The value of the built type + value: NativeType, + }, + /// A Type that could not be built or parsed. This is a fallback for when we don't know what the type is + /// This type is usually a reference to a built type + /// + /// If this is a reference you you will want to push any data within Value to the reference. + /// For example the Packet type references another type called "PacketData" that has a variable within it for the compareTo value. You will want to take the value from the Value and push it to the PacketData type. + Other { + // The name of the type if found + name: Option, + // The JSON value of the type + value: Value, + }, +} + +impl From for PacketDataType { + fn from(native: NativeType) -> Self { + PacketDataType::Native(native) + } +} + +impl PacketDataType { + pub fn new(key: &str, value: Cow<'_, Value>) -> Option { + if !NativeType::contains_type(key) { + match value.into_owned() { + Value::String(string) => Some(PacketDataType::UnknownNativeType(string)), + Value::Array(mut array) => { + if array.len() != 2 { + return Some(PacketDataType::Other { + name: Some(key.to_string().into()), + value: Value::Array(array), + }); + } + + let inner_type_values = array.pop().unwrap_or_default(); + let inner_type_name = array.pop().unwrap(); + if let Value::String(inner_type_name) = inner_type_name { + return if let Some(type_) = + NativeType::new(&inner_type_name, Cow::Borrowed(&inner_type_values)) + { + Some(PacketDataType::Built { + name: TypeName::Named(key.to_string()), + value: type_, + }) + } else { + Some(PacketDataType::Other { + name: Some(inner_type_name.into()), + value: inner_type_values, + }) + }; + } + None + } + v => Some(PacketDataType::Other { + name: Some(key.to_string().into()), + value: v, + }), + } + } else { + None + } + } +} + +#[derive(Debug, Clone)] +pub struct PacketDataTypes { + pub types: Vec, +} + +use std::fmt; +use std::fmt::{Debug, Display, Formatter}; + +use serde::de::MapAccess; + +impl<'de> Deserialize<'de> for PacketDataTypes { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct PacketDataTypesVisitor; + + impl<'de> Visitor<'de> for PacketDataTypesVisitor { + type Value = PacketDataTypes; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("struct PacketDataTypes") + } + + fn visit_map(self, mut map: V) -> Result + where + V: MapAccess<'de>, + { + let mut types = Vec::new(); + while let Some(key) = map.next_key::()? { + let value = map.next_value::()?; + if let Some(ty) = PacketDataType::new(&key, Cow::Owned(value)) { + types.push(ty); + } + } + Ok(PacketDataTypes { types }) + } + } + + deserializer.deserialize_map(PacketDataTypesVisitor) + } +}