diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2022-12-07 21:09:58 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-12-07 21:09:58 -0600 |
| commit | 7d901e39bc10a855b545d7b6c167f45148a1fb0a (patch) | |
| tree | 88fe0a8f2f04f49f4df90e2f5462aa35a4278c68 /azalea-buf | |
| parent | 9f5e5c092be9167e4d5222fdee4a1d2c419e5052 (diff) | |
| download | azalea-drasl-7d901e39bc10a855b545d7b6c167f45148a1fb0a.tar.xz | |
1.19.3 (#34)
* start updating to 22w42a
* work a bit more on 22w42a
* player chat packet
* serverbound hello packet
* Update mod.rs
* add more stuff to clientbound player chat packet
* ClientboundPlayerInfoUpdatePacket
* features enabled and container closed
* serverbound chat packets
* make it compile
* 22w43a
* ServerboundChatSessionUpdatePacket
* profile_public_key isn't Option anymore
* Update bitset.rs
* joining a server works
* fix entitydatavalue
* backtraces + fix clientbound chat message
* fix some warnings and add more ecomments
* 22w44a
* generate en_us.json
* add updating guide to codegen/readme
* fix some markdown
* update list of generated things
* metadata stuff
* Replace PJS generator mod with PixLyzer (#38)
* pixlizer extractor
* start working on shape extraction
* fix generating language
* fix pixlyzer shape generation
* use empty_shape
* generate blocks and shapes
* update pixlyzer dir
* Revert "update pixlyzer dir"
This reverts commit ee9a0e7a49936dd8569c610ba9b6455895eeff71.
* fix
* fix
* Revert "fix"
This reverts commit ad12ddcb009ccc4eeb13ddef0871db1d9322ab7d.
* fix
* detect pixlyzer fail
* fix pixlyzer
* 22w45a
* gen entities
* add async-trait dep
* update codegen/readme.md
* explain when rust_log should be used
* remove some unused code
* start fixing pixlyzer issues
* fix a thing in codegen
* almost fixed
* more progress towards 1.19.3
* 1.19.3-pre2
* fixes
* revert some hardcoded property names
* Delete clientbound_player_info_packet.rs
* handle 1.19.3 player info packets
* handle playerinforemove
* start updating to 1.19.3-rc1
* optional registries work
* fix some issues with 1.19.3
chat doesn't work yet
* aaaaaaaaaaaaaaaaa
* oh
* ignore unused shapes
* uncomment generate_blocks
* fix migrate
* 1.19.3-rc2
* fix clippy warnings
* 1.19.3-rc3
* split the azalea-buf macro into separate modules
* improve Recipe in protocol
* 1.19.3
Diffstat (limited to 'azalea-buf')
| -rw-r--r--[-rwxr-xr-x] | azalea-buf/Cargo.toml | 5 | ||||
| -rwxr-xr-x | azalea-buf/azalea-buf-macros/src/lib.rs | 282 | ||||
| -rw-r--r-- | azalea-buf/azalea-buf-macros/src/read.rs | 158 | ||||
| -rw-r--r-- | azalea-buf/azalea-buf-macros/src/write.rs | 193 | ||||
| -rwxr-xr-x | azalea-buf/src/read.rs | 40 |
5 files changed, 393 insertions, 285 deletions
diff --git a/azalea-buf/Cargo.toml b/azalea-buf/Cargo.toml index 01048ceb..d58e6733 100755..100644 --- a/azalea-buf/Cargo.toml +++ b/azalea-buf/Cargo.toml @@ -9,10 +9,11 @@ version = "0.4.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -azalea-buf-macros = {path = "./azalea-buf-macros", version = "^0.4.0" } +azalea-buf-macros = {path = "./azalea-buf-macros", version = "^0.4.0"} byteorder = "^1.4.3" +log = "0.4.17" serde_json = {version = "^1.0", optional = true} -thiserror = "^1.0.34" +thiserror = "1.0.37" tokio = {version = "^1.21.2", features = ["io-util", "net", "macros"]} uuid = "^1.1.2" diff --git a/azalea-buf/azalea-buf-macros/src/lib.rs b/azalea-buf/azalea-buf-macros/src/lib.rs index a247db0f..4089b4b7 100755 --- a/azalea-buf/azalea-buf-macros/src/lib.rs +++ b/azalea-buf/azalea-buf-macros/src/lib.rs @@ -1,294 +1,30 @@ -use proc_macro::TokenStream; -use quote::{quote, ToTokens}; -use syn::{self, parse_macro_input, Data, DeriveInput, FieldsNamed, Ident}; - -fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream { - match data { - syn::Data::Struct(syn::DataStruct { fields, .. }) => { - let FieldsNamed { named, .. } = match fields { - syn::Fields::Named(f) => f, - _ => panic!("#[derive(McBuf)] can only be used on structs with named fields"), - }; - - let read_fields = named - .iter() - .map(|f| { - let field_name = &f.ident; - let field_type = &f.ty; - // do a different buf.write_* for each field depending on the type - // if it's a string, use buf.write_string - match field_type { - syn::Type::Path(_) | syn::Type::Array(_) => { - if f.attrs.iter().any(|a| a.path.is_ident("var")) { - quote! { - let #field_name = azalea_buf::McBufVarReadable::var_read_from(buf)?; - } - } else { - quote! { - let #field_name = azalea_buf::McBufReadable::read_from(buf)?; - } - } - } - _ => panic!( - "Error reading field {}: {}", - field_name.clone().unwrap(), - field_type.to_token_stream() - ), - } - }) - .collect::<Vec<_>>(); - let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>(); - - quote! { - impl azalea_buf::McBufReadable for #ident { - fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> { - #(#read_fields)* - Ok(#ident { - #(#read_field_names: #read_field_names),* - }) - } - } - } - } - syn::Data::Enum(syn::DataEnum { variants, .. }) => { - let mut match_contents = quote!(); - let mut variant_discrim: u32 = 0; - let mut first = true; - let mut first_reader = None; - for variant in variants { - let variant_name = &variant.ident; - match &variant.discriminant.as_ref() { - Some(d) => { - variant_discrim = match &d.1 { - syn::Expr::Lit(e) => match &e.lit { - syn::Lit::Int(i) => i.base10_parse().unwrap(), - _ => panic!("Error parsing enum discriminant as int"), - }, - syn::Expr::Unary(_) => { - panic!("Negative enum discriminants are not supported") - } - _ => { - panic!("Error parsing enum discriminant as literal (is {:?})", d.1) - } - } - } - None => { - if !first { - variant_discrim += 1; - } - } - } - let reader = match variant.fields { - syn::Fields::Named(_) => { - panic!("writing named fields in enums is not supported") - } - syn::Fields::Unnamed(_) => quote! { - Ok(Self::#variant_name(azalea_buf::McBufReadable::read_from(buf)?)) - }, - syn::Fields::Unit => quote! { - Ok(Self::#variant_name) - }, - }; - if first { - first_reader = Some(reader.clone()); - first = false; - }; - - match_contents.extend(quote! { - #variant_discrim => { - #reader - }, - }); - } - - let first_reader = first_reader.expect("There should be at least one variant"); - - quote! { - impl azalea_buf::McBufReadable for #ident { - fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> { - let id = azalea_buf::McBufVarReadable::var_read_from(buf)?; - Self::read_from_id(buf, id) - } - } - - impl #ident { - pub fn read_from_id(buf: &mut std::io::Cursor<&[u8]>, id: u32) -> Result<Self, azalea_buf::BufReadError> { - match id { - #match_contents - // you'd THINK this throws an error, but mojang decided to make it default for some reason - _ => #first_reader - } - } - } - } - } - _ => panic!("#[derive(McBuf)] can only be used on structs"), - } -} - -fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream { - match data { - syn::Data::Struct(syn::DataStruct { fields, .. }) => { - let FieldsNamed { named, .. } = match fields { - syn::Fields::Named(f) => f, - _ => panic!("#[derive(McBuf)] can only be used on structs with named fields"), - }; - - let write_fields = named - .iter() - .map(|f| { - let field_name = &f.ident; - let field_type = &f.ty; - // do a different buf.write_* for each field depending on the type - // if it's a string, use buf.write_string - match field_type { - syn::Type::Path(_) | syn::Type::Array(_) => { - if f.attrs.iter().any(|attr| attr.path.is_ident("var")) { - quote! { - azalea_buf::McBufVarWritable::var_write_into(&self.#field_name, buf)?; - } - } else { - quote! { - azalea_buf::McBufWritable::write_into(&self.#field_name, buf)?; - } - } - } - _ => panic!( - "Error writing field {}: {}", - field_name.clone().unwrap(), - field_type.to_token_stream() - ), - } - }) - .collect::<Vec<_>>(); +mod read; +mod write; - quote! { - impl azalea_buf::McBufWritable for #ident { - fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - #(#write_fields)* - Ok(()) - } - } - } - } - syn::Data::Enum(syn::DataEnum { variants, .. }) => { - // remember whether it's a data variant so we can do an optimization later - let mut is_data_enum = false; - let mut match_arms = quote!(); - let mut match_arms_without_id = quote!(); - let mut variant_discrim: u32 = 0; - let mut first = true; - for variant in variants { - match &variant.discriminant.as_ref() { - Some(d) => { - variant_discrim = match &d.1 { - syn::Expr::Lit(e) => match &e.lit { - syn::Lit::Int(i) => i.base10_parse().unwrap(), - _ => panic!("Error parsing enum discriminant as int"), - }, - syn::Expr::Unary(_) => { - panic!("Negative enum discriminants are not supported") - } - _ => { - panic!("Error parsing enum discriminant as literal (is {:?})", d.1) - } - } - } - None => { - if first { - first = false; - } else { - variant_discrim += 1; - } - } - } - - match &variant.fields { - syn::Fields::Named(_) => { - panic!("Enum variants with named fields are not supported yet"); - } - syn::Fields::Unit => { - let variant_name = &variant.ident; - match_arms.extend(quote! { - Self::#variant_name => { - azalea_buf::McBufVarWritable::var_write_into(&#variant_discrim, buf)?; - } - }); - match_arms_without_id.extend(quote! { - Self::#variant_name => {} - }); - } - syn::Fields::Unnamed(_) => { - is_data_enum = true; - let variant_name = &variant.ident; - match_arms.extend(quote! { - Self::#variant_name(data) => { - azalea_buf::McBufVarWritable::var_write_into(&#variant_discrim, buf)?; - azalea_buf::McBufWritable::write_into(data, buf)?; - } - }); - match_arms_without_id.extend(quote! { - Self::#variant_name(data) => { - azalea_buf::McBufWritable::write_into(data, buf)?; - } - }); - } - } - } - if is_data_enum { - quote! { - impl azalea_buf::McBufWritable for #ident { - fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - match self { - #match_arms - } - Ok(()) - } - } - impl #ident { - pub fn write_without_id(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - match self { - #match_arms_without_id - } - Ok(()) - } - } - } - } else { - // optimization: if it doesn't have data we can just do `as u32` - quote! { - impl azalea_buf::McBufWritable for #ident { - fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - azalea_buf::McBufVarWritable::var_write_into(&(*self as u32), buf) - } - } - } - } - } - _ => panic!("#[derive(McBuf)] can only be used on structs"), - } -} +use proc_macro::TokenStream; +use quote::quote; +use syn::{self, parse_macro_input, DeriveInput}; #[proc_macro_derive(McBufReadable, attributes(var))] pub fn derive_mcbufreadable(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); - create_impl_mcbufreadable(&ident, &data).into() + read::create_impl_mcbufreadable(&ident, &data).into() } #[proc_macro_derive(McBufWritable, attributes(var))] pub fn derive_mcbufwritable(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); - create_impl_mcbufwritable(&ident, &data).into() + write::create_impl_mcbufwritable(&ident, &data).into() } #[proc_macro_derive(McBuf, attributes(var))] pub fn derive_mcbuf(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); - let writable = create_impl_mcbufwritable(&ident, &data); - let readable = create_impl_mcbufreadable(&ident, &data); + let writable = write::create_impl_mcbufwritable(&ident, &data); + let readable = read::create_impl_mcbufreadable(&ident, &data); quote! { #writable #readable diff --git a/azalea-buf/azalea-buf-macros/src/read.rs b/azalea-buf/azalea-buf-macros/src/read.rs new file mode 100644 index 00000000..42050d6b --- /dev/null +++ b/azalea-buf/azalea-buf-macros/src/read.rs @@ -0,0 +1,158 @@ +use proc_macro::TokenStream; +use proc_macro2::Span; +use quote::{quote, ToTokens}; +use syn::{ + self, parse_macro_input, punctuated::Punctuated, token::Comma, Data, DeriveInput, Field, + FieldsNamed, Ident, +}; + +fn read_named_fields( + named: &Punctuated<Field, Comma>, +) -> (Vec<proc_macro2::TokenStream>, Vec<&Option<Ident>>) { + let read_fields = named + .iter() + .map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + // do a different buf.write_* for each field depending on the type + // if it's a string, use buf.write_string + match field_type { + syn::Type::Path(_) | syn::Type::Array(_) => { + if f.attrs.iter().any(|a| a.path.is_ident("var")) { + quote! { + let #field_name = azalea_buf::McBufVarReadable::var_read_from(buf)?; + } + } else { + quote! { + let #field_name = azalea_buf::McBufReadable::read_from(buf)?; + } + } + } + _ => panic!( + "Error reading field {}: {}", + field_name.clone().unwrap(), + field_type.to_token_stream() + ), + } + }) + .collect::<Vec<_>>(); + let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>(); + + (read_fields, read_field_names) +} + +pub fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream { + match data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => { + let FieldsNamed { named, .. } = match fields { + syn::Fields::Named(f) => f, + _ => panic!("#[derive(McBuf)] can only be used on structs with named fields"), + }; + + let (read_fields, read_field_names) = read_named_fields(named); + + quote! { + impl azalea_buf::McBufReadable for #ident { + fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> { + #(#read_fields)* + Ok(#ident { + #(#read_field_names: #read_field_names),* + }) + } + } + } + } + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + let mut match_contents = quote!(); + let mut variant_discrim: u32 = 0; + let mut first = true; + let mut first_reader = None; + for variant in variants { + let variant_name = &variant.ident; + match &variant.discriminant.as_ref() { + Some(d) => { + variant_discrim = match &d.1 { + syn::Expr::Lit(e) => match &e.lit { + syn::Lit::Int(i) => i.base10_parse().unwrap(), + _ => panic!("Error parsing enum discriminant as int (is {:?})", e), + }, + syn::Expr::Unary(_) => { + panic!("Negative enum discriminants are not supported") + } + _ => { + panic!("Error parsing enum discriminant as literal (is {:?})", d.1) + } + } + } + None => { + if !first { + variant_discrim += 1; + } + } + } + let reader = match &variant.fields { + syn::Fields::Named(f) => { + let (read_fields, read_field_names) = read_named_fields(&f.named); + + quote! { + #(#read_fields)* + Ok(#ident::#variant_name { + #(#read_field_names: #read_field_names),* + }) + } + } + syn::Fields::Unnamed(fields) => { + let mut reader_code = quote! {}; + for f in &fields.unnamed { + if f.attrs.iter().any(|attr| attr.path.is_ident("var")) { + reader_code.extend(quote! { + Self::#variant_name(azalea_buf::McBufVarReadable::var_read_from(buf)?), + }) + } else { + reader_code.extend(quote! { + Self::#variant_name(azalea_buf::McBufReadable::read_from(buf)?), + }) + } + } + quote! { Ok(#reader_code) } + } + syn::Fields::Unit => quote! { + Ok(Self::#variant_name) + }, + }; + if first { + first_reader = Some(reader.clone()); + first = false; + }; + + match_contents.extend(quote! { + #variant_discrim => { + #reader + }, + }); + } + + let first_reader = first_reader.expect("There should be at least one variant"); + + quote! { + impl azalea_buf::McBufReadable for #ident { + fn read_from(buf: &mut std::io::Cursor<&[u8]>) -> Result<Self, azalea_buf::BufReadError> { + let id = azalea_buf::McBufVarReadable::var_read_from(buf)?; + Self::read_from_id(buf, id) + } + } + + impl #ident { + pub fn read_from_id(buf: &mut std::io::Cursor<&[u8]>, id: u32) -> Result<Self, azalea_buf::BufReadError> { + match id { + #match_contents + // you'd THINK this throws an error, but mojang decided to make it default for some reason + _ => {#first_reader} + } + } + } + } + } + _ => panic!("#[derive(McBuf)] can only be used on structs"), + } +} diff --git a/azalea-buf/azalea-buf-macros/src/write.rs b/azalea-buf/azalea-buf-macros/src/write.rs new file mode 100644 index 00000000..a711dbdd --- /dev/null +++ b/azalea-buf/azalea-buf-macros/src/write.rs @@ -0,0 +1,193 @@ +use proc_macro2::Span; +use quote::{quote, ToTokens}; +use syn::{self, punctuated::Punctuated, token::Comma, Data, Field, FieldsNamed, Ident}; + +fn write_named_fields( + named: &Punctuated<Field, Comma>, + ident_name: Option<&Ident>, +) -> proc_macro2::TokenStream { + let write_fields = named.iter().map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + let ident_dot_field = match ident_name { + Some(ident) => quote! { &#ident.#field_name }, + None => quote! { #field_name }, + }; + // do a different buf.write_* for each field depending on the type + // if it's a string, use buf.write_string + match field_type { + syn::Type::Path(_) | syn::Type::Array(_) => { + if f.attrs.iter().any(|attr| attr.path.is_ident("var")) { + quote! { + azalea_buf::McBufVarWritable::var_write_into(#ident_dot_field, buf)?; + } + } else { + quote! { + azalea_buf::McBufWritable::write_into(#ident_dot_field, buf)?; + } + } + } + _ => panic!( + "Error writing field {}: {}", + field_name.clone().unwrap(), + field_type.to_token_stream() + ), + } + }); + quote! { #(#write_fields)* } +} + +pub fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream { + match data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => { + let FieldsNamed { named, .. } = match fields { + syn::Fields::Named(f) => f, + _ => panic!("#[derive(McBuf)] can only be used on structs with named fields"), + }; + + let write_fields = + write_named_fields(named, Some(&Ident::new("self", Span::call_site()))); + + quote! { + impl azalea_buf::McBufWritable for #ident { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + #write_fields + Ok(()) + } + } + } + } + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + // remember whether it's a data variant so we can do an optimization later + let mut is_data_enum = false; + let mut match_arms = quote!(); + let mut match_arms_without_id = quote!(); + let mut variant_discrim: u32 = 0; + let mut first = true; + for variant in variants { + match &variant.discriminant.as_ref() { + Some(d) => { + variant_discrim = match &d.1 { + syn::Expr::Lit(e) => match &e.lit { + syn::Lit::Int(i) => i.base10_parse().unwrap(), + // syn::Lit::Str(s) => s.value(), + _ => panic!("Error parsing enum discriminant as int (is {:?})", e), + }, + syn::Expr::Unary(_) => { + panic!("Negative enum discriminants are not supported") + } + _ => { + panic!("Error parsing enum discriminant as literal (is {:?})", d.1) + } + } + } + None => { + if first { + first = false; + } else { + variant_discrim += 1; + } + } + } + + let variant_name = &variant.ident; + + // the variant number that we're going to write + let write_the_variant = quote! { + azalea_buf::McBufVarWritable::var_write_into(&#variant_discrim, buf)?; + }; + match &variant.fields { + syn::Fields::Named(f) => { + is_data_enum = true; + let field_names = f + .named + .iter() + .map(|f| f.ident.clone().unwrap()) + .collect::<Vec<_>>(); + let write_fields = write_named_fields(&f.named, None); + match_arms.extend(quote! { + Self::#variant_name { #(#field_names),* } => { + #write_the_variant + #write_fields + } + }); + match_arms_without_id.extend(quote! { + Self::#variant_name { #(#field_names),* } => { + #write_fields + } + }); + } + syn::Fields::Unit => { + match_arms.extend(quote! { + Self::#variant_name => { + #write_the_variant + } + }); + match_arms_without_id.extend(quote! { + Self::#variant_name => {} + }); + } + syn::Fields::Unnamed(fields) => { + is_data_enum = true; + let mut writers_code = quote! {}; + let mut params_code = quote! {}; + for (i, f) in fields.unnamed.iter().enumerate() { + let param_ident = Ident::new(&format!("data{i}"), Span::call_site()); + params_code.extend(quote! { #param_ident, }); + if f.attrs.iter().any(|attr| attr.path.is_ident("var")) { + writers_code.extend(quote! { + azalea_buf::McBufVarWritable::var_write_into(#param_ident, buf)?; + }) + } else { + writers_code.extend(quote! { + azalea_buf::McBufWritable::write_into(#param_ident, buf)?; + }) + } + } + match_arms.extend(quote! { + Self::#variant_name(#params_code) => { + #write_the_variant + #writers_code + } + }); + match_arms_without_id.extend(quote! { + Self::#variant_name(data) => { + azalea_buf::McBufWritable::write_into(data, buf)?; + } + }); + } + } + } + if is_data_enum { + quote! { + impl azalea_buf::McBufWritable for #ident { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + match self { + #match_arms + } + Ok(()) + } + } + impl #ident { + pub fn write_without_id(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + match self { + #match_arms_without_id + } + Ok(()) + } + } + } + } else { + // optimization: if it doesn't have data we can just do `as u32` + quote! { + impl azalea_buf::McBufWritable for #ident { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + azalea_buf::McBufVarWritable::var_write_into(&(*self as u32), buf) + } + } + } + } + } + _ => panic!("#[derive(McBuf)] can only be used on structs"), + } +} diff --git a/azalea-buf/src/read.rs b/azalea-buf/src/read.rs index b387dcfb..69a7d8ef 100755 --- a/azalea-buf/src/read.rs +++ b/azalea-buf/src/read.rs @@ -1,6 +1,8 @@ use super::{UnsizedByteArray, MAX_STRING_LENGTH}; use byteorder::{ReadBytesExt, BE}; +use log::warn; use std::{ + backtrace::Backtrace, collections::HashMap, hash::Hash, io::{Cursor, Read}, @@ -17,14 +19,18 @@ pub enum BufReadError { CouldNotReadBytes, #[error("The received encoded string buffer length is longer than maximum allowed ({length} > {max_length})")] StringLengthTooLong { length: u32, max_length: u32 }, - #[error("{0}")] - Io( + #[error("{source}")] + Io { #[from] #[backtrace] - std::io::Error, - ), - #[error("Invalid UTF-8")] - InvalidUtf8, + source: std::io::Error, + }, + #[error("Invalid UTF-8: {bytes:?} (lossy: {lossy:?})")] + InvalidUtf8 { + bytes: Vec<u8>, + lossy: String, + // backtrace: Backtrace, + }, #[error("Unexpected enum variant {id}")] UnexpectedEnumVariant { id: i32 }, #[error("Unexpected enum variant {id}")] @@ -33,12 +39,17 @@ pub enum BufReadError { UnexpectedEof { attempted_read: usize, actual_read: usize, + backtrace: Backtrace, }, #[error("{0}")] Custom(String), #[cfg(feature = "serde_json")] - #[error("{0}")] - Deserialization(#[from] serde_json::Error), + #[error("{source}")] + Deserialization { + #[from] + #[backtrace] + source: serde_json::Error, + }, } fn read_bytes<'a>(buf: &'a mut Cursor<&[u8]>, length: usize) -> Result<&'a [u8], BufReadError> { @@ -46,6 +57,7 @@ fn read_bytes<'a>(buf: &'a mut Cursor<&[u8]>, length: usize) -> Result<&'a [u8], return Err(BufReadError::UnexpectedEof { attempted_read: length, actual_read: buf.get_ref().len() - buf.position() as usize, + backtrace: Backtrace::capture(), }); } let initial_position = buf.position() as usize; @@ -66,7 +78,11 @@ fn read_utf_with_len(buf: &mut Cursor<&[u8]>, max_length: u32) -> Result<String, let buffer = read_bytes(buf, length as usize)?; let string = std::str::from_utf8(buffer) - .map_err(|_| BufReadError::InvalidUtf8)? + .map_err(|_| BufReadError::InvalidUtf8 { + bytes: buffer.to_vec(), + lossy: String::from_utf8_lossy(buffer).to_string(), + // backtrace: Backtrace::capture(), + })? .to_string(); if string.len() > length as usize { return Err(BufReadError::StringLengthTooLong { length, max_length }); @@ -265,7 +281,11 @@ impl McBufReadable for u64 { impl McBufReadable for bool { fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> { - Ok(u8::read_from(buf)? != 0) + let byte = u8::read_from(buf)?; + if byte > 1 { + warn!("Boolean value was not 0 or 1, but {}", byte); + } + Ok(byte != 0) } } |
