Expanded the Packet Type API a bit more. Added some docs and errors on failure.

pull/5/head
Wyatt Herkamp 2 years ago
parent 3b8bf2bc02
commit d0f05cf14c

@ -3,9 +3,14 @@ use crate::models::version::Version;
use crate::{DataError, DataResult}; use crate::{DataError, DataResult};
use std::sync::Arc; 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 { pub struct Protocol {
version: Arc<Version>, version: Arc<Version>,
} }
impl Protocol { impl Protocol {
pub fn new(version: Arc<Version>) -> Self { pub fn new(version: Arc<Version>) -> Self {
Self { version } Self { version }

@ -3,18 +3,21 @@ use crate::api::tests::get_test_versions;
use crate::models::protocol::{NativeType, PacketDataType}; use crate::models::protocol::{NativeType, PacketDataType};
use std::sync::Arc; use std::sync::Arc;
use crate::DataResult; use crate::DataResult;
pub const VERSIONS_TO_SKIP: [&str; 3] = ["21w07a", "20w14a", "20w13b"];
#[test] #[test]
pub fn simple_test() { pub fn simple_test() {
let versions = get_test_versions(); let versions = get_test_versions();
for x in versions { for x in versions {
if VERSIONS_TO_SKIP.contains(&x.minecraft_version.as_str()) {
continue;
}
let arc = Arc::new(x); let arc = Arc::new(x);
let protocol = Protocol::new(arc.clone()); let protocol = Protocol::new(arc.clone());
let protocol1 = protocol.get_protocol(); let protocol1 = protocol.get_protocol();
match protocol1 { match protocol1 {
Ok(_) => {} Ok(_) => {}
Err(error) => { Err(error) => {
println!("{:?} On Version {}", error, arc.minecraft_version); panic!("Minecraft Version {} could not be parsed into a Protocol object: {}", arc.minecraft_version, error);
} }
} }
} }

@ -1,38 +1,40 @@
use serde::de::Visitor; use serde::de::Visitor;
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use serde_json::Value; use serde_json::Value;
use std::borrow::{Cow}; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug, Clone)]
pub struct BitField { pub struct BitField {
pub name: String, pub name: String,
pub size: i64, pub size: i64,
pub signed: bool, pub signed: bool,
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum SwitchType { pub enum SwitchType {
Packet(String), Packet(String),
Type(Box<PacketDataType>), Type(Box<PacketDataType>),
Unknown(Value), Unknown(Value),
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum TypeName { pub enum TypeName {
Anonymous, Anonymous,
Named(String), Named(String),
} }
impl From<String> for TypeName {
fn from(value: String) -> Self {
Self::Named(value)
}
}
impl Display for TypeName { impl Display for TypeName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self { match self {
TypeName::Anonymous => { TypeName::Anonymous => f.write_str("Anonymous"),
f.write_str("Anonymous") TypeName::Named(name) => f.write_str(name.as_str()),
}
TypeName::Named(name) => {
f.write_str(name.as_str())
}
} }
} }
} }
@ -50,7 +52,8 @@ impl PartialEq<String> for TypeName {
/// These data types should be available in every version. /// These data types should be available in every version.
/// However, they won't break anything if not present /// However, they won't break anything if not present
/// This can also be known as the Native Types /// This can also be known as the Native Types
#[derive(Debug)] #[derive(Debug, Clone)]
#[non_exhaustive]
pub enum NativeType { pub enum NativeType {
/// Please read the following link for information on parsing https://wiki.vg/Protocol#VarInt_and_VarLong /// Please read the following link for information on parsing https://wiki.vg/Protocol#VarInt_and_VarLong
VarInt, VarInt,
@ -97,40 +100,77 @@ pub enum NativeType {
OptionalNBT, 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 { impl NativeType {
pub fn contains_type(name: &str) -> bool { 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") 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<Self> { pub fn new(name: &str, layout: Cow<'_, Value>) -> Option<Self> {
match name { match name {
"varint" => Some(NativeType::VarInt), "varint" => Some(NativeType::VarInt),
"pstring" => { "pstring" => Self::pstring(layout),
if let Value::Object(mut obj) = layout.into_owned() { "buffer" => Self::generate_buffer(layout),
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
}
"buffer" => {
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
}
"bool" => Some(NativeType::Bool), "bool" => Some(NativeType::Bool),
"u8" => Some(NativeType::U8), "u8" => Some(NativeType::U8),
"u16" => Some(NativeType::U16), "u16" => Some(NativeType::U16),
@ -144,24 +184,36 @@ impl NativeType {
"f64" => Some(NativeType::F64), "f64" => Some(NativeType::F64),
"uuid" => Some(NativeType::Uuid), "uuid" => Some(NativeType::Uuid),
"option" => Some(NativeType::Option(build_inner_type(layout.into_owned()))), "option" => Some(NativeType::Option(build_inner_type(layout.into_owned()))),
"entityMetadataLoop" => { "entityMetadataLoop" => Self::entity_metadata_loop(layout),
match layout.into_owned() { "topbitsetterminatedarray" => Self::top_bitsetterminated_array(layout),
Value::Object(mut layout) => { "bitfield" => Self::bitfield(layout),
let end_val = layout "container" => Self::container(layout),
.remove("endVal")
.and_then(|v| v.as_i64()) "switch" => Self::switch(layout),
.unwrap_or_default(); "void" => Some(NativeType::Void),
let inner_type = layout.remove("type").unwrap_or_default(); "array" => Self::array(layout),
let inner_type = build_inner_type(inner_type);
Some(NativeType::EntityMetadataLoop { "restbuffer" => Some(NativeType::RestBuffer),
end_val, "nbt" => Some(NativeType::NBT),
metadata_type: inner_type, "optionalnbt" => Some(NativeType::OptionalNBT),
})
}
_ => None, _ => None,
} }
} }
"topbitsetterminatedarray" => {
pub fn pstring(layout: Cow<Value>) -> Option<NativeType> {
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 top_bitsetterminated_array(layout: Cow<Value>) -> Option<NativeType> {
if let Value::Object(mut layout) = layout.into_owned() { if let Value::Object(mut layout) = layout.into_owned() {
let inner_type = layout.remove("type").unwrap_or_default(); let inner_type = layout.remove("type").unwrap_or_default();
let inner_type = build_inner_type(inner_type); let inner_type = build_inner_type(inner_type);
@ -169,7 +221,8 @@ impl NativeType {
} }
None None
} }
"bitfield" => {
pub fn bitfield(layout: Cow<Value>) -> Option<NativeType> {
if let Value::Array(bit_fields) = layout.into_owned() { if let Value::Array(bit_fields) = layout.into_owned() {
let bit_fields_vec = bit_fields let bit_fields_vec = bit_fields
.into_iter() .into_iter()
@ -181,38 +234,16 @@ impl NativeType {
None None
} }
} }
"container" => {
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)) pub fn switch(layout: Cow<Value>) -> Option<NativeType> {
} else {
None
}
}
"switch" => {
if let Value::Object(mut layout) = layout.into_owned() { if let Value::Object(mut layout) = layout.into_owned() {
return Some(NativeType::Switch { return Some(NativeType::Switch {
compare_to: layout.remove("compareTo").unwrap().as_str().unwrap_or_default().to_string(), compare_to: layout
.remove("compareTo")
.unwrap()
.as_str()
.unwrap_or_default()
.to_string(),
fields: layout fields: layout
.remove("fields") .remove("fields")
.and_then(|v| { .and_then(|v| {
@ -225,10 +256,20 @@ impl NativeType {
if value.starts_with("packet") { if value.starts_with("packet") {
(k, SwitchType::Packet(value)) (k, SwitchType::Packet(value))
} else { } else {
(k, SwitchType::Type(build_inner_type(Value::String(value)))) (
k,
SwitchType::Type(build_inner_type(
Value::String(value),
)),
)
} }
} else if let Value::Array(array) = v { } else if let Value::Array(array) = v {
(k, SwitchType::Type(build_inner_type(Value::Array(array)))) (
k,
SwitchType::Type(build_inner_type(Value::Array(
array,
))),
)
} else { } else {
(k, SwitchType::Unknown(v)) (k, SwitchType::Unknown(v))
} }
@ -240,13 +281,15 @@ impl NativeType {
} }
}) })
.unwrap_or_default(), .unwrap_or_default(),
default: layout.remove("default").map(|v| v.as_str().unwrap_or_default().to_string()), default: layout
.remove("default")
.map(|v| v.as_str().unwrap_or_default().to_string()),
}); });
} }
None None
} }
"void" => Some(NativeType::Void),
"array" => { pub fn array(layout: Cow<Value>) -> Option<NativeType> {
if let Value::Object(mut obj) = layout.into_owned() { if let Value::Object(mut obj) = layout.into_owned() {
let value = NativeType::new( let value = NativeType::new(
obj.remove("countType") obj.remove("countType")
@ -266,42 +309,64 @@ impl NativeType {
None None
} }
"restbuffer" => Some(NativeType::RestBuffer), pub fn container(layout: Cow<Value>) -> Option<NativeType> {
"nbt" => Some(NativeType::NBT), if let Value::Array(containers) = layout.into_owned() {
"optionalnbt" => Some(NativeType::OptionalNBT), 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 entity_metadata_loop(layout: Cow<Value>) -> Option<NativeType> {
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, _ => None,
} }
} }
pub fn get_name(&self) -> &str {
match self { pub fn generate_buffer(layout: Cow<Value>) -> Option<NativeType> {
NativeType::Bool => "bool", if let Value::Object(mut obj) = layout.into_owned() {
NativeType::U8 => "u8", if let Value::String(count_type) = obj.remove("countType").unwrap_or_default() {
NativeType::U16 => "u16", if let Some(count_type) = NativeType::new(&count_type, Cow::Owned(Value::Null)) {
NativeType::U32 => "u32", return Some(NativeType::PString {
NativeType::U64 => "u64", count_type: Box::new(count_type),
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" }
} }
} }
None
}
} }
#[inline] #[inline]
@ -313,40 +378,49 @@ fn build_inner_type(value: Value) -> Box<PacketDataType> {
Box::new(PacketDataType::Native(simple_type)) Box::new(PacketDataType::Native(simple_type))
} else { } else {
// Probably a reference to a built type // Probably a reference to a built type
Box::new(PacketDataType::Other(simple_type, Value::Null)) Box::new(PacketDataType::Other {
name: Some(simple_type.into()),
value: Value::Null,
})
}; };
} }
Value::Array(mut array) => { Value::Array(mut array) => {
if array.len() != 2 { if array.len() != 2 {
return Box::new(PacketDataType::Other(String::new(), Value::Array(array))); return Box::new(PacketDataType::Other {
name: None,
value: Value::Array(array),
});
} }
let inner_value = Cow::Owned(array.pop().unwrap_or_default()); let inner_value = Cow::Owned(array.pop().unwrap_or_default());
let key = array.pop().unwrap(); let key = array.pop().unwrap();
if let Value::String(key) = &key { if let Value::String(key) = key {
let value = PacketDataType::new(key, Cow::clone(&inner_value)).or_else(|| { let value = PacketDataType::new(&key, Cow::clone(&inner_value)).or_else(|| {
let option = NativeType::new(key, inner_value.clone()); let option = NativeType::new(&key, inner_value.clone());
option.map(PacketDataType::Native) option.map(PacketDataType::Native)
}); });
if let Some(value) = value { if let Some(value) = value {
Box::new(value) Box::new(value)
} else { } else {
Box::new(PacketDataType::Other( Box::new(PacketDataType::Other {
key.clone(), name: Some(key.into()),
inner_value.into_owned(), value: inner_value.into_owned(),
)) })
} }
} else { } else {
Box::new(PacketDataType::Other( Box::new(PacketDataType::Other {
key.as_str().unwrap_or_default().to_string(), name: None,
inner_value.into_owned(), value: inner_value.into_owned(),
)) })
} }
} }
v => Box::new(PacketDataType::Other(String::new(), v)), v => Box::new(PacketDataType::Other {
name: None,
value: v,
}),
} }
} }
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum PacketDataType { pub enum PacketDataType {
// Just a pure native type // Just a pure native type
Native(NativeType), Native(NativeType),
@ -359,8 +433,20 @@ pub enum PacketDataType {
// The value of the built type // The value of the built type
value: NativeType, 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
Other {
// The name of the type if found
name: Option<TypeName>,
// The JSON value of the type
value: Value,
},
}
Other(String, Value), impl From<NativeType> for PacketDataType {
fn from(native: NativeType) -> Self {
PacketDataType::Native(native)
}
} }
impl PacketDataType { impl PacketDataType {
@ -370,7 +456,10 @@ impl PacketDataType {
Value::String(string) => Some(PacketDataType::UnknownNativeType(string)), Value::String(string) => Some(PacketDataType::UnknownNativeType(string)),
Value::Array(mut array) => { Value::Array(mut array) => {
if array.len() != 2 { if array.len() != 2 {
return Some(PacketDataType::Other(key.to_string(), Value::Array(array))); return Some(PacketDataType::Other {
name: None,
value: Value::Array(array),
});
} }
let inner_type_values = array.pop().unwrap_or_default(); let inner_type_values = array.pop().unwrap_or_default();
@ -384,14 +473,18 @@ impl PacketDataType {
value: type_, value: type_,
}) })
} else { } else {
Some(PacketDataType::Other(inner_type_name, inner_type_values)) Some(PacketDataType::Other {
name: Some(inner_type_name.into()),
value: inner_type_values,
})
}; };
} }
None None
} }
v => { v => Some(PacketDataType::Other {
Some(PacketDataType::Other(key.to_string(), v)) name: None,
} value: v,
}),
} }
} else { } else {
None None
@ -407,7 +500,7 @@ pub struct PacketDataTypes {
use std::fmt; use std::fmt;
use std::fmt::{Debug, Display, Formatter}; use std::fmt::{Debug, Display, Formatter};
use serde::de::{MapAccess}; use serde::de::MapAccess;
impl<'de> Deserialize<'de> for PacketDataTypes { impl<'de> Deserialize<'de> for PacketDataTypes {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
@ -419,7 +512,7 @@ impl<'de> Deserialize<'de> for PacketDataTypes {
impl<'de> Visitor<'de> for PacketDataTypesVisitor { impl<'de> Visitor<'de> for PacketDataTypesVisitor {
type Value = PacketDataTypes; type Value = PacketDataTypes;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("struct PacketDataTypes") formatter.write_str("struct PacketDataTypes")
} }

Loading…
Cancel
Save