Add VarInt discriminant type (#15)

This commit is contained in:
vagola 2021-09-22 23:29:00 +03:00 committed by GitHub
parent 024786e618
commit 05cf9c6e6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 250 additions and 75 deletions

View File

@ -7,14 +7,13 @@ pub(crate) enum DeriveInputParserError {
UnsupportedData, UnsupportedData,
/// Data fields must be named. /// Data fields must be named.
UnnamedDataFields, UnnamedDataFields,
FieldError { /// Possible errors while parsing attributes.
field_error: FieldError, AttributeError { attribute_error: AttributeError },
},
} }
/// Possible errors while parsing field. /// Possible errors while parsing attributes.
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum FieldError { pub(crate) enum AttributeError {
/// Failed to parse field meta due incorrect syntax. /// Failed to parse field meta due incorrect syntax.
BadAttributeSyntax { syn_error: SynError }, BadAttributeSyntax { syn_error: SynError },
/// Unsupported field attribute type. /// Unsupported field attribute type.
@ -24,22 +23,22 @@ pub(crate) enum FieldError {
AttributeWrongValueType, AttributeWrongValueType,
} }
impl From<FieldError> for DeriveInputParserError { impl From<AttributeError> for DeriveInputParserError {
fn from(field_error: FieldError) -> Self { fn from(attribute_error: AttributeError) -> Self {
DeriveInputParserError::FieldError { field_error } DeriveInputParserError::AttributeError { attribute_error }
} }
} }
impl From<SynError> for DeriveInputParserError { impl From<SynError> for DeriveInputParserError {
fn from(syn_error: SynError) -> Self { fn from(syn_error: SynError) -> Self {
DeriveInputParserError::FieldError { DeriveInputParserError::AttributeError {
field_error: FieldError::BadAttributeSyntax { syn_error }, attribute_error: AttributeError::BadAttributeSyntax { syn_error },
} }
} }
} }
impl From<SynError> for FieldError { impl From<SynError> for AttributeError {
fn from(syn_error: SynError) -> Self { fn from(syn_error: SynError) -> Self {
FieldError::BadAttributeSyntax { syn_error } AttributeError::BadAttributeSyntax { syn_error }
} }
} }

View File

@ -18,7 +18,11 @@ pub fn derive_encoder(tokens: TokenStream) -> TokenStream {
TokenStream::from(match derive_parse_result { TokenStream::from(match derive_parse_result {
DeriveInputParseResult::Struct { name, fields } => render_struct_encoder(name, &fields), DeriveInputParseResult::Struct { name, fields } => render_struct_encoder(name, &fields),
DeriveInputParseResult::Enum { name, variants } => render_enum_encoder(name, &variants), DeriveInputParseResult::Enum {
name,
discriminant_type,
variants,
} => render_enum_encoder(name, &discriminant_type, &variants),
}) })
} }
@ -29,6 +33,10 @@ pub fn derive_decoder(tokens: TokenStream) -> TokenStream {
TokenStream::from(match derive_parse_result { TokenStream::from(match derive_parse_result {
DeriveInputParseResult::Struct { name, fields } => render_struct_decoder(name, &fields), DeriveInputParseResult::Struct { name, fields } => render_struct_decoder(name, &fields),
DeriveInputParseResult::Enum { name, variants } => render_enum_decoder(name, &variants), DeriveInputParseResult::Enum {
name,
discriminant_type,
variants,
} => render_enum_decoder(name, &discriminant_type, &variants),
}) })
} }

View File

@ -1,8 +1,10 @@
use crate::error::{DeriveInputParserError, FieldError}; use crate::error::{AttributeError, DeriveInputParserError};
use proc_macro2::Ident; use proc_macro2::Ident;
use std::iter::FromIterator; use std::iter::FromIterator;
use syn::punctuated::Punctuated; use syn::punctuated::Punctuated;
use syn::{Data, DeriveInput, ExprLit, Field, Fields, FieldsNamed, Lit, Meta, NestedMeta, Type}; use syn::{
Attribute, Data, DeriveInput, ExprLit, Fields, FieldsNamed, Lit, Meta, NestedMeta, Type,
};
use syn::{Error as SynError, Variant}; use syn::{Error as SynError, Variant};
use syn::{Expr, Token}; use syn::{Expr, Token};
@ -13,12 +15,13 @@ pub(crate) enum DeriveInputParseResult<'a> {
}, },
Enum { Enum {
name: &'a Ident, name: &'a Ident,
discriminant_type: DiscriminantType,
variants: Vec<VariantData<'a>>, variants: Vec<VariantData<'a>>,
}, },
} }
pub(crate) struct VariantData<'a> { pub(crate) struct VariantData<'a> {
pub(crate) discriminant: u8, pub(crate) discriminant: usize,
pub(crate) name: &'a Ident, pub(crate) name: &'a Ident,
pub(crate) fields: Vec<FieldData<'a>>, pub(crate) fields: Vec<FieldData<'a>>,
} }
@ -26,16 +29,22 @@ pub(crate) struct VariantData<'a> {
pub(crate) struct FieldData<'a> { pub(crate) struct FieldData<'a> {
pub(crate) name: &'a Ident, pub(crate) name: &'a Ident,
pub(crate) ty: &'a Type, pub(crate) ty: &'a Type,
pub(crate) attribute: Attribute, pub(crate) attribute: AttributeData,
} }
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub(crate) enum Attribute { pub(crate) enum AttributeData {
With { module: String }, With { module: String },
MaxLength { length: usize }, MaxLength { length: usize },
Empty, Empty,
} }
#[derive(Debug, Eq, PartialEq)]
pub(crate) enum DiscriminantType {
UnsignedByte,
VarInt,
}
pub(crate) fn parse_derive_input( pub(crate) fn parse_derive_input(
input: &DeriveInput, input: &DeriveInput,
) -> Result<DeriveInputParseResult, DeriveInputParserError> { ) -> Result<DeriveInputParseResult, DeriveInputParserError> {
@ -52,24 +61,41 @@ pub(crate) fn parse_derive_input(
}, },
Data::Enum(data_enum) => { Data::Enum(data_enum) => {
let variants = parse_variants(&data_enum.variants)?; let variants = parse_variants(&data_enum.variants)?;
let discriminant_type = parse_discriminant_type(&input.attrs)?;
Ok(DeriveInputParseResult::Enum { name, variants }) Ok(DeriveInputParseResult::Enum {
name,
discriminant_type,
variants,
})
} }
_ => Err(DeriveInputParserError::UnsupportedData), _ => Err(DeriveInputParserError::UnsupportedData),
} }
} }
fn parse_discriminant_type(
attributes: &Vec<Attribute>,
) -> Result<DiscriminantType, DeriveInputParserError> {
let nested_metas = parse_attributes_nested_metas(attributes)?;
let attribute = parse_attribute(nested_metas)?;
match attribute {
AttributeData::With { module } if module == "var_int" => Ok(DiscriminantType::VarInt),
_ => Ok(DiscriminantType::UnsignedByte),
}
}
fn parse_variants( fn parse_variants(
variants: &Punctuated<Variant, Token![,]>, variants: &Punctuated<Variant, Token![,]>,
) -> Result<Vec<VariantData>, DeriveInputParserError> { ) -> Result<Vec<VariantData>, DeriveInputParserError> {
variants variants
.iter() .iter()
.enumerate() .enumerate()
.map(|(idx, v)| parse_variant(idx as u8, v)) .map(|(idx, v)| parse_variant(idx, v))
.collect() .collect()
} }
fn parse_variant(idx: u8, variant: &Variant) -> Result<VariantData, DeriveInputParserError> { fn parse_variant(idx: usize, variant: &Variant) -> Result<VariantData, DeriveInputParserError> {
let discriminant = parse_variant_discriminant(variant).unwrap_or(idx); let discriminant = parse_variant_discriminant(variant).unwrap_or(idx);
let name = &variant.ident; let name = &variant.ident;
@ -86,7 +112,7 @@ fn parse_variant(idx: u8, variant: &Variant) -> Result<VariantData, DeriveInputP
}) })
} }
fn parse_variant_discriminant(variant: &Variant) -> Option<u8> { fn parse_variant_discriminant(variant: &Variant) -> Option<usize> {
variant variant
.discriminant .discriminant
.as_ref() .as_ref()
@ -106,7 +132,7 @@ fn parse_fields(named_fields: &FieldsNamed) -> Result<Vec<FieldData>, DeriveInpu
let name = field.ident.as_ref().unwrap(); let name = field.ident.as_ref().unwrap();
let ty = &field.ty; let ty = &field.ty;
let nested_metas = parse_field_nested_metas(field)?; let nested_metas = parse_attributes_nested_metas(&field.attrs)?;
let attribute = parse_attribute(nested_metas)?; let attribute = parse_attribute(nested_metas)?;
fields_data.push(FieldData { fields_data.push(FieldData {
@ -119,9 +145,10 @@ fn parse_fields(named_fields: &FieldsNamed) -> Result<Vec<FieldData>, DeriveInpu
Ok(fields_data) Ok(fields_data)
} }
fn parse_field_nested_metas(field: &Field) -> Result<Vec<NestedMeta>, DeriveInputParserError> { fn parse_attributes_nested_metas(
let parsed_metas = field attributes: &Vec<Attribute>,
.attrs ) -> Result<Vec<NestedMeta>, DeriveInputParserError> {
let parsed_metas = attributes
.iter() .iter()
.filter(|a| a.path.is_ident("data_type")) .filter(|a| a.path.is_ident("data_type"))
.map(|a| a.parse_meta()) .map(|a| a.parse_meta())
@ -131,56 +158,56 @@ fn parse_field_nested_metas(field: &Field) -> Result<Vec<NestedMeta>, DeriveInpu
.into_iter() .into_iter()
.map(|m| match m { .map(|m| match m {
Meta::List(meta_list) => Ok(Vec::from_iter(meta_list.nested)), Meta::List(meta_list) => Ok(Vec::from_iter(meta_list.nested)),
_ => Err(FieldError::UnsupportedAttribute), _ => Err(AttributeError::UnsupportedAttribute),
}) })
.collect::<Result<Vec<Vec<NestedMeta>>, FieldError>>()?; .collect::<Result<Vec<Vec<NestedMeta>>, AttributeError>>()?;
Ok(nested_metas.into_iter().flatten().collect()) Ok(nested_metas.into_iter().flatten().collect())
} }
fn parse_attribute(nested_metas: Vec<NestedMeta>) -> Result<Attribute, DeriveInputParserError> { fn parse_attribute(nested_metas: Vec<NestedMeta>) -> Result<AttributeData, DeriveInputParserError> {
let attribute_parsers: Vec<fn(&NestedMeta) -> Result<Attribute, FieldError>> = let attribute_parsers: Vec<fn(&NestedMeta) -> Result<AttributeData, AttributeError>> =
vec![get_module_attribute, get_max_length_attribute]; vec![get_module_attribute, get_max_length_attribute];
for nested_meta in nested_metas.iter() { for nested_meta in nested_metas.iter() {
for attribute_parser in attribute_parsers.iter() { for attribute_parser in attribute_parsers.iter() {
let attribute = attribute_parser(nested_meta)?; let attribute = attribute_parser(nested_meta)?;
if attribute != Attribute::Empty { if attribute != AttributeData::Empty {
return Ok(attribute); return Ok(attribute);
} }
} }
} }
Ok(Attribute::Empty) Ok(AttributeData::Empty)
} }
fn get_module_attribute(nested_meta: &NestedMeta) -> Result<Attribute, FieldError> { fn get_module_attribute(nested_meta: &NestedMeta) -> Result<AttributeData, AttributeError> {
if let NestedMeta::Meta(Meta::NameValue(named_meta)) = nested_meta { if let NestedMeta::Meta(Meta::NameValue(named_meta)) = nested_meta {
if matches!(&named_meta.path, path if path.is_ident("with")) { if matches!(&named_meta.path, path if path.is_ident("with")) {
return match &named_meta.lit { return match &named_meta.lit {
Lit::Str(lit_str) => Ok(Attribute::With { Lit::Str(lit_str) => Ok(AttributeData::With {
module: lit_str.value(), module: lit_str.value(),
}), }),
_ => Err(FieldError::AttributeWrongValueType), _ => Err(AttributeError::AttributeWrongValueType),
}; };
} }
} }
Ok(Attribute::Empty) Ok(AttributeData::Empty)
} }
fn get_max_length_attribute(nested_meta: &NestedMeta) -> Result<Attribute, FieldError> { fn get_max_length_attribute(nested_meta: &NestedMeta) -> Result<AttributeData, AttributeError> {
if let NestedMeta::Meta(Meta::NameValue(named_meta)) = nested_meta { if let NestedMeta::Meta(Meta::NameValue(named_meta)) = nested_meta {
if matches!(&named_meta.path, path if path.is_ident("max_length")) { if matches!(&named_meta.path, path if path.is_ident("max_length")) {
return match &named_meta.lit { return match &named_meta.lit {
Lit::Int(lit_int) => Ok(Attribute::MaxLength { Lit::Int(lit_int) => Ok(AttributeData::MaxLength {
length: lit_int.base10_parse::<usize>()?, length: lit_int.base10_parse()?,
}), }),
_ => Err(FieldError::AttributeWrongValueType), _ => Err(AttributeError::AttributeWrongValueType),
}; };
} }
} }
Ok(Attribute::Empty) Ok(AttributeData::Empty)
} }

View File

@ -1,4 +1,4 @@
use crate::parse::{Attribute, FieldData, VariantData}; use crate::parse::{AttributeData, DiscriminantType, FieldData, VariantData};
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use proc_macro2::{Ident, Span}; use proc_macro2::{Ident, Span};
use quote::quote; use quote::quote;
@ -24,8 +24,13 @@ pub(crate) fn render_struct_decoder(name: &Ident, fields: &Vec<FieldData>) -> To
} }
} }
pub(crate) fn render_enum_decoder(name: &Ident, variants: &Vec<VariantData>) -> TokenStream2 { pub(crate) fn render_enum_decoder(
let render_variants = render_variants(variants); name: &Ident,
discriminant_type: &DiscriminantType,
variants: &Vec<VariantData>,
) -> TokenStream2 {
let render_variants = render_variants(discriminant_type, variants);
let render_discriminant_type = render_discriminant_type(discriminant_type);
quote! { quote! {
#[automatically_derived] #[automatically_derived]
@ -33,31 +38,40 @@ pub(crate) fn render_enum_decoder(name: &Ident, variants: &Vec<VariantData>) ->
type Output = Self; type Output = Self;
fn decode<R: std::io::Read>(reader: &mut R) -> Result<Self::Output, crate::error::DecodeError> { fn decode<R: std::io::Read>(reader: &mut R) -> Result<Self::Output, crate::error::DecodeError> {
let type_id = reader.read_u8()?; let type_id = #render_discriminant_type;
match type_id { match type_id {
#render_variants #render_variants
_ => Err(DecodeError::UnknownEnumType { type_id }), _ => Err(DecodeError::UnknownEnumType { type_id: type_id as usize, }),
} }
} }
} }
} }
} }
fn render_variants(variants: &Vec<VariantData>) -> TokenStream2 { fn render_variants(
variants.iter().map(|v| render_variant(v)).collect() discriminant_type: &DiscriminantType,
variants: &Vec<VariantData>,
) -> TokenStream2 {
variants
.iter()
.map(|v| render_variant(discriminant_type, v))
.collect()
} }
fn render_variant(variant: &VariantData) -> TokenStream2 { fn render_variant(discriminant_type: &DiscriminantType, variant: &VariantData) -> TokenStream2 {
if variant.fields.is_empty() { if variant.fields.is_empty() {
render_unit_variant(variant) render_unit_variant(discriminant_type, variant)
} else { } else {
render_struct_variant(variant) render_struct_variant(discriminant_type, variant)
} }
} }
fn render_unit_variant(variant: &VariantData) -> TokenStream2 { fn render_unit_variant(
let discriminant = variant.discriminant; discriminant_type: &DiscriminantType,
variant: &VariantData,
) -> TokenStream2 {
let discriminant = render_discriminant(discriminant_type, variant.discriminant);
let name = variant.name; let name = variant.name;
quote! { quote! {
@ -65,8 +79,11 @@ fn render_unit_variant(variant: &VariantData) -> TokenStream2 {
} }
} }
fn render_struct_variant(variant: &VariantData) -> TokenStream2 { fn render_struct_variant(
let discriminant = variant.discriminant; discriminant_type: &DiscriminantType,
variant: &VariantData,
) -> TokenStream2 {
let discriminant = render_discriminant(discriminant_type, variant.discriminant);
let name = variant.name; let name = variant.name;
let fields = &variant.fields; let fields = &variant.fields;
@ -84,6 +101,30 @@ fn render_struct_variant(variant: &VariantData) -> TokenStream2 {
} }
} }
fn render_discriminant_type(discriminant_type: &DiscriminantType) -> TokenStream2 {
match discriminant_type {
DiscriminantType::UnsignedByte => {
quote!(reader.read_u8()?;)
}
DiscriminantType::VarInt => {
quote!(reader.read_var_i32()?;)
}
}
}
fn render_discriminant(discriminant_type: &DiscriminantType, discriminant: usize) -> TokenStream2 {
match discriminant_type {
DiscriminantType::UnsignedByte => {
let u8 = discriminant as u8;
quote!(#u8)
}
DiscriminantType::VarInt => {
let i32 = discriminant as i32;
quote!(#i32)
}
}
}
fn render_field_names_joined_comma(fields: &Vec<FieldData>) -> TokenStream2 { fn render_field_names_joined_comma(fields: &Vec<FieldData>) -> TokenStream2 {
fields.iter().map(|f| f.name).map(|n| quote!(#n,)).collect() fields.iter().map(|f| f.name).map(|n| quote!(#n,)).collect()
} }
@ -97,9 +138,9 @@ fn render_field(field: &FieldData) -> TokenStream2 {
let ty = field.ty; let ty = field.ty;
match &field.attribute { match &field.attribute {
Attribute::With { module } => render_with_field(name, module), AttributeData::With { module } => render_with_field(name, module),
Attribute::MaxLength { length } => render_max_length_field(name, *length as u16), AttributeData::MaxLength { length } => render_max_length_field(name, *length as u16),
Attribute::Empty => render_simple_field(name, ty), AttributeData::Empty => render_simple_field(name, ty),
} }
} }

View File

@ -1,4 +1,4 @@
use crate::parse::{Attribute, FieldData, VariantData}; use crate::parse::{AttributeData, DiscriminantType, FieldData, VariantData};
use proc_macro2::TokenStream as TokenStream2; use proc_macro2::TokenStream as TokenStream2;
use proc_macro2::{Ident, Span}; use proc_macro2::{Ident, Span};
use quote::quote; use quote::quote;
@ -18,8 +18,12 @@ pub(crate) fn render_struct_encoder(name: &Ident, fields: &Vec<FieldData>) -> To
} }
} }
pub(crate) fn render_enum_encoder(name: &Ident, variants: &Vec<VariantData>) -> TokenStream2 { pub(crate) fn render_enum_encoder(
let render_variants = render_variants(variants); name: &Ident,
discriminant_type: &DiscriminantType,
variants: &Vec<VariantData>,
) -> TokenStream2 {
let render_variants = render_variants(discriminant_type, variants);
quote! { quote! {
#[automatically_derived] #[automatically_derived]
@ -35,34 +39,49 @@ pub(crate) fn render_enum_encoder(name: &Ident, variants: &Vec<VariantData>) ->
} }
} }
fn render_variants(variants: &Vec<VariantData>) -> TokenStream2 { fn render_variants(
variants.iter().map(|v| render_variant(v)).collect() discriminant_type: &DiscriminantType,
variants: &Vec<VariantData>,
) -> TokenStream2 {
variants
.iter()
.map(|v| render_variant(discriminant_type, v))
.collect()
} }
fn render_variant(variant: &VariantData) -> TokenStream2 { fn render_variant(discriminant_type: &DiscriminantType, variant: &VariantData) -> TokenStream2 {
if variant.fields.is_empty() { if variant.fields.is_empty() {
render_unit_variant(variant) render_unit_variant(discriminant_type, variant)
} else { } else {
render_struct_variant(variant) render_struct_variant(discriminant_type, variant)
} }
} }
fn render_unit_variant(variant: &VariantData) -> TokenStream2 { fn render_unit_variant(
discriminant_type: &DiscriminantType,
variant: &VariantData,
) -> TokenStream2 {
let discriminant = variant.discriminant; let discriminant = variant.discriminant;
let name = variant.name; let name = variant.name;
let render_discriminant_type = render_discriminant_type(discriminant_type, discriminant);
quote! { quote! {
Self::#name => { Self::#name => {
writer.write_u8(#discriminant)?; #render_discriminant_type
} }
} }
} }
fn render_struct_variant(variant: &VariantData) -> TokenStream2 { fn render_struct_variant(
discriminant_type: &DiscriminantType,
variant: &VariantData,
) -> TokenStream2 {
let discriminant = variant.discriminant; let discriminant = variant.discriminant;
let name = variant.name; let name = variant.name;
let fields = &variant.fields; let fields = &variant.fields;
let render_discriminant_type = render_discriminant_type(discriminant_type, discriminant);
let field_names_joined_comma = render_field_names_joined_comma(fields); let field_names_joined_comma = render_field_names_joined_comma(fields);
let render_fields = render_fields(fields, false); let render_fields = render_fields(fields, false);
@ -70,13 +89,31 @@ fn render_struct_variant(variant: &VariantData) -> TokenStream2 {
Self::#name { Self::#name {
#field_names_joined_comma #field_names_joined_comma
} => { } => {
writer.write_u8(#discriminant)?; #render_discriminant_type
#render_fields #render_fields
} }
} }
} }
fn render_discriminant_type(
discriminant_type: &DiscriminantType,
discriminant: usize,
) -> TokenStream2 {
match discriminant_type {
DiscriminantType::UnsignedByte => {
let u8 = discriminant as u8;
quote!(writer.write_u8(#u8)?;)
}
DiscriminantType::VarInt => {
let var_i32 = discriminant as i32;
quote!(writer.write_var_i32(#var_i32)?;)
}
}
}
fn render_field_names_joined_comma(fields: &Vec<FieldData>) -> TokenStream2 { fn render_field_names_joined_comma(fields: &Vec<FieldData>) -> TokenStream2 {
fields.iter().map(|f| f.name).map(|n| quote!(#n,)).collect() fields.iter().map(|f| f.name).map(|n| quote!(#n,)).collect()
} }
@ -89,9 +126,11 @@ fn render_field(field: &FieldData, with_self: bool) -> TokenStream2 {
let name = field.name; let name = field.name;
match &field.attribute { match &field.attribute {
Attribute::With { module } => render_with_field(name, module, with_self), AttributeData::With { module } => render_with_field(name, module, with_self),
Attribute::MaxLength { length } => render_max_length_field(name, *length as u16, with_self), AttributeData::MaxLength { length } => {
Attribute::Empty => render_simple_field(name, with_self), render_max_length_field(name, *length as u16, with_self)
}
AttributeData::Empty => render_simple_field(name, with_self),
} }
} }

View File

@ -65,7 +65,7 @@ pub enum DecodeError {
}, },
/// Type id was not parsed as valid enum value. /// Type id was not parsed as valid enum value.
UnknownEnumType { UnknownEnumType {
type_id: u8, type_id: usize,
}, },
TagDecodeError { TagDecodeError {
tag_decode_error: TagDecodeError, tag_decode_error: TagDecodeError,

View File

@ -1,5 +1,7 @@
use crate::data::chat::Message; use crate::data::chat::Message;
use crate::decoder::Decoder; use crate::decoder::Decoder;
use crate::decoder::DecoderReadExt;
use crate::encoder::EncoderWriteExt;
use crate::error::DecodeError; use crate::error::DecodeError;
use byteorder::{ReadBytesExt, WriteBytesExt}; use byteorder::{ReadBytesExt, WriteBytesExt};
use minecraft_protocol_derive::{Decoder, Encoder}; use minecraft_protocol_derive::{Decoder, Encoder};
@ -19,6 +21,7 @@ pub enum GameClientBoundPacket {
ChunkData(ChunkData), ChunkData(ChunkData),
GameDisconnect(GameDisconnect), GameDisconnect(GameDisconnect),
BossBar(BossBar), BossBar(BossBar),
EntityAction(EntityAction),
} }
impl GameServerBoundPacket { impl GameServerBoundPacket {
@ -55,6 +58,7 @@ impl GameClientBoundPacket {
GameClientBoundPacket::ChunkData(_) => 0x21, GameClientBoundPacket::ChunkData(_) => 0x21,
GameClientBoundPacket::JoinGame(_) => 0x25, GameClientBoundPacket::JoinGame(_) => 0x25,
GameClientBoundPacket::BossBar(_) => 0x0D, GameClientBoundPacket::BossBar(_) => 0x0D,
GameClientBoundPacket::EntityAction(_) => 0x1B,
} }
} }
@ -305,6 +309,29 @@ impl BossBar {
} }
} }
#[derive(Encoder, Decoder, Debug, PartialEq)]
pub struct EntityAction {
#[data_type(with = "var_int")]
pub entity_id: i32,
pub action_id: EntityActionId,
#[data_type(with = "var_int")]
pub jump_boost: i32,
}
#[derive(Encoder, Decoder, Debug, PartialEq)]
#[data_type(with = "var_int")]
pub enum EntityActionId {
StartSneaking,
StopSneaking,
LeaveBad,
StartSprinting,
StopSprinting,
StartJumpWithHorse,
StopJumpWithHorse,
OpenHorseInventory,
StartFlyingWithElytra,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::data::chat::Payload; use crate::data::chat::Payload;
@ -626,4 +653,37 @@ mod tests {
action: BossBarAction::Remove, action: BossBarAction::Remove,
} }
} }
#[test]
fn test_entity_action_encode() {
let entity_action = EntityAction {
entity_id: 12345,
action_id: EntityActionId::StartFlyingWithElytra,
jump_boost: i32::MAX,
};
let mut vec = Vec::new();
entity_action.encode(&mut vec).unwrap();
assert_eq!(
vec,
include_bytes!("../../../test/packet/game/entity_action.dat").to_vec()
);
}
#[test]
fn test_entity_action_decode() {
let mut cursor =
Cursor::new(include_bytes!("../../../test/packet/game/entity_action.dat").to_vec());
let entity_action = EntityAction::decode(&mut cursor).unwrap();
assert_eq!(
entity_action,
EntityAction {
entity_id: 12345,
action_id: EntityActionId::StartFlyingWithElytra,
jump_boost: i32::MAX,
}
);
}
} }

View File

@ -0,0 +1 @@
<08><><EFBFBD><EFBFBD>