aboutsummaryrefslogtreecommitdiff
path: root/azalea-buf
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2022-12-07 21:09:58 -0600
committerGitHub <noreply@github.com>2022-12-07 21:09:58 -0600
commit7d901e39bc10a855b545d7b6c167f45148a1fb0a (patch)
tree88fe0a8f2f04f49f4df90e2f5462aa35a4278c68 /azalea-buf
parent9f5e5c092be9167e4d5222fdee4a1d2c419e5052 (diff)
downloadazalea-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.toml5
-rwxr-xr-xazalea-buf/azalea-buf-macros/src/lib.rs282
-rw-r--r--azalea-buf/azalea-buf-macros/src/read.rs158
-rw-r--r--azalea-buf/azalea-buf-macros/src/write.rs193
-rwxr-xr-xazalea-buf/src/read.rs40
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)
}
}