From 5ca49e680ed8519456dc9a9af84321d4b69dcbb3 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 23 Jun 2022 15:12:17 -0500 Subject: azalea-buf --- Cargo.lock | 23 + Cargo.toml | 1 + azalea-buf/Cargo.toml | 11 + azalea-buf/README.md | 3 + azalea-buf/buf-macros/Cargo.toml | 13 + azalea-buf/buf-macros/src/lib.rs | 177 +++++++ azalea-buf/src/definitions.rs | 39 ++ azalea-buf/src/lib.rs | 190 ++++++++ azalea-buf/src/read.rs | 380 +++++++++++++++ azalea-buf/src/write.rs | 284 +++++++++++ azalea-chat/Cargo.toml | 1 + azalea-chat/src/component.rs | 27 ++ azalea-client/src/client.rs | 51 +- azalea-core/Cargo.toml | 1 + azalea-core/src/delta.rs | 26 + azalea-core/src/difficulty.rs | 12 + azalea-core/src/direction.rs | 2 +- azalea-core/src/game_type.rs | 24 + azalea-core/src/lib.rs | 6 + azalea-core/src/particle/mod.rs | 259 ++++++++++ azalea-core/src/position.rs | 59 +++ azalea-core/src/resource_location.rs | 26 + azalea-core/src/serializable_uuid.rs | 22 + azalea-core/src/slot.rs | 28 ++ azalea-crypto/Cargo.toml | 1 + azalea-crypto/src/lib.rs | 2 + azalea-crypto/src/signing.rs | 16 + azalea-entity/src/data.rs | 136 ++++++ azalea-entity/src/lib.rs | 22 +- azalea-nbt/Cargo.toml | 1 + azalea-nbt/src/decode.rs | 10 + azalea-nbt/src/encode.rs | 7 + azalea-nbt/src/lib.rs | 33 ++ azalea-protocol/Cargo.toml | 10 +- azalea-protocol/packet-macros/src/lib.rs | 177 ------- azalea-protocol/src/lib.rs | 6 +- azalea-protocol/src/mc_buf/definitions.rs | 442 ----------------- azalea-protocol/src/mc_buf/mod.rs | 233 --------- azalea-protocol/src/mc_buf/read.rs | 525 --------------------- azalea-protocol/src/mc_buf/write.rs | 430 ----------------- .../packets/game/clientbound_add_player_packet.rs | 3 +- .../game/clientbound_move_entity_pos_packet.rs | 5 +- .../game/clientbound_move_entity_posrot_packet.rs | 8 +- azalea-world/src/lib.rs | 16 + bot/src/main.rs | 2 +- 45 files changed, 1909 insertions(+), 1841 deletions(-) create mode 100644 azalea-buf/Cargo.toml create mode 100644 azalea-buf/README.md create mode 100644 azalea-buf/buf-macros/Cargo.toml create mode 100644 azalea-buf/buf-macros/src/lib.rs create mode 100644 azalea-buf/src/definitions.rs create mode 100644 azalea-buf/src/lib.rs create mode 100644 azalea-buf/src/read.rs create mode 100644 azalea-buf/src/write.rs create mode 100644 azalea-core/src/delta.rs create mode 100644 azalea-core/src/particle/mod.rs create mode 100644 azalea-entity/src/data.rs delete mode 100644 azalea-protocol/src/mc_buf/definitions.rs delete mode 100644 azalea-protocol/src/mc_buf/mod.rs delete mode 100644 azalea-protocol/src/mc_buf/read.rs delete mode 100644 azalea-protocol/src/mc_buf/write.rs diff --git a/Cargo.lock b/Cargo.lock index c3e5a0ce..855b65de 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,10 +89,20 @@ dependencies = [ name = "azalea-brigadier" version = "0.1.0" +[[package]] +name = "azalea-buf" +version = "0.1.0" +dependencies = [ + "buf-macros", + "byteorder", + "tokio", +] + [[package]] name = "azalea-chat" version = "0.1.0" dependencies = [ + "azalea-buf", "azalea-language", "lazy_static", "serde", @@ -117,6 +127,7 @@ dependencies = [ name = "azalea-core" version = "0.1.0" dependencies = [ + "azalea-buf", "azalea-chat", "azalea-nbt", "uuid", @@ -127,6 +138,7 @@ name = "azalea-crypto" version = "0.1.0" dependencies = [ "aes", + "azalea-buf", "cfb8", "num-bigint", "rand", @@ -156,6 +168,7 @@ dependencies = [ name = "azalea-nbt" version = "0.1.0" dependencies = [ + "azalea-buf", "byteorder", "criterion", "flate2", @@ -171,6 +184,7 @@ dependencies = [ "async-recursion", "azalea-auth", "azalea-brigadier", + "azalea-buf", "azalea-chat", "azalea-core", "azalea-crypto", @@ -250,6 +264,15 @@ dependencies = [ "serde", ] +[[package]] +name = "buf-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "bumpalo" version = "3.10.0" diff --git a/Cargo.toml b/Cargo.toml index fc32b2d9..72019b5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "azalea-language", "azalea-block", "azalea-entity", + "azalea-buf", ] [profile.release] diff --git a/azalea-buf/Cargo.toml b/azalea-buf/Cargo.toml new file mode 100644 index 00000000..79f9d64d --- /dev/null +++ b/azalea-buf/Cargo.toml @@ -0,0 +1,11 @@ +[package] +edition = "2021" +name = "azalea-buf" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +buf-macros = {path = "./buf-macros"} +byteorder = "1.4.3" +tokio = {version = "^1.19.2", features = ["io-util", "net", "macros"]} diff --git a/azalea-buf/README.md b/azalea-buf/README.md new file mode 100644 index 00000000..c988bcdb --- /dev/null +++ b/azalea-buf/README.md @@ -0,0 +1,3 @@ +# Azalea Buf + +An implementation of Minecraft's FriendlyByteBuf. This is used frequently in the game for serialization and deserialization of data. diff --git a/azalea-buf/buf-macros/Cargo.toml b/azalea-buf/buf-macros/Cargo.toml new file mode 100644 index 00000000..fecf64ed --- /dev/null +++ b/azalea-buf/buf-macros/Cargo.toml @@ -0,0 +1,13 @@ +[package] +edition = "2021" +name = "buf-macros" +version = "0.1.0" + +[lib] +proc-macro = true +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +proc-macro2 = "^1.0.36" +quote = "^1.0.10" +syn = "^1.0.82" diff --git a/azalea-buf/buf-macros/src/lib.rs b/azalea-buf/buf-macros/src/lib.rs new file mode 100644 index 00000000..3afeaeed --- /dev/null +++ b/azalea-buf/buf-macros/src/lib.rs @@ -0,0 +1,177 @@ +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(_) => { + if f.attrs.iter().any(|a| a.path.is_ident("var")) { + quote! { + let #field_name = crate::McBufVarReadable::var_read_into(buf)?; + } + } else { + quote! { + let #field_name = crate::McBufReadable::read_into(buf)?; + } + } + } + _ => panic!( + "Error reading field {}: {}", + field_name.clone().unwrap(), + field_type.to_token_stream() + ), + } + }) + .collect::>(); + let read_field_names = named.iter().map(|f| &f.ident).collect::>(); + + quote! { + impl crate::McBufReadable for #ident { + fn read_into(buf: &mut impl std::io::Read) -> Result { + #(#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; + 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"), + }, + _ => panic!("Error parsing enum discriminant"), + } + } + None => { + variant_discrim += 1; + } + } + match_contents.extend(quote! { + #variant_discrim => Ok(Self::#variant_name), + }); + } + + quote! { + impl crate::McBufReadable for #ident { + fn read_into(buf: &mut impl std::io::Read) -> Result + { + let id = crate::McBufVarReadable::var_read_into(buf)?; + match id { + #match_contents + _ => Err(format!("Unknown enum variant {}", id)), + } + } + } + } + } + _ => 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(_) => { + if f.attrs.iter().any(|attr| attr.path.is_ident("var")) { + quote! { + crate::McBufVarWritable::var_write_into(&self.#field_name, buf)?; + } + } else { + quote! { + crate::McBufWritable::write_into(&self.#field_name, buf)?; + } + } + } + _ => panic!( + "Error writing field {}: {}", + field_name.clone().unwrap(), + field_type.to_token_stream() + ), + } + }) + .collect::>(); + + quote! { + impl crate::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 { .. }) => { + quote! { + impl crate::McBufWritable for #ident { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + crate::Writable::write_varint(buf, *self as i32) + } + } + } + } + _ => panic!("#[derive(McBuf)] can only be used on structs"), + } +} + +#[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() +} + +#[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() +} + +#[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); + quote! { + #writable + #readable + } + .into() +} diff --git a/azalea-buf/src/definitions.rs b/azalea-buf/src/definitions.rs new file mode 100644 index 00000000..e5d8e0c0 --- /dev/null +++ b/azalea-buf/src/definitions.rs @@ -0,0 +1,39 @@ +use buf_macros::McBuf; +use std::ops::Deref; + +/// A Vec that isn't prefixed by a VarInt with the size. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct UnsizedByteArray(Vec); + +impl Deref for UnsizedByteArray { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From> for UnsizedByteArray { + fn from(vec: Vec) -> Self { + Self(vec) + } +} + +impl From<&str> for UnsizedByteArray { + fn from(s: &str) -> Self { + Self(s.as_bytes().to_vec()) + } +} + +/// Represents Java's BitSet, a list of bits. +#[derive(Debug, Clone, PartialEq, Eq, Hash, McBuf)] +pub struct BitSet { + data: Vec, +} + +// the Index trait requires us to return a reference, but we can't do that +impl BitSet { + pub fn index(&self, index: usize) -> bool { + (self.data[index / 64] & (1u64 << (index % 64))) != 0 + } +} diff --git a/azalea-buf/src/lib.rs b/azalea-buf/src/lib.rs new file mode 100644 index 00000000..2ba17ac2 --- /dev/null +++ b/azalea-buf/src/lib.rs @@ -0,0 +1,190 @@ +//! Utilities for reading and writing for the Minecraft protocol + +#![feature(min_specialization)] +#![feature(arbitrary_enum_discriminant)] + +mod definitions; +mod read; +mod write; + +pub use definitions::*; +pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable}; +pub use write::{McBufVarWritable, McBufWritable, Writable}; + +// const DEFAULT_NBT_QUOTA: u32 = 2097152; +const MAX_STRING_LENGTH: u16 = 32767; +// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144; + +#[cfg(test)] +mod tests { + use super::*; + use std::{collections::HashMap, io::Cursor}; + + #[test] + fn test_write_varint() { + let mut buf = Vec::new(); + buf.write_varint(0).unwrap(); + assert_eq!(buf, vec![0]); + + let mut buf = Vec::new(); + buf.write_varint(1).unwrap(); + assert_eq!(buf, vec![1]); + + let mut buf = Vec::new(); + buf.write_varint(2).unwrap(); + assert_eq!(buf, vec![2]); + + let mut buf = Vec::new(); + buf.write_varint(127).unwrap(); + assert_eq!(buf, vec![127]); + + let mut buf = Vec::new(); + buf.write_varint(128).unwrap(); + assert_eq!(buf, vec![128, 1]); + + let mut buf = Vec::new(); + buf.write_varint(255).unwrap(); + assert_eq!(buf, vec![255, 1]); + + let mut buf = Vec::new(); + buf.write_varint(25565).unwrap(); + assert_eq!(buf, vec![221, 199, 1]); + + let mut buf = Vec::new(); + buf.write_varint(2097151).unwrap(); + assert_eq!(buf, vec![255, 255, 127]); + + let mut buf = Vec::new(); + buf.write_varint(2147483647).unwrap(); + assert_eq!(buf, vec![255, 255, 255, 255, 7]); + + let mut buf = Vec::new(); + buf.write_varint(-1).unwrap(); + assert_eq!(buf, vec![255, 255, 255, 255, 15]); + + let mut buf = Vec::new(); + buf.write_varint(-2147483648).unwrap(); + assert_eq!(buf, vec![128, 128, 128, 128, 8]); + } + + #[test] + fn test_read_varint() { + let mut buf = Cursor::new(vec![0]); + assert_eq!(buf.read_varint().unwrap(), 0); + assert_eq!(buf.get_varint_size(0), 1); + + let mut buf = Cursor::new(vec![1]); + assert_eq!(buf.read_varint().unwrap(), 1); + assert_eq!(buf.get_varint_size(1), 1); + + let mut buf = Cursor::new(vec![2]); + assert_eq!(buf.read_varint().unwrap(), 2); + assert_eq!(buf.get_varint_size(2), 1); + + let mut buf = Cursor::new(vec![127]); + assert_eq!(buf.read_varint().unwrap(), 127); + assert_eq!(buf.get_varint_size(127), 1); + + let mut buf = Cursor::new(vec![128, 1]); + assert_eq!(buf.read_varint().unwrap(), 128); + assert_eq!(buf.get_varint_size(128), 2); + + let mut buf = Cursor::new(vec![255, 1]); + assert_eq!(buf.read_varint().unwrap(), 255); + assert_eq!(buf.get_varint_size(255), 2); + + let mut buf = Cursor::new(vec![221, 199, 1]); + assert_eq!(buf.read_varint().unwrap(), 25565); + assert_eq!(buf.get_varint_size(25565), 3); + + let mut buf = Cursor::new(vec![255, 255, 127]); + assert_eq!(buf.read_varint().unwrap(), 2097151); + assert_eq!(buf.get_varint_size(2097151), 3); + + let mut buf = Cursor::new(vec![255, 255, 255, 255, 7]); + assert_eq!(buf.read_varint().unwrap(), 2147483647); + assert_eq!(buf.get_varint_size(2147483647), 5); + + let mut buf = Cursor::new(vec![255, 255, 255, 255, 15]); + assert_eq!(buf.read_varint().unwrap(), -1); + assert_eq!(buf.get_varint_size(-1), 5); + + let mut buf = Cursor::new(vec![128, 128, 128, 128, 8]); + assert_eq!(buf.read_varint().unwrap(), -2147483648); + assert_eq!(buf.get_varint_size(-2147483648), 5); + } + + #[test] + fn test_read_varint_longer() { + let mut buf = Cursor::new(vec![138, 56, 0, 135, 56, 123]); + assert_eq!(buf.read_varint().unwrap(), 7178); + } + + #[test] + fn test_list() { + let mut buf = Vec::new(); + buf.write_list(&vec!["a", "bc", "def"], |buf, s| buf.write_utf(s)) + .unwrap(); + + // there's no read_list because idk how to do it in rust + let mut buf = Cursor::new(buf); + + let mut result = Vec::new(); + let length = buf.read_varint().unwrap(); + for _ in 0..length { + result.push(buf.read_utf().unwrap()); + } + + assert_eq!(result, vec!["a", "bc", "def"]); + } + + #[test] + fn test_int_id_list() { + let mut buf = Vec::new(); + buf.write_list(&vec![1, 2, 3], |buf, i| buf.write_varint(*i)) + .unwrap(); + + let mut buf = Cursor::new(buf); + + let result = buf.read_int_id_list().unwrap(); + assert_eq!(result, vec![1, 2, 3]); + } + + #[test] + fn test_map() { + let mut buf = Vec::new(); + buf.write_map( + vec![("a", 1), ("bc", 23), ("def", 456)], + Vec::write_utf, + Vec::write_varint, + ) + .unwrap(); + + let mut buf = Cursor::new(buf); + + let mut result = Vec::new(); + let length = buf.read_varint().unwrap(); + for _ in 0..length { + result.push((buf.read_utf().unwrap(), buf.read_varint().unwrap())); + } + + assert_eq!( + result, + vec![ + ("a".to_string(), 1), + ("bc".to_string(), 23), + ("def".to_string(), 456) + ] + ); + } + + #[test] + fn test_long() { + let mut buf = Vec::new(); + buf.write_long(123456).unwrap(); + + let mut buf = Cursor::new(buf); + + assert_eq!(buf.read_long().unwrap(), 123456); + } +} diff --git a/azalea-buf/src/read.rs b/azalea-buf/src/read.rs new file mode 100644 index 00000000..569a5b1d --- /dev/null +++ b/azalea-buf/src/read.rs @@ -0,0 +1,380 @@ +use super::{UnsizedByteArray, MAX_STRING_LENGTH}; +use byteorder::{ReadBytesExt, BE}; +use std::{collections::HashMap, hash::Hash, io::Read}; +use tokio::io::{AsyncRead, AsyncReadExt}; + +// TODO: get rid of Readable and use McBufReadable everywhere + +pub trait Readable { + fn read_int_id_list(&mut self) -> Result, String>; + fn read_varint(&mut self) -> Result; + fn get_varint_size(&mut self, value: i32) -> u8; + fn get_varlong_size(&mut self, value: i32) -> u8; + fn read_byte_array(&mut self) -> Result, String>; + fn read_bytes_with_len(&mut self, n: usize) -> Result, String>; + fn read_bytes(&mut self) -> Result, String>; + fn read_utf(&mut self) -> Result; + fn read_utf_with_len(&mut self, max_length: u32) -> Result; + fn read_byte(&mut self) -> Result; + fn read_int(&mut self) -> Result; + fn read_boolean(&mut self) -> Result; + fn read_long(&mut self) -> Result; + fn read_short(&mut self) -> Result; + fn read_float(&mut self) -> Result; + fn read_double(&mut self) -> Result; +} + +impl Readable for R +where + R: Read, +{ + fn read_int_id_list(&mut self) -> Result, String> { + let len = self.read_varint()?; + let mut list = Vec::with_capacity(len as usize); + for _ in 0..len { + list.push(self.read_varint()?); + } + Ok(list) + } + + // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 + /// Read a single varint from the reader and return the value, along with the number of bytes read + fn read_varint(&mut self) -> Result { + let mut buffer = [0]; + let mut ans = 0; + for i in 0..5 { + self.read_exact(&mut buffer) + .map_err(|_| "Invalid VarInt".to_string())?; + ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); + if buffer[0] & 0b1000_0000 == 0 { + return Ok(ans); + } + } + Ok(ans) + } + + fn get_varint_size(&mut self, value: i32) -> u8 { + for i in 1..5 { + if (value & -1 << (i * 7)) != 0 { + continue; + } + return i; + } + 5 + } + + fn get_varlong_size(&mut self, value: i32) -> u8 { + for i in 1..10 { + if (value & -1 << (i * 7)) != 0 { + continue; + } + return i; + } + 10 + } + + fn read_byte_array(&mut self) -> Result, String> { + let length = self.read_varint()? as usize; + self.read_bytes_with_len(length) + } + + fn read_bytes_with_len(&mut self, n: usize) -> Result, String> { + let mut buffer = vec![0; n]; + self.read_exact(&mut buffer) + .map_err(|_| "Error reading bytes".to_string())?; + Ok(buffer) + } + + fn read_bytes(&mut self) -> Result, String> { + // read to end of the buffer + let mut bytes = vec![]; + self.read_to_end(&mut bytes) + .map_err(|_| "Error reading bytes".to_string())?; + Ok(bytes) + } + + fn read_utf(&mut self) -> Result { + self.read_utf_with_len(MAX_STRING_LENGTH.into()) + } + + fn read_utf_with_len(&mut self, max_length: u32) -> Result { + let length = self.read_varint()?; + // i don't know why it's multiplied by 4 but it's like that in mojang's code so + if length < 0 { + return Err( + "The received encoded string buffer length is less than zero! Weird string!" + .to_string(), + ); + } + if length as u32 > max_length * 4 { + return Err(format!( + "The received encoded string buffer length is longer than maximum allowed ({} > {})", + length, + max_length * 4 + )); + } + + // this is probably quite inefficient, idk how to do it better + let mut string = String::new(); + let mut buffer = vec![0; length as usize]; + self.read_exact(&mut buffer) + .map_err(|_| "Invalid UTF-8".to_string())?; + string.push_str(std::str::from_utf8(&buffer).unwrap()); + if string.len() > length as usize { + return Err(format!( + "The received string length is longer than maximum allowed ({} > {})", + length, max_length + )); + } + + Ok(string) + } + + /// Read a single byte from the reader + fn read_byte(&mut self) -> Result { + self.read_u8().map_err(|_| "Error reading byte".to_string()) + } + + fn read_int(&mut self) -> Result { + match self.read_i32::() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading int".to_string()), + } + } + + fn read_boolean(&mut self) -> Result { + match self.read_byte()? { + 0 => Ok(false), + 1 => Ok(true), + _ => Err("Error reading boolean".to_string()), + } + } + + fn read_long(&mut self) -> Result { + match self.read_i64::() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading long".to_string()), + } + } + + fn read_short(&mut self) -> Result { + match self.read_i16::() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading short".to_string()), + } + } + + fn read_float(&mut self) -> Result { + match self.read_f32::() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading float".to_string()), + } + } + + fn read_double(&mut self) -> Result { + match self.read_f64::() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading double".to_string()), + } + } +} + +// fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 +/// Read a single varint from the reader and return the value, along with the number of bytes read +pub async fn read_varint_async(reader: &mut (dyn AsyncRead + Unpin + Send)) -> Result { + let mut buffer = [0]; + let mut ans = 0; + for i in 0..5 { + reader + .read_exact(&mut buffer) + .await + .map_err(|_| "Invalid VarInt".to_string())?; + ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); + if buffer[0] & 0b1000_0000 == 0 { + return Ok(ans); + } + } + Ok(ans) +} + +pub trait McBufReadable +where + Self: Sized, +{ + fn read_into(buf: &mut impl Read) -> Result; +} + +pub trait McBufVarReadable +where + Self: Sized, +{ + fn var_read_into(buf: &mut impl Read) -> Result; +} + +impl McBufReadable for i32 { + fn read_into(buf: &mut impl Read) -> Result { + Readable::read_int(buf) + } +} + +impl McBufVarReadable for i32 { + fn var_read_into(buf: &mut impl Read) -> Result { + buf.read_varint() + } +} + +impl McBufVarReadable for i64 { + // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L54 + fn var_read_into(buf: &mut impl Read) -> Result { + let mut buffer = [0]; + let mut ans = 0; + for i in 0..8 { + buf.read_exact(&mut buffer) + .map_err(|_| "Invalid VarLong".to_string())?; + ans |= ((buffer[0] & 0b0111_1111) as i64) << 7 * i; + if buffer[0] & 0b1000_0000 == 0 { + break; + } + } + Ok(ans) + } +} +impl McBufVarReadable for u64 { + fn var_read_into(buf: &mut impl Read) -> Result { + i64::var_read_into(buf).map(|i| i as u64) + } +} + +impl McBufReadable for UnsizedByteArray { + fn read_into(buf: &mut impl Read) -> Result { + Ok(buf.read_bytes()?.into()) + } +} + +impl McBufReadable for Vec { + default fn read_into(buf: &mut impl Read) -> Result { + let length = buf.read_varint()? as usize; + let mut contents = Vec::with_capacity(length); + for _ in 0..length { + contents.push(T::read_into(buf)?); + } + Ok(contents) + } +} + +impl McBufReadable for HashMap { + default fn read_into(buf: &mut impl Read) -> Result { + let length = buf.read_varint()? as usize; + let mut contents = HashMap::with_capacity(length); + for _ in 0..length { + contents.insert(K::read_into(buf)?, V::read_into(buf)?); + } + Ok(contents) + } +} + +impl McBufReadable for Vec { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_byte_array() + } +} + +impl McBufReadable for String { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_utf() + } +} + +impl McBufReadable for u32 { + fn read_into(buf: &mut impl Read) -> Result { + Readable::read_int(buf).map(|i| i as u32) + } +} + +impl McBufVarReadable for u32 { + fn var_read_into(buf: &mut impl Read) -> Result { + buf.read_varint().map(|i| i as u32) + } +} + +impl McBufReadable for u16 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_short().map(|i| i as u16) + } +} + +impl McBufReadable for i16 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_short() + } +} + +impl McBufVarReadable for u16 { + fn var_read_into(buf: &mut impl Read) -> Result { + buf.read_varint().map(|i| i as u16) + } +} + +impl McBufVarReadable for Vec { + fn var_read_into(buf: &mut impl Read) -> Result { + let length = buf.read_varint()? as usize; + let mut contents = Vec::with_capacity(length); + for _ in 0..length { + contents.push(T::var_read_into(buf)?); + } + Ok(contents) + } +} + +impl McBufReadable for i64 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_long() + } +} + +impl McBufReadable for u64 { + fn read_into(buf: &mut impl Read) -> Result { + i64::read_into(buf).map(|i| i as u64) + } +} + +impl McBufReadable for bool { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_boolean() + } +} + +impl McBufReadable for u8 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_byte() + } +} + +impl McBufReadable for i8 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_byte().map(|i| i as i8) + } +} + +impl McBufReadable for f32 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_float() + } +} + +impl McBufReadable for f64 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_double() + } +} + +impl McBufReadable for Option { + default fn read_into(buf: &mut impl Read) -> Result { + let present = buf.read_boolean()?; + Ok(if present { + Some(T::read_into(buf)?) + } else { + None + }) + } +} diff --git a/azalea-buf/src/write.rs b/azalea-buf/src/write.rs new file mode 100644 index 00000000..fdf58203 --- /dev/null +++ b/azalea-buf/src/write.rs @@ -0,0 +1,284 @@ +use super::{UnsizedByteArray, MAX_STRING_LENGTH}; +use byteorder::{BigEndian, WriteBytesExt}; +use std::{collections::HashMap, io::Write}; + +// TODO: get rid of Writable and use McBufWritable everywhere + +pub trait Writable: Write { + fn write_list(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error> + where + F: FnOnce(&mut Self, &T) -> Result<(), std::io::Error> + Copy, + { + self.write_varint(list.len() as i32)?; + for item in list { + writer(self, item)?; + } + Ok(()) + } + + fn write_int_id_list(&mut self, list: &[i32]) -> Result<(), std::io::Error> { + self.write_list(list, |buf, n| buf.write_varint(*n)) + } + + fn write_map( + &mut self, + map: Vec<(KT, VT)>, + key_writer: KF, + value_writer: VF, + ) -> Result<(), std::io::Error> + where + KF: Fn(&mut Self, KT) -> Result<(), std::io::Error> + Copy, + VF: Fn(&mut Self, VT) -> Result<(), std::io::Error> + Copy, + { + self.write_varint(map.len() as i32)?; + for (key, value) in map { + key_writer(self, key)?; + value_writer(self, value)?; + } + Ok(()) + } + + fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error> { + WriteBytesExt::write_u8(self, n) + } + + fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { + self.write_all(bytes)?; + Ok(()) + } + + fn write_varint(&mut self, mut value: i32) -> Result<(), std::io::Error> { + let mut buffer = [0]; + if value == 0 { + self.write_all(&buffer).unwrap(); + } + while value != 0 { + buffer[0] = (value & 0b0111_1111) as u8; + value = (value >> 7) & (i32::max_value() >> 6); + if value != 0 { + buffer[0] |= 0b1000_0000; + } + self.write_all(&buffer)?; + } + Ok(()) + } + + fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error> { + if string.len() > len { + panic!( + "String too big (was {} bytes encoded, max {})", + string.len(), + len + ); + } + self.write_varint(string.len() as i32)?; + self.write_bytes(string.as_bytes()) + } + + fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error> { + self.write_utf_with_len(string, MAX_STRING_LENGTH.into()) + } + + fn write_short(&mut self, n: i16) -> Result<(), std::io::Error> { + WriteBytesExt::write_i16::(self, n) + } + + fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { + self.write_varint(bytes.len() as i32)?; + self.write_bytes(bytes) + } + + fn write_int(&mut self, n: i32) -> Result<(), std::io::Error> { + WriteBytesExt::write_i32::(self, n) + } + + fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error> { + self.write_byte(if b { 1 } else { 0 }) + } + + fn write_long(&mut self, n: i64) -> Result<(), std::io::Error> { + WriteBytesExt::write_i64::(self, n) + } + + fn write_float(&mut self, n: f32) -> Result<(), std::io::Error> { + WriteBytesExt::write_f32::(self, n) + } + + fn write_double(&mut self, n: f64) -> Result<(), std::io::Error> { + WriteBytesExt::write_f64::(self, n) + } +} + +impl Writable for W {} + +pub trait McBufWritable { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>; +} + +pub trait McBufVarWritable { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>; +} + +impl McBufWritable for i32 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + Writable::write_int(buf, *self) + } +} + +impl McBufVarWritable for i32 { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_varint(*self) + } +} + +impl McBufWritable for UnsizedByteArray { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_bytes(self) + } +} + +impl McBufWritable for Vec { + default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_list(self, |buf, i| T::write_into(i, buf)) + } +} + +impl McBufWritable for HashMap { + default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + u32::var_write_into(&(self.len() as u32), buf)?; + for (key, value) in self { + key.write_into(buf)?; + value.write_into(buf)?; + } + + Ok(()) + } +} + +impl McBufWritable for Vec { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_byte_array(self) + } +} + +impl McBufWritable for String { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_utf(self) + } +} + +impl McBufWritable for u32 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i16::write_into(&(*self as i16), buf) + } +} + +impl McBufVarWritable for u32 { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i32::var_write_into(&(*self as i32), buf) + } +} + +impl McBufVarWritable for i64 { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + let mut buffer = [0]; + let mut value = *self; + while value != 0 { + buffer[0] = (value & 0b0111_1111) as u8; + value = (value >> 7) & (i64::max_value() >> 6); + if value != 0 { + buffer[0] |= 0b1000_0000; + } + buf.write(&mut buffer)?; + } + Ok(()) + } +} + +impl McBufVarWritable for u64 { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i64::var_write_into(&(*self as i64), buf) + } +} + +impl McBufWritable for u16 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i16::write_into(&(*self as i16), buf) + } +} + +impl McBufVarWritable for u16 { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i32::var_write_into(&(*self as i32), buf) + } +} + +impl McBufVarWritable for Vec { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + u32::var_write_into(&(self.len() as u32), buf)?; + for i in self { + i.var_write_into(buf)?; + } + Ok(()) + } +} + +impl McBufWritable for u8 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_byte(*self) + } +} + +impl McBufWritable for i16 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + Writable::write_short(buf, *self) + } +} + +impl McBufWritable for i64 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + Writable::write_long(buf, *self) + } +} + +impl McBufWritable for u64 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i64::write_into(&(*self as i64), buf) + } +} + +impl McBufWritable for bool { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_boolean(*self) + } +} + +impl McBufWritable for i8 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_byte(*self as u8) + } +} + +impl McBufWritable for f32 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_float(*self) + } +} + +impl McBufWritable for f64 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_double(*self) + } +} + +impl McBufWritable for Option { + default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + if let Some(s) = self { + buf.write_boolean(true)?; + s.write_into(buf)?; + } else { + buf.write_boolean(false)?; + }; + Ok(()) + } +} diff --git a/azalea-chat/Cargo.toml b/azalea-chat/Cargo.toml index 192d3405..5b85f6c2 100755 --- a/azalea-chat/Cargo.toml +++ b/azalea-chat/Cargo.toml @@ -6,6 +6,7 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +azalea-buf = {path = "../azalea-buf"} azalea-language = {path = "../azalea-language"} lazy_static = "1.4.0" serde = "^1.0.130" diff --git a/azalea-chat/src/component.rs b/azalea-chat/src/component.rs index 36709cc0..872a0a5c 100755 --- a/azalea-chat/src/component.rs +++ b/azalea-chat/src/component.rs @@ -264,3 +264,30 @@ impl<'de> Deserialize<'de> for Component { Ok(component) } } + +impl McBufReadable for Component { + fn read_into(buf: &mut impl Read) -> Result { + let string = buf.read_utf()?; + let json: serde_json::Value = serde_json::from_str(string.as_str()) + .map_err(|_| "Component isn't valid JSON".to_string())?; + let component = Component::deserialize(json).map_err(|e| e.to_string())?; + Ok(component) + } +} + +impl McBufWritable for Component { + // async fn read_into(buf: &mut impl Read) -> Result + // where + // R: AsyncRead + std::marker::Unpin + std::marker::Send, + // { + // let string = buf.read_utf().await?; + // let json: serde_json::Value = serde_json::from_str(string.as_str()) + // .map_err(|e| "Component isn't valid JSON".to_string())?; + // let component = Component::deserialize(json).map_err(|e| e.to_string())?; + // Ok(component) + // } + fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> { + // component doesn't have serialize implemented yet + todo!() + } +} diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 828578de..a5259db9 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -1,5 +1,5 @@ use crate::{Account, Player}; -use azalea_core::{resource_location::ResourceLocation, ChunkPos}; +use azalea_core::{resource_location::ResourceLocation, ChunkPos, EntityPos}; use azalea_entity::Entity; use azalea_protocol::{ connect::{GameConnection, HandshakeConnection}, @@ -387,6 +387,13 @@ impl Client { } GamePacket::ClientboundAddPlayerPacket(p) => { println!("Got add player packet {:?}", p); + let entity = Entity::from(p); + state + .lock()? + .world + .as_mut() + .expect("World doesn't exist! We should've gotten a login packet by now.") + .add_entity(entity); } GamePacket::ClientboundInitializeBorderPacket(p) => { println!("Got initialize border packet {:?}", p); @@ -406,20 +413,18 @@ impl Client { GamePacket::ClientboundSetExperiencePacket(p) => { println!("Got set experience packet {:?}", p); } - GamePacket::ClientboundTeleportEntityPacket(_p) => { - // println!("Got teleport entity packet {:?}", p); - // let state_lock = state.lock()?; - - // let entity = state_lock - // .world - // .unwrap() - // .entity_by_id(p.id) - // .ok_or("Teleporting entity that doesn't exist.".to_string())?; - // state_lock - // .world - // .as_mut() - // .expect("World doesn't exist! We should've gotten a login packet by now.") - // .move_entity(&mut entity, new_pos) + GamePacket::ClientboundTeleportEntityPacket(p) => { + let mut state_lock = state.lock()?; + let world = state_lock.world.as_mut().unwrap(); + + world.move_entity( + p.id, + EntityPos { + x: p.x, + y: p.y, + z: p.z, + }, + )?; } GamePacket::ClientboundUpdateAdvancementsPacket(p) => { println!("Got update advancements packet {:?}", p); @@ -427,11 +432,21 @@ impl Client { GamePacket::ClientboundRotateHeadPacket(_p) => { // println!("Got rotate head packet {:?}", p); } - GamePacket::ClientboundMoveEntityPosPacket(_p) => { + GamePacket::ClientboundMoveEntityPosPacket(p) => { // println!("Got move entity pos packet {:?}", p); } - GamePacket::ClientboundMoveEntityPosRotPacket(_p) => { - // println!("Got move entity pos rot packet {:?}", p); + GamePacket::ClientboundMoveEntityPosRotPacket(p) => { + let mut state_lock = state.lock()?; + let world = state_lock.world.as_mut().unwrap(); + + world.move_entity( + p.entity_id, + EntityPos { + x: p.x, + y: p.y, + z: p.z, + }, + )?; } GamePacket::ClientboundMoveEntityRotPacket(p) => { println!("Got move entity rot packet {:?}", p); diff --git a/azalea-core/Cargo.toml b/azalea-core/Cargo.toml index 27112b21..470bc998 100755 --- a/azalea-core/Cargo.toml +++ b/azalea-core/Cargo.toml @@ -6,6 +6,7 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +azalea-buf = {path = "../azalea-buf"} azalea-chat = {path = "../azalea-chat"} azalea-nbt = {path = "../azalea-nbt"} uuid = "^1.1.2" diff --git a/azalea-core/src/delta.rs b/azalea-core/src/delta.rs new file mode 100644 index 00000000..32517e0d --- /dev/null +++ b/azalea-core/src/delta.rs @@ -0,0 +1,26 @@ +/// Only works for up to 8 blocks +#[derive(Clone, Debug, McBuf)] +pub struct PositionDelta { + xa: i16, + ya: i16, + za: i16, +} + +impl PositionDelta { + pub fn float(&self) -> (f64, f64, f64) { + ( + (self.xa as f64) / 4096.0, + (self.ya as f64) / 4096.0, + (self.za as f64) / 4096.0, + ) + } +} + +impl EntityPos { + pub fn apply_delta(&mut self, delta: &PositionDelta) { + let (x, y, z) = delta.float(); + self.x += x; + self.y += y; + self.z += z; + } +} diff --git a/azalea-core/src/difficulty.rs b/azalea-core/src/difficulty.rs index 5d869325..7568f8e2 100755 --- a/azalea-core/src/difficulty.rs +++ b/azalea-core/src/difficulty.rs @@ -61,6 +61,18 @@ impl Difficulty { } } +impl McBufReadable for Difficulty { + fn read_into(buf: &mut impl Read) -> Result { + Ok(Difficulty::by_id(u8::read_into(buf)?)) + } +} + +impl McBufWritable for Difficulty { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + u8::write_into(&self.id(), buf) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-core/src/direction.rs b/azalea-core/src/direction.rs index 721f21a0..bb655bdb 100644 --- a/azalea-core/src/direction.rs +++ b/azalea-core/src/direction.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, McBuf)] pub enum Direction { Down = 0, Up = 1, diff --git a/azalea-core/src/game_type.rs b/azalea-core/src/game_type.rs index f5b9fb38..963048dd 100755 --- a/azalea-core/src/game_type.rs +++ b/azalea-core/src/game_type.rs @@ -71,3 +71,27 @@ impl GameType { } } } + +impl McBufReadable for GameType { + fn read_into(buf: &mut impl Read) -> Result { + GameType::from_id(buf.read_byte()?) + } +} + +impl McBufReadable for Option { + fn read_into(buf: &mut impl Read) -> Result { + GameType::from_optional_id(buf.read_byte()? as i8) + } +} + +impl McBufWritable for GameType { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + u8::write_into(&self.to_id(), buf) + } +} + +impl McBufWritable for Option { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_byte(GameType::to_optional_id(self) as u8) + } +} diff --git a/azalea-core/src/lib.rs b/azalea-core/src/lib.rs index a2632871..41c901c8 100755 --- a/azalea-core/src/lib.rs +++ b/azalea-core/src/lib.rs @@ -15,3 +15,9 @@ pub use position::*; mod direction; pub use direction::Direction; + +mod delta; +pub use delta::*; + +mod particle; +pub use particle::*; diff --git a/azalea-core/src/particle/mod.rs b/azalea-core/src/particle/mod.rs new file mode 100644 index 00000000..fc815a0b --- /dev/null +++ b/azalea-core/src/particle/mod.rs @@ -0,0 +1,259 @@ + +#[derive(Debug, Clone, McBuf)] +pub struct Particle { + #[var] + pub id: i32, + pub data: ParticleData, +} + +#[derive(Clone, Debug)] +pub enum ParticleData { + AmbientEntityEffect, + AngryVillager, + Block(BlockParticle), + BlockMarker(BlockParticle), + Bubble, + Cloud, + Crit, + DamageIndicator, + DragonBreath, + DrippingLava, + FallingLava, + LandingLava, + DrippingWater, + FallingWater, + Dust(DustParticle), + DustColorTransition(DustColorTransitionParticle), + Effect, + ElderGuardian, + EnchantedHit, + Enchant, + EndRod, + EntityEffect, + ExplosionEmitter, + Explosion, + FallingDust(BlockParticle), + Firework, + Fishing, + Flame, + SoulFireFlame, + Soul, + Flash, + HappyVillager, + Composter, + Heart, + InstantEffect, + Item(ItemParticle), + Vibration(VibrationParticle), + ItemSlime, + ItemSnowball, + LargeSmoke, + Lava, + Mycelium, + Note, + Poof, + Portal, + Rain, + Smoke, + Sneeze, + Spit, + SquidInk, + SweepAttack, + TotemOfUndying, + Underwater, + Splash, + Witch, + BubblePop, + CurrentDown, + BubbleColumnUp, + Nautilus, + Dolphin, + CampfireCozySmoke, + CampfireSignalSmoke, + DrippingHoney, + FallingHoney, + LandingHoney, + FallingNectar, + FallingSporeBlossom, + Ash, + CrimsonSpore, + WarpedSpore, + SporeBlossomAir, + DrippingObsidianTear, + FallingObsidianTear, + LandingObsidianTear, + ReversePortal, + WhiteAsh, + SmallFlame, + Snowflake, + DrippingDripstoneLava, + FallingDripstoneLava, + DrippingDripstoneWater, + FallingDripstoneWater, + GlowSquidInk, + Glow, + WaxOn, + WaxOff, + ElectricSpark, + Scrape, +} + +#[derive(Debug, Clone, McBuf)] +pub struct BlockParticle { + #[var] + pub block_state: i32, +} +#[derive(Debug, Clone, McBuf)] +pub struct DustParticle { + /// Red value, 0-1 + pub red: f32, + /// Green value, 0-1 + pub green: f32, + /// Blue value, 0-1 + pub blue: f32, + /// The scale, will be clamped between 0.01 and 4. + pub scale: f32, +} + +#[derive(Debug, Clone, McBuf)] +pub struct DustColorTransitionParticle { + /// Red value, 0-1 + pub from_red: f32, + /// Green value, 0-1 + pub from_green: f32, + /// Blue value, 0-1 + pub from_blue: f32, + /// The scale, will be clamped between 0.01 and 4. + pub scale: f32, + /// Red value, 0-1 + pub to_red: f32, + /// Green value, 0-1 + pub to_green: f32, + /// Blue value, 0-1 + pub to_blue: f32, +} + +#[derive(Debug, Clone, McBuf)] +pub struct ItemParticle { + pub item: Slot, +} + +#[derive(Debug, Clone, McBuf)] +pub struct VibrationParticle { + pub origin: BlockPos, + pub position_type: String, + pub block_position: BlockPos, + #[var] + pub entity_id: u32, + #[var] + pub ticks: u32, +} + +impl ParticleData { + pub fn read_from_particle_id(buf: &mut impl Read, id: u32) -> Result { + Ok(match id { + 0 => ParticleData::AmbientEntityEffect, + 1 => ParticleData::AngryVillager, + 2 => ParticleData::Block(BlockParticle::read_into(buf)?), + 3 => ParticleData::BlockMarker(BlockParticle::read_into(buf)?), + 4 => ParticleData::Bubble, + 5 => ParticleData::Cloud, + 6 => ParticleData::Crit, + 7 => ParticleData::DamageIndicator, + 8 => ParticleData::DragonBreath, + 9 => ParticleData::DrippingLava, + 10 => ParticleData::FallingLava, + 11 => ParticleData::LandingLava, + 12 => ParticleData::DrippingWater, + 13 => ParticleData::FallingWater, + 14 => ParticleData::Dust(DustParticle::read_into(buf)?), + 15 => ParticleData::DustColorTransition(DustColorTransitionParticle::read_into(buf)?), + 16 => ParticleData::Effect, + 17 => ParticleData::ElderGuardian, + 18 => ParticleData::EnchantedHit, + 19 => ParticleData::Enchant, + 20 => ParticleData::EndRod, + 21 => ParticleData::EntityEffect, + 22 => ParticleData::ExplosionEmitter, + 23 => ParticleData::Explosion, + 24 => ParticleData::FallingDust(BlockParticle::read_into(buf)?), + 25 => ParticleData::Firework, + 26 => ParticleData::Fishing, + 27 => ParticleData::Flame, + 28 => ParticleData::SoulFireFlame, + 29 => ParticleData::Soul, + 30 => ParticleData::Flash, + 31 => ParticleData::HappyVillager, + 32 => ParticleData::Composter, + 33 => ParticleData::Heart, + 34 => ParticleData::InstantEffect, + 35 => ParticleData::Item(ItemParticle::read_into(buf)?), + 36 => ParticleData::Vibration(VibrationParticle::read_into(buf)?), + 37 => ParticleData::ItemSlime, + 38 => ParticleData::ItemSnowball, + 39 => ParticleData::LargeSmoke, + 40 => ParticleData::Lava, + 41 => ParticleData::Mycelium, + 42 => ParticleData::Note, + 43 => ParticleData::Poof, + 44 => ParticleData::Portal, + 45 => ParticleData::Rain, + 46 => ParticleData::Smoke, + 47 => ParticleData::Sneeze, + 48 => ParticleData::Spit, + 49 => ParticleData::SquidInk, + 50 => ParticleData::SweepAttack, + 51 => ParticleData::TotemOfUndying, + 52 => ParticleData::Underwater, + 53 => ParticleData::Splash, + 54 => ParticleData::Witch, + 55 => ParticleData::BubblePop, + 56 => ParticleData::CurrentDown, + 57 => ParticleData::BubbleColumnUp, + 58 => ParticleData::Nautilus, + 59 => ParticleData::Dolphin, + 60 => ParticleData::CampfireCozySmoke, + 61 => ParticleData::CampfireSignalSmoke, + 62 => ParticleData::DrippingHoney, + 63 => ParticleData::FallingHoney, + 64 => ParticleData::LandingHoney, + 65 => ParticleData::FallingNectar, + 66 => ParticleData::FallingSporeBlossom, + 67 => ParticleData::Ash, + 68 => ParticleData::CrimsonSpore, + 69 => ParticleData::WarpedSpore, + 70 => ParticleData::SporeBlossomAir, + 71 => ParticleData::DrippingObsidianTear, + 72 => ParticleData::FallingObsidianTear, + 73 => ParticleData::LandingObsidianTear, + 74 => ParticleData::ReversePortal, + 75 => ParticleData::WhiteAsh, + 76 => ParticleData::SmallFlame, + 77 => ParticleData::Snowflake, + 78 => ParticleData::DrippingDripstoneLava, + 79 => ParticleData::FallingDripstoneLava, + 80 => ParticleData::DrippingDripstoneWater, + 81 => ParticleData::FallingDripstoneWater, + 82 => ParticleData::GlowSquidInk, + 83 => ParticleData::Glow, + 84 => ParticleData::WaxOn, + 85 => ParticleData::WaxOff, + 86 => ParticleData::ElectricSpark, + 87 => ParticleData::Scrape, + _ => return Err(format!("Unknown particle id: {}", id)), + }) + } +} + +impl McBufReadable for ParticleData { + fn read_into(buf: &mut impl Read) -> Result { + let id = u32::var_read_into(buf)?; + ParticleData::read_from_particle_id(buf, id) + } +} + +impl McBufWritable for ParticleData { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + todo!() + } +} diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index 7b2c01da..9e7aca20 100644 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -170,6 +170,65 @@ impl From<&EntityPos> for ChunkPos { } } +impl McBufReadable for BlockPos { + fn read_into(buf: &mut impl Read) -> Result { + let val = u64::read_into(buf)?; + let x = (val >> 38) as i32; + let y = (val & 0xFFF) as i32; + let z = ((val >> 12) & 0x3FFFFFF) as i32; + Ok(BlockPos { x, y, z }) + } +} + +impl McBufReadable for GlobalPos { + fn read_into(buf: &mut impl Read) -> Result { + Ok(GlobalPos { + dimension: ResourceLocation::read_into(buf)?, + pos: BlockPos::read_into(buf)?, + }) + } +} + +impl McBufReadable for ChunkSectionPos { + fn read_into(buf: &mut impl Read) -> Result { + let long = i64::read_into(buf)?; + Ok(ChunkSectionPos { + x: (long >> 42) as i32, + y: (long << 44 >> 44) as i32, + z: (long << 22 >> 42) as i32, + }) + } +} + +impl McBufWritable for BlockPos { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_long( + (((self.x & 0x3FFFFFF) as i64) << 38) + | (((self.z & 0x3FFFFFF) as i64) << 12) + | ((self.y & 0xFFF) as i64), + ) + } +} + +impl McBufWritable for GlobalPos { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + ResourceLocation::write_into(&self.dimension, buf)?; + BlockPos::write_into(&self.pos, buf)?; + + Ok(()) + } +} + +impl McBufWritable for ChunkSectionPos { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + let long = (((self.x & 0x3FFFFF) as i64) << 42) + | (self.y & 0xFFFFF) as i64 + | (((self.z & 0x3FFFFF) as i64) << 20); + long.write_into(buf)?; + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs index cdf8f381..6807714b 100755 --- a/azalea-core/src/resource_location.rs +++ b/azalea-core/src/resource_location.rs @@ -42,6 +42,18 @@ impl std::fmt::Debug for ResourceLocation { } } +impl McBufReadable for ResourceLocation { + fn read_into(buf: &mut impl Read) -> Result { + let location_string = self.read_utf()?; + ResourceLocation::new(&location_string) + } +} +impl McBufWritable for ResourceLocation { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_utf(&self.to_string()) + } +} + #[cfg(test)] mod tests { use super::*; @@ -70,4 +82,18 @@ mod tests { assert_eq!(r.namespace, "azalea"); assert_eq!(r.path, ""); } + + #[test] + fn mcbuf_resource_location() { + let mut buf = Vec::new(); + buf.write_resource_location(&ResourceLocation::new("minecraft:dirt").unwrap()) + .unwrap(); + + let mut buf = Cursor::new(buf); + + assert_eq!( + buf.read_resource_location().unwrap(), + ResourceLocation::new("minecraft:dirt").unwrap() + ); + } } diff --git a/azalea-core/src/serializable_uuid.rs b/azalea-core/src/serializable_uuid.rs index f8c03b60..2c7128ff 100755 --- a/azalea-core/src/serializable_uuid.rs +++ b/azalea-core/src/serializable_uuid.rs @@ -30,6 +30,28 @@ impl SerializableUuid for Uuid { } } +impl McBufReadable for Uuid { + fn read_into(buf: &mut impl Read) -> Result { + Ok(Uuid::from_int_array([ + Readable::read_int(self)? as u32, + Readable::read_int(self)? as u32, + Readable::read_int(self)? as u32, + Readable::read_int(self)? as u32, + ])) + } +} + +impl McBufWritable for Uuid { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + let [a, b, c, d] = self.to_int_array(); + a.write_into(buf)?; + b.write_into(buf)?; + c.write_into(buf)?; + d.write_into(buf)?; + Ok(()) + } +} + #[cfg(tests)] mod tests { use super::*; diff --git a/azalea-core/src/slot.rs b/azalea-core/src/slot.rs index 5e42f558..e3b78289 100644 --- a/azalea-core/src/slot.rs +++ b/azalea-core/src/slot.rs @@ -12,3 +12,31 @@ pub struct SlotData { pub count: u8, pub nbt: azalea_nbt::Tag, } + +impl McBufReadable for Slot { + fn read_into(buf: &mut impl Read) -> Result { + let present = buf.read_boolean()?; + if !present { + return Ok(Slot::Empty); + } + let id = buf.read_varint()?; + let count = buf.read_byte()?; + let nbt = buf.read_nbt()?; + Ok(Slot::Present(SlotData { id, count, nbt })) + } +} + +impl McBufWritable for Slot { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + match self { + Slot::Empty => buf.write_byte(0)?, + Slot::Present(i) => { + buf.write_varint(i.id)?; + buf.write_byte(i.count)?; + buf.write_nbt(&i.nbt)?; + } + } + + Ok(()) + } +} diff --git a/azalea-crypto/Cargo.toml b/azalea-crypto/Cargo.toml index 2532bff9..ee652565 100644 --- a/azalea-crypto/Cargo.toml +++ b/azalea-crypto/Cargo.toml @@ -7,6 +7,7 @@ version = "0.1.0" [dependencies] aes = "0.8.1" +azalea-buf = {path = "../azalea-buf"} cfb8 = "0.8.1" num-bigint = "^0.4.3" rand = {version = "^0.8.4", features = ["getrandom"]} diff --git a/azalea-crypto/src/lib.rs b/azalea-crypto/src/lib.rs index a5e797e8..85705883 100644 --- a/azalea-crypto/src/lib.rs +++ b/azalea-crypto/src/lib.rs @@ -79,6 +79,8 @@ pub fn decrypt_packet(cipher: &mut Aes128CfbDec, packet: &mut [u8]) { cipher.decrypt_blocks_inout_mut(chunks); } + + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-crypto/src/signing.rs b/azalea-crypto/src/signing.rs index 21cd813a..a5280a18 100644 --- a/azalea-crypto/src/signing.rs +++ b/azalea-crypto/src/signing.rs @@ -3,3 +3,19 @@ pub struct SaltSignaturePair { pub salt: u64, pub signature: Vec, } + +impl McBufReadable for SaltSignaturePair { + fn read_into(buf: &mut impl Read) -> Result { + let salt = u64::read_into(buf)?; + let signature = Vec::::read_into(buf)?; + Ok(SaltSignaturePair { salt, signature }) + } +} + +impl McBufWritable for SaltSignaturePair { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + self.salt.write_into(buf)?; + self.signature.write_into(buf)?; + Ok(()) + } +} diff --git a/azalea-entity/src/data.rs b/azalea-entity/src/data.rs new file mode 100644 index 00000000..ddc6f57b --- /dev/null +++ b/azalea-entity/src/data.rs @@ -0,0 +1,136 @@ +pub type EntityMetadata = Vec; + +#[derive(Clone, Debug)] +pub struct EntityDataItem { + // we can't identify what the index is for here because we don't know the + // entity type + pub index: u8, + pub value: EntityDataValue, +} + +impl McBufReadable for Vec { + fn read_into(buf: &mut impl Read) -> Result { + let mut metadata = Vec::new(); + loop { + let index = u8::read_into(buf)?; + if index == 0xff { + break; + } + let value = EntityDataValue::read_into(buf)?; + metadata.push(EntityDataItem { index, value }); + } + Ok(metadata) + } +} + +impl McBufWritable for Vec { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + for item in self { + buf.write_byte(item.index)?; + item.value.write_into(buf)?; + } + buf.write_byte(0xff)?; + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub enum EntityDataValue { + Byte(u8), + // varint + Int(i32), + Float(f32), + String(String), + Component(Component), + OptionalComponent(Option), + ItemStack(Slot), + Boolean(bool), + Rotations { x: f32, y: f32, z: f32 }, + BlockPos(BlockPos), + OptionalBlockPos(Option), + Direction(Direction), + OptionalUuid(Option), + // 0 for absent (implies air); otherwise, a block state ID as per the global palette + // this is a varint + OptionalBlockState(Option), + CompoundTag(azalea_nbt::Tag), + Particle(Particle), + VillagerData(VillagerData), + // 0 for absent; 1 + actual value otherwise. Used for entity IDs. + OptionalUnsignedInt(Option), + Pose(Pose), +} + +impl McBufReadable for EntityDataValue { + fn read_into(buf: &mut impl Read) -> Result { + let type_ = buf.read_varint()?; + Ok(match type_ { + 0 => EntityDataValue::Byte(buf.read_byte()?), + 1 => EntityDataValue::Int(buf.read_varint()?), + 2 => EntityDataValue::Float(buf.read_float()?), + 3 => EntityDataValue::String(buf.read_utf()?), + 4 => EntityDataValue::Component(Component::read_into(buf)?), + 5 => EntityDataValue::OptionalComponent(Option::::read_into(buf)?), + 6 => EntityDataValue::ItemStack(Slot::read_into(buf)?), + 7 => EntityDataValue::Boolean(buf.read_boolean()?), + 8 => EntityDataValue::Rotations { + x: buf.read_float()?, + y: buf.read_float()?, + z: buf.read_float()?, + }, + 9 => EntityDataValue::BlockPos(BlockPos::read_into(buf)?), + 10 => EntityDataValue::OptionalBlockPos(Option::::read_into(buf)?), + 11 => EntityDataValue::Direction(Direction::read_into(buf)?), + 12 => EntityDataValue::OptionalUuid(Option::::read_into(buf)?), + 13 => EntityDataValue::OptionalBlockState({ + let val = i32::read_into(buf)?; + if val == 0 { + None + } else { + Some(val) + } + }), + 14 => EntityDataValue::CompoundTag(azalea_nbt::Tag::read_into(buf)?), + 15 => EntityDataValue::Particle(Particle::read_into(buf)?), + 16 => EntityDataValue::VillagerData(VillagerData::read_into(buf)?), + 17 => EntityDataValue::OptionalUnsignedInt({ + let val = buf.read_varint()?; + if val == 0 { + None + } else { + Some((val - 1) as u32) + } + }), + 18 => EntityDataValue::Pose(Pose::read_into(buf)?), + _ => return Err(format!("Unknown entity data type: {}", type_)), + }) + } +} + +impl McBufWritable for EntityDataValue { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + todo!(); + } +} + +#[derive(Clone, Debug, Copy, McBuf)] +pub enum Pose { + Standing = 0, + FallFlying = 1, + Sleeping = 2, + Swimming = 3, + SpinAttack = 4, + Sneaking = 5, + LongJumping = 6, + Dying = 7, +} + +#[derive(Debug, Clone, McBuf)] +pub struct VillagerData { + #[var] + type_: u32, + #[var] + profession: u32, + #[var] + level: u32, +} diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index d32a1a96..2283142f 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -1,6 +1,11 @@ +mod data; + use azalea_core::EntityPos; #[cfg(feature = "protocol")] -use azalea_protocol::packets::game::clientbound_add_entity_packet::ClientboundAddEntityPacket; +use azalea_protocol::packets::game::{ + clientbound_add_entity_packet::ClientboundAddEntityPacket, + clientbound_add_player_packet::ClientboundAddPlayerPacket, +}; use uuid::Uuid; #[derive(Default, Debug)] @@ -38,6 +43,21 @@ impl From<&ClientboundAddEntityPacket> for Entity { } } +#[cfg(feature = "protocol")] +impl From<&ClientboundAddPlayerPacket> for Entity { + fn from(p: &ClientboundAddPlayerPacket) -> Self { + Self { + id: p.id, + uuid: p.uuid, + pos: EntityPos { + x: p.x, + y: p.y, + z: p.z, + }, + } + } +} + // #[cfg(test)] // mod tests { // #[test] diff --git a/azalea-nbt/Cargo.toml b/azalea-nbt/Cargo.toml index ad466e1f..992d242a 100755 --- a/azalea-nbt/Cargo.toml +++ b/azalea-nbt/Cargo.toml @@ -6,6 +6,7 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +azalea-buf = {path = "../azalea-buf"} byteorder = "1.4.3" flate2 = "1.0.23" num-derive = "^0.3.3" diff --git a/azalea-nbt/src/decode.rs b/azalea-nbt/src/decode.rs index 7f2ca754..73cd613e 100755 --- a/azalea-nbt/src/decode.rs +++ b/azalea-nbt/src/decode.rs @@ -136,3 +136,13 @@ impl Tag { Tag::read(&mut gz) } } + +impl McBufReadable for Tag { + fn read_into(buf: &mut impl Read) -> Result { + match Tag::read(self) { + Ok(r) => Ok(r), + // Err(e) => Err(e.to_string()), + Err(e) => Err(e.to_string()).unwrap(), + } + } +} diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index fb5585b3..17d20270 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -217,3 +217,10 @@ impl Tag { self.write(&mut encoder) } } + +impl McBufWritable for Tag { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + self.write(buf) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) + } +} diff --git a/azalea-nbt/src/lib.rs b/azalea-nbt/src/lib.rs index d14fd929..8cca1f2b 100755 --- a/azalea-nbt/src/lib.rs +++ b/azalea-nbt/src/lib.rs @@ -5,3 +5,36 @@ mod tag; pub use error::Error; pub use tag::Tag; + +#[cfg(test)] +mod tests { + use super::*; + use std::{collections::HashMap, io::Cursor}; + + #[test] + fn mcbuf_nbt() { + let mut buf = Vec::new(); + buf.write_nbt(&Tag::Compound(HashMap::from_iter(vec![( + "hello world".to_string(), + Tag::Compound(HashMap::from_iter(vec![( + "name".to_string(), + Tag::String("Bananrama".to_string()), + )])), + )]))) + .unwrap(); + + let mut buf = Cursor::new(buf); + + let result = buf.read_nbt().unwrap(); + assert_eq!( + result, + Tag::Compound(HashMap::from_iter(vec![( + "hello world".to_string(), + Tag::Compound(HashMap::from_iter(vec![( + "name".to_string(), + Tag::String("Bananrama".to_string()), + )])), + )])) + ); + } +} diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml index f1640a01..dadb212c 100755 --- a/azalea-protocol/Cargo.toml +++ b/azalea-protocol/Cargo.toml @@ -6,12 +6,13 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-compression = {version = "^0.3.8", features = ["tokio", "zlib"]} +async-compression = {version = "^0.3.8", features = ["tokio", "zlib"], optional = true} async-recursion = "^0.3.2" azalea-auth = {path = "../azalea-auth"} azalea-brigadier = {path = "../azalea-brigadier"} +azalea-buf = {path = "../azalea-buf"} azalea-chat = {path = "../azalea-chat"} -azalea-core = {path = "../azalea-core"} +azalea-core = {path = "../azalea-core", optional = true} azalea-crypto = {path = "../azalea-crypto"} azalea-nbt = {path = "../azalea-nbt"} byteorder = "^1.4.3" @@ -27,3 +28,8 @@ tokio = {version = "^1.19.2", features = ["io-util", "net", "macros"]} tokio-util = "^0.6.9" trust-dns-resolver = "^0.20.3" uuid = "^1.1.2" + +[features] +connecting = [] +default = ["packets"] +packets = ["connecting", "dep:async-compression", "dep:azalea-core"] diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 5ea69a62..56b5dc52 100755 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -6,180 +6,6 @@ use syn::{ parse_macro_input, Data, DeriveInput, FieldsNamed, Ident, LitInt, Token, }; -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(*Packet)] 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(_) => { - if f.attrs.iter().any(|a| a.path.is_ident("var")) { - quote! { - let #field_name = crate::mc_buf::McBufVarReadable::var_read_into(buf)?; - } - } else { - quote! { - let #field_name = crate::mc_buf::McBufReadable::read_into(buf)?; - } - } - } - _ => panic!( - "Error reading field {}: {}", - field_name.clone().unwrap(), - field_type.to_token_stream() - ), - } - }) - .collect::>(); - let read_field_names = named.iter().map(|f| &f.ident).collect::>(); - - quote! { - impl crate::mc_buf::McBufReadable for #ident { - fn read_into(buf: &mut impl std::io::Read) -> Result { - #(#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; - 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"), - }, - _ => panic!("Error parsing enum discriminant"), - } - } - None => { - variant_discrim += 1; - } - } - match_contents.extend(quote! { - #variant_discrim => Ok(Self::#variant_name), - }); - } - - quote! { - impl crate::mc_buf::McBufReadable for #ident { - fn read_into(buf: &mut impl std::io::Read) -> Result - { - let id = crate::mc_buf::McBufVarReadable::var_read_into(buf)?; - match id { - #match_contents - _ => Err(format!("Unknown enum variant {}", id)), - } - } - } - } - } - _ => panic!("#[derive(*Packet)] 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(*Packet)] 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(_) => { - if f.attrs.iter().any(|attr| attr.path.is_ident("var")) { - quote! { - crate::mc_buf::McBufVarWritable::var_write_into(&self.#field_name, buf)?; - } - } else { - quote! { - crate::mc_buf::McBufWritable::write_into(&self.#field_name, buf)?; - } - } - } - _ => panic!( - "Error writing field {}: {}", - field_name.clone().unwrap(), - field_type.to_token_stream() - ), - } - }) - .collect::>(); - - quote! { - impl crate::mc_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 { .. }) => { - quote! { - impl crate::mc_buf::McBufWritable for #ident { - fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - crate::mc_buf::Writable::write_varint(buf, *self as i32) - } - } - } - } - _ => panic!("#[derive(*Packet)] can only be used on structs"), - } -} - -#[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() -} - -#[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() -} - -#[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); - quote! { - #writable - #readable - } - .into() -} - fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); @@ -192,9 +18,6 @@ fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> Toke _ => panic!("#[derive(*Packet)] can only be used on structs with named fields"), }; - let _mcbufreadable_impl = create_impl_mcbufreadable(&ident, &data); - let _mcbufwritable_impl = create_impl_mcbufwritable(&ident, &data); - let contents = quote! { impl #ident { pub fn get(self) -> #state { diff --git a/azalea-protocol/src/lib.rs b/azalea-protocol/src/lib.rs index d7f75f00..cadbb437 100755 --- a/azalea-protocol/src/lib.rs +++ b/azalea-protocol/src/lib.rs @@ -1,13 +1,12 @@ //! This lib is responsible for parsing Minecraft packets. -#![feature(min_specialization)] -#![feature(arbitrary_enum_discriminant)] - use std::net::IpAddr; use std::str::FromStr; +#[cfg(feature = "connecting")] pub mod connect; pub mod mc_buf; +#[cfg(feature = "packets")] pub mod packets; pub mod read; pub mod resolver; @@ -43,6 +42,7 @@ impl<'a> TryFrom<&'a str> for ServerAddress { } } +#[cfg(feature = "connecting")] pub async fn connect(address: ServerAddress) -> Result<(), Box> { let resolved_address = resolver::resolve_address(&address).await; println!("Resolved address: {:?}", resolved_address); diff --git a/azalea-protocol/src/mc_buf/definitions.rs b/azalea-protocol/src/mc_buf/definitions.rs deleted file mode 100644 index 3867b3fc..00000000 --- a/azalea-protocol/src/mc_buf/definitions.rs +++ /dev/null @@ -1,442 +0,0 @@ -use crate::mc_buf::read::{McBufReadable, Readable}; -use crate::mc_buf::write::{McBufWritable, Writable}; -use crate::mc_buf::McBufVarReadable; -use azalea_chat::component::Component; -use azalea_core::{BlockPos, Direction, Slot}; -use packet_macros::McBuf; -use std::io::{Read, Write}; -use std::ops::Deref; -use uuid::Uuid; - -/// A Vec that isn't prefixed by a VarInt with the size. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct UnsizedByteArray(Vec); - -impl Deref for UnsizedByteArray { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From> for UnsizedByteArray { - fn from(vec: Vec) -> Self { - Self(vec) - } -} - -impl From<&str> for UnsizedByteArray { - fn from(s: &str) -> Self { - Self(s.as_bytes().to_vec()) - } -} - -/// Represents Java's BitSet, a list of bits. -#[derive(Debug, Clone, PartialEq, Eq, Hash, McBuf)] -pub struct BitSet { - data: Vec, -} - -// the Index trait requires us to return a reference, but we can't do that -impl BitSet { - pub fn index(&self, index: usize) -> bool { - (self.data[index / 64] & (1u64 << (index % 64))) != 0 - } -} - -pub type EntityMetadata = Vec; - -#[derive(Clone, Debug)] -pub struct EntityDataItem { - // we can't identify what the index is for here because we don't know the - // entity type - pub index: u8, - pub value: EntityDataValue, -} - -impl McBufReadable for Vec { - fn read_into(buf: &mut impl Read) -> Result { - let mut metadata = Vec::new(); - loop { - let index = buf.read_byte()?; - if index == 0xff { - break; - } - let value = EntityDataValue::read_into(buf)?; - metadata.push(EntityDataItem { index, value }); - } - Ok(metadata) - } -} - -impl McBufWritable for Vec { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - for item in self { - buf.write_byte(item.index)?; - item.value.write_into(buf)?; - } - buf.write_byte(0xff)?; - Ok(()) - } -} - -#[derive(Clone, Debug)] -pub enum EntityDataValue { - Byte(u8), - // varint - Int(i32), - Float(f32), - String(String), - Component(Component), - OptionalComponent(Option), - ItemStack(Slot), - Boolean(bool), - Rotations { x: f32, y: f32, z: f32 }, - BlockPos(BlockPos), - OptionalBlockPos(Option), - Direction(Direction), - OptionalUuid(Option), - // 0 for absent (implies air); otherwise, a block state ID as per the global palette - // this is a varint - OptionalBlockState(Option), - CompoundTag(azalea_nbt::Tag), - Particle(Particle), - VillagerData(VillagerData), - // 0 for absent; 1 + actual value otherwise. Used for entity IDs. - OptionalUnsignedInt(Option), - Pose(Pose), -} - -impl McBufReadable for EntityDataValue { - fn read_into(buf: &mut impl Read) -> Result { - let type_ = buf.read_varint()?; - Ok(match type_ { - 0 => EntityDataValue::Byte(buf.read_byte()?), - 1 => EntityDataValue::Int(buf.read_varint()?), - 2 => EntityDataValue::Float(buf.read_float()?), - 3 => EntityDataValue::String(buf.read_utf()?), - 4 => EntityDataValue::Component(Component::read_into(buf)?), - 5 => EntityDataValue::OptionalComponent(Option::::read_into(buf)?), - 6 => EntityDataValue::ItemStack(Slot::read_into(buf)?), - 7 => EntityDataValue::Boolean(buf.read_boolean()?), - 8 => EntityDataValue::Rotations { - x: buf.read_float()?, - y: buf.read_float()?, - z: buf.read_float()?, - }, - 9 => EntityDataValue::BlockPos(BlockPos::read_into(buf)?), - 10 => EntityDataValue::OptionalBlockPos(Option::::read_into(buf)?), - 11 => EntityDataValue::Direction(Direction::read_into(buf)?), - 12 => EntityDataValue::OptionalUuid(Option::::read_into(buf)?), - 13 => EntityDataValue::OptionalBlockState({ - let val = i32::read_into(buf)?; - if val == 0 { - None - } else { - Some(val) - } - }), - 14 => EntityDataValue::CompoundTag(azalea_nbt::Tag::read_into(buf)?), - 15 => EntityDataValue::Particle(Particle::read_into(buf)?), - 16 => EntityDataValue::VillagerData(VillagerData::read_into(buf)?), - 17 => EntityDataValue::OptionalUnsignedInt({ - let val = buf.read_varint()?; - if val == 0 { - None - } else { - Some((val - 1) as u32) - } - }), - 18 => EntityDataValue::Pose(Pose::read_into(buf)?), - _ => return Err(format!("Unknown entity data type: {}", type_)), - }) - } -} - -impl McBufWritable for EntityDataValue { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - todo!(); - } -} - -#[derive(Clone, Debug, Copy, McBuf)] -pub enum Pose { - Standing = 0, - FallFlying = 1, - Sleeping = 2, - Swimming = 3, - SpinAttack = 4, - Sneaking = 5, - LongJumping = 6, - Dying = 7, -} - -#[derive(Debug, Clone, McBuf)] -pub struct VillagerData { - #[var] - type_: u32, - #[var] - profession: u32, - #[var] - level: u32, -} - -#[derive(Debug, Clone, McBuf)] -pub struct Particle { - #[var] - pub id: i32, - pub data: ParticleData, -} - -#[derive(Clone, Debug)] -pub enum ParticleData { - AmbientEntityEffect, - AngryVillager, - Block(BlockParticle), - BlockMarker(BlockParticle), - Bubble, - Cloud, - Crit, - DamageIndicator, - DragonBreath, - DrippingLava, - FallingLava, - LandingLava, - DrippingWater, - FallingWater, - Dust(DustParticle), - DustColorTransition(DustColorTransitionParticle), - Effect, - ElderGuardian, - EnchantedHit, - Enchant, - EndRod, - EntityEffect, - ExplosionEmitter, - Explosion, - FallingDust(BlockParticle), - Firework, - Fishing, - Flame, - SoulFireFlame, - Soul, - Flash, - HappyVillager, - Composter, - Heart, - InstantEffect, - Item(ItemParticle), - Vibration(VibrationParticle), - ItemSlime, - ItemSnowball, - LargeSmoke, - Lava, - Mycelium, - Note, - Poof, - Portal, - Rain, - Smoke, - Sneeze, - Spit, - SquidInk, - SweepAttack, - TotemOfUndying, - Underwater, - Splash, - Witch, - BubblePop, - CurrentDown, - BubbleColumnUp, - Nautilus, - Dolphin, - CampfireCozySmoke, - CampfireSignalSmoke, - DrippingHoney, - FallingHoney, - LandingHoney, - FallingNectar, - FallingSporeBlossom, - Ash, - CrimsonSpore, - WarpedSpore, - SporeBlossomAir, - DrippingObsidianTear, - FallingObsidianTear, - LandingObsidianTear, - ReversePortal, - WhiteAsh, - SmallFlame, - Snowflake, - DrippingDripstoneLava, - FallingDripstoneLava, - DrippingDripstoneWater, - FallingDripstoneWater, - GlowSquidInk, - Glow, - WaxOn, - WaxOff, - ElectricSpark, - Scrape, -} - -#[derive(Debug, Clone, McBuf)] -pub struct BlockParticle { - #[var] - pub block_state: i32, -} -#[derive(Debug, Clone, McBuf)] -pub struct DustParticle { - /// Red value, 0-1 - pub red: f32, - /// Green value, 0-1 - pub green: f32, - /// Blue value, 0-1 - pub blue: f32, - /// The scale, will be clamped between 0.01 and 4. - pub scale: f32, -} - -#[derive(Debug, Clone, McBuf)] -pub struct DustColorTransitionParticle { - /// Red value, 0-1 - pub from_red: f32, - /// Green value, 0-1 - pub from_green: f32, - /// Blue value, 0-1 - pub from_blue: f32, - /// The scale, will be clamped between 0.01 and 4. - pub scale: f32, - /// Red value, 0-1 - pub to_red: f32, - /// Green value, 0-1 - pub to_green: f32, - /// Blue value, 0-1 - pub to_blue: f32, -} - -#[derive(Debug, Clone, McBuf)] -pub struct ItemParticle { - pub item: Slot, -} - -#[derive(Debug, Clone, McBuf)] -pub struct VibrationParticle { - pub origin: BlockPos, - pub position_type: String, - pub block_position: BlockPos, - #[var] - pub entity_id: u32, - #[var] - pub ticks: u32, -} - -impl ParticleData { - pub fn read_from_particle_id(buf: &mut impl Read, id: u32) -> Result { - Ok(match id { - 0 => ParticleData::AmbientEntityEffect, - 1 => ParticleData::AngryVillager, - 2 => ParticleData::Block(BlockParticle::read_into(buf)?), - 3 => ParticleData::BlockMarker(BlockParticle::read_into(buf)?), - 4 => ParticleData::Bubble, - 5 => ParticleData::Cloud, - 6 => ParticleData::Crit, - 7 => ParticleData::DamageIndicator, - 8 => ParticleData::DragonBreath, - 9 => ParticleData::DrippingLava, - 10 => ParticleData::FallingLava, - 11 => ParticleData::LandingLava, - 12 => ParticleData::DrippingWater, - 13 => ParticleData::FallingWater, - 14 => ParticleData::Dust(DustParticle::read_into(buf)?), - 15 => ParticleData::DustColorTransition(DustColorTransitionParticle::read_into(buf)?), - 16 => ParticleData::Effect, - 17 => ParticleData::ElderGuardian, - 18 => ParticleData::EnchantedHit, - 19 => ParticleData::Enchant, - 20 => ParticleData::EndRod, - 21 => ParticleData::EntityEffect, - 22 => ParticleData::ExplosionEmitter, - 23 => ParticleData::Explosion, - 24 => ParticleData::FallingDust(BlockParticle::read_into(buf)?), - 25 => ParticleData::Firework, - 26 => ParticleData::Fishing, - 27 => ParticleData::Flame, - 28 => ParticleData::SoulFireFlame, - 29 => ParticleData::Soul, - 30 => ParticleData::Flash, - 31 => ParticleData::HappyVillager, - 32 => ParticleData::Composter, - 33 => ParticleData::Heart, - 34 => ParticleData::InstantEffect, - 35 => ParticleData::Item(ItemParticle::read_into(buf)?), - 36 => ParticleData::Vibration(VibrationParticle::read_into(buf)?), - 37 => ParticleData::ItemSlime, - 38 => ParticleData::ItemSnowball, - 39 => ParticleData::LargeSmoke, - 40 => ParticleData::Lava, - 41 => ParticleData::Mycelium, - 42 => ParticleData::Note, - 43 => ParticleData::Poof, - 44 => ParticleData::Portal, - 45 => ParticleData::Rain, - 46 => ParticleData::Smoke, - 47 => ParticleData::Sneeze, - 48 => ParticleData::Spit, - 49 => ParticleData::SquidInk, - 50 => ParticleData::SweepAttack, - 51 => ParticleData::TotemOfUndying, - 52 => ParticleData::Underwater, - 53 => ParticleData::Splash, - 54 => ParticleData::Witch, - 55 => ParticleData::BubblePop, - 56 => ParticleData::CurrentDown, - 57 => ParticleData::BubbleColumnUp, - 58 => ParticleData::Nautilus, - 59 => ParticleData::Dolphin, - 60 => ParticleData::CampfireCozySmoke, - 61 => ParticleData::CampfireSignalSmoke, - 62 => ParticleData::DrippingHoney, - 63 => ParticleData::FallingHoney, - 64 => ParticleData::LandingHoney, - 65 => ParticleData::FallingNectar, - 66 => ParticleData::FallingSporeBlossom, - 67 => ParticleData::Ash, - 68 => ParticleData::CrimsonSpore, - 69 => ParticleData::WarpedSpore, - 70 => ParticleData::SporeBlossomAir, - 71 => ParticleData::DrippingObsidianTear, - 72 => ParticleData::FallingObsidianTear, - 73 => ParticleData::LandingObsidianTear, - 74 => ParticleData::ReversePortal, - 75 => ParticleData::WhiteAsh, - 76 => ParticleData::SmallFlame, - 77 => ParticleData::Snowflake, - 78 => ParticleData::DrippingDripstoneLava, - 79 => ParticleData::FallingDripstoneLava, - 80 => ParticleData::DrippingDripstoneWater, - 81 => ParticleData::FallingDripstoneWater, - 82 => ParticleData::GlowSquidInk, - 83 => ParticleData::Glow, - 84 => ParticleData::WaxOn, - 85 => ParticleData::WaxOff, - 86 => ParticleData::ElectricSpark, - 87 => ParticleData::Scrape, - _ => return Err(format!("Unknown particle id: {}", id)), - }) - } -} - -impl McBufReadable for ParticleData { - fn read_into(buf: &mut impl Read) -> Result { - let id = u32::var_read_into(buf)?; - ParticleData::read_from_particle_id(buf, id) - } -} - -impl McBufWritable for ParticleData { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - todo!() - } -} diff --git a/azalea-protocol/src/mc_buf/mod.rs b/azalea-protocol/src/mc_buf/mod.rs deleted file mode 100644 index 548ba7c2..00000000 --- a/azalea-protocol/src/mc_buf/mod.rs +++ /dev/null @@ -1,233 +0,0 @@ -//! Utilities for reading and writing for the Minecraft protocol - -mod definitions; -mod read; -mod write; - -pub use definitions::{BitSet, EntityMetadata, ParticleData, UnsizedByteArray}; -pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable}; -pub use write::{McBufVarWritable, McBufWritable, Writable}; - -// const DEFAULT_NBT_QUOTA: u32 = 2097152; -const MAX_STRING_LENGTH: u16 = 32767; -// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144; - -// TODO: maybe get rid of the readable/writable traits so there's not two ways to do the same thing and improve McBufReadable/McBufWritable - -// TODO: have a definitions.rs in mc_buf that contains UnsizedByteArray and BitSet - -#[cfg(test)] -mod tests { - use super::*; - use azalea_core::resource_location::ResourceLocation; - use std::{collections::HashMap, io::Cursor}; - - #[test] - fn test_write_varint() { - let mut buf = Vec::new(); - buf.write_varint(0).unwrap(); - assert_eq!(buf, vec![0]); - - let mut buf = Vec::new(); - buf.write_varint(1).unwrap(); - assert_eq!(buf, vec![1]); - - let mut buf = Vec::new(); - buf.write_varint(2).unwrap(); - assert_eq!(buf, vec![2]); - - let mut buf = Vec::new(); - buf.write_varint(127).unwrap(); - assert_eq!(buf, vec![127]); - - let mut buf = Vec::new(); - buf.write_varint(128).unwrap(); - assert_eq!(buf, vec![128, 1]); - - let mut buf = Vec::new(); - buf.write_varint(255).unwrap(); - assert_eq!(buf, vec![255, 1]); - - let mut buf = Vec::new(); - buf.write_varint(25565).unwrap(); - assert_eq!(buf, vec![221, 199, 1]); - - let mut buf = Vec::new(); - buf.write_varint(2097151).unwrap(); - assert_eq!(buf, vec![255, 255, 127]); - - let mut buf = Vec::new(); - buf.write_varint(2147483647).unwrap(); - assert_eq!(buf, vec![255, 255, 255, 255, 7]); - - let mut buf = Vec::new(); - buf.write_varint(-1).unwrap(); - assert_eq!(buf, vec![255, 255, 255, 255, 15]); - - let mut buf = Vec::new(); - buf.write_varint(-2147483648).unwrap(); - assert_eq!(buf, vec![128, 128, 128, 128, 8]); - } - - #[test] - fn test_read_varint() { - let mut buf = Cursor::new(vec![0]); - assert_eq!(buf.read_varint().unwrap(), 0); - assert_eq!(buf.get_varint_size(0), 1); - - let mut buf = Cursor::new(vec![1]); - assert_eq!(buf.read_varint().unwrap(), 1); - assert_eq!(buf.get_varint_size(1), 1); - - let mut buf = Cursor::new(vec![2]); - assert_eq!(buf.read_varint().unwrap(), 2); - assert_eq!(buf.get_varint_size(2), 1); - - let mut buf = Cursor::new(vec![127]); - assert_eq!(buf.read_varint().unwrap(), 127); - assert_eq!(buf.get_varint_size(127), 1); - - let mut buf = Cursor::new(vec![128, 1]); - assert_eq!(buf.read_varint().unwrap(), 128); - assert_eq!(buf.get_varint_size(128), 2); - - let mut buf = Cursor::new(vec![255, 1]); - assert_eq!(buf.read_varint().unwrap(), 255); - assert_eq!(buf.get_varint_size(255), 2); - - let mut buf = Cursor::new(vec![221, 199, 1]); - assert_eq!(buf.read_varint().unwrap(), 25565); - assert_eq!(buf.get_varint_size(25565), 3); - - let mut buf = Cursor::new(vec![255, 255, 127]); - assert_eq!(buf.read_varint().unwrap(), 2097151); - assert_eq!(buf.get_varint_size(2097151), 3); - - let mut buf = Cursor::new(vec![255, 255, 255, 255, 7]); - assert_eq!(buf.read_varint().unwrap(), 2147483647); - assert_eq!(buf.get_varint_size(2147483647), 5); - - let mut buf = Cursor::new(vec![255, 255, 255, 255, 15]); - assert_eq!(buf.read_varint().unwrap(), -1); - assert_eq!(buf.get_varint_size(-1), 5); - - let mut buf = Cursor::new(vec![128, 128, 128, 128, 8]); - assert_eq!(buf.read_varint().unwrap(), -2147483648); - assert_eq!(buf.get_varint_size(-2147483648), 5); - } - - #[test] - fn test_read_varint_longer() { - let mut buf = Cursor::new(vec![138, 56, 0, 135, 56, 123]); - assert_eq!(buf.read_varint().unwrap(), 7178); - } - - #[test] - fn test_list() { - let mut buf = Vec::new(); - buf.write_list(&vec!["a", "bc", "def"], |buf, s| buf.write_utf(s)) - .unwrap(); - - // there's no read_list because idk how to do it in rust - let mut buf = Cursor::new(buf); - - let mut result = Vec::new(); - let length = buf.read_varint().unwrap(); - for _ in 0..length { - result.push(buf.read_utf().unwrap()); - } - - assert_eq!(result, vec!["a", "bc", "def"]); - } - - #[test] - fn test_int_id_list() { - let mut buf = Vec::new(); - buf.write_list(&vec![1, 2, 3], |buf, i| buf.write_varint(*i)) - .unwrap(); - - let mut buf = Cursor::new(buf); - - let result = buf.read_int_id_list().unwrap(); - assert_eq!(result, vec![1, 2, 3]); - } - - #[test] - fn test_map() { - let mut buf = Vec::new(); - buf.write_map( - vec![("a", 1), ("bc", 23), ("def", 456)], - Vec::write_utf, - Vec::write_varint, - ) - .unwrap(); - - let mut buf = Cursor::new(buf); - - let mut result = Vec::new(); - let length = buf.read_varint().unwrap(); - for _ in 0..length { - result.push((buf.read_utf().unwrap(), buf.read_varint().unwrap())); - } - - assert_eq!( - result, - vec![ - ("a".to_string(), 1), - ("bc".to_string(), 23), - ("def".to_string(), 456) - ] - ); - } - - #[test] - fn test_nbt() { - let mut buf = Vec::new(); - buf.write_nbt(&azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "hello world".to_string(), - azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "name".to_string(), - azalea_nbt::Tag::String("Bananrama".to_string()), - )])), - )]))) - .unwrap(); - - let mut buf = Cursor::new(buf); - - let result = buf.read_nbt().unwrap(); - assert_eq!( - result, - azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "hello world".to_string(), - azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "name".to_string(), - azalea_nbt::Tag::String("Bananrama".to_string()), - )])), - )])) - ); - } - - #[test] - fn test_long() { - let mut buf = Vec::new(); - buf.write_long(123456).unwrap(); - - let mut buf = Cursor::new(buf); - - assert_eq!(buf.read_long().unwrap(), 123456); - } - - #[test] - fn test_resource_location() { - let mut buf = Vec::new(); - buf.write_resource_location(&ResourceLocation::new("minecraft:dirt").unwrap()) - .unwrap(); - - let mut buf = Cursor::new(buf); - - assert_eq!( - buf.read_resource_location().unwrap(), - ResourceLocation::new("minecraft:dirt").unwrap() - ); - } -} diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs deleted file mode 100644 index ac8d5ccb..00000000 --- a/azalea-protocol/src/mc_buf/read.rs +++ /dev/null @@ -1,525 +0,0 @@ -use super::{UnsizedByteArray, MAX_STRING_LENGTH}; -use azalea_chat::component::Component; -use azalea_core::{ - difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, - serializable_uuid::SerializableUuid, BlockPos, ChunkSectionPos, Direction, GlobalPos, Slot, - SlotData, -}; -use azalea_crypto::SaltSignaturePair; -use byteorder::{ReadBytesExt, BE}; -use serde::Deserialize; -use std::{collections::HashMap, hash::Hash, io::Read}; -use tokio::io::{AsyncRead, AsyncReadExt}; -use uuid::Uuid; - -pub trait Readable { - fn read_int_id_list(&mut self) -> Result, String>; - fn read_varint(&mut self) -> Result; - fn get_varint_size(&mut self, value: i32) -> u8; - fn get_varlong_size(&mut self, value: i32) -> u8; - fn read_byte_array(&mut self) -> Result, String>; - fn read_bytes_with_len(&mut self, n: usize) -> Result, String>; - fn read_bytes(&mut self) -> Result, String>; - fn read_utf(&mut self) -> Result; - fn read_utf_with_len(&mut self, max_length: u32) -> Result; - fn read_byte(&mut self) -> Result; - fn read_int(&mut self) -> Result; - fn read_boolean(&mut self) -> Result; - fn read_nbt(&mut self) -> Result; - fn read_long(&mut self) -> Result; - fn read_resource_location(&mut self) -> Result; - fn read_short(&mut self) -> Result; - fn read_float(&mut self) -> Result; - fn read_double(&mut self) -> Result; - fn read_uuid(&mut self) -> Result; -} - -impl Readable for R -where - R: Read, -{ - fn read_int_id_list(&mut self) -> Result, String> { - let len = self.read_varint()?; - let mut list = Vec::with_capacity(len as usize); - for _ in 0..len { - list.push(self.read_varint()?); - } - Ok(list) - } - - // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 - /// Read a single varint from the reader and return the value, along with the number of bytes read - fn read_varint(&mut self) -> Result { - let mut buffer = [0]; - let mut ans = 0; - for i in 0..5 { - self.read_exact(&mut buffer) - .map_err(|_| "Invalid VarInt".to_string())?; - ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); - if buffer[0] & 0b1000_0000 == 0 { - return Ok(ans); - } - } - Ok(ans) - } - - fn get_varint_size(&mut self, value: i32) -> u8 { - for i in 1..5 { - if (value & -1 << (i * 7)) != 0 { - continue; - } - return i; - } - 5 - } - - fn get_varlong_size(&mut self, value: i32) -> u8 { - for i in 1..10 { - if (value & -1 << (i * 7)) != 0 { - continue; - } - return i; - } - 10 - } - - fn read_byte_array(&mut self) -> Result, String> { - let length = self.read_varint()? as usize; - self.read_bytes_with_len(length) - } - - fn read_bytes_with_len(&mut self, n: usize) -> Result, String> { - let mut buffer = vec![0; n]; - self.read_exact(&mut buffer) - .map_err(|_| "Error reading bytes".to_string())?; - Ok(buffer) - } - - fn read_bytes(&mut self) -> Result, String> { - // read to end of the buffer - let mut bytes = vec![]; - self.read_to_end(&mut bytes) - .map_err(|_| "Error reading bytes".to_string())?; - Ok(bytes) - } - - fn read_utf(&mut self) -> Result { - self.read_utf_with_len(MAX_STRING_LENGTH.into()) - } - - fn read_utf_with_len(&mut self, max_length: u32) -> Result { - let length = self.read_varint()?; - // i don't know why it's multiplied by 4 but it's like that in mojang's code so - if length < 0 { - return Err( - "The received encoded string buffer length is less than zero! Weird string!" - .to_string(), - ); - } - if length as u32 > max_length * 4 { - return Err(format!( - "The received encoded string buffer length is longer than maximum allowed ({} > {})", - length, - max_length * 4 - )); - } - - // this is probably quite inefficient, idk how to do it better - let mut string = String::new(); - let mut buffer = vec![0; length as usize]; - self.read_exact(&mut buffer) - .map_err(|_| "Invalid UTF-8".to_string())?; - string.push_str(std::str::from_utf8(&buffer).unwrap()); - if string.len() > length as usize { - return Err(format!( - "The received string length is longer than maximum allowed ({} > {})", - length, max_length - )); - } - - Ok(string) - } - - /// Read a single byte from the reader - fn read_byte(&mut self) -> Result { - self.read_u8().map_err(|_| "Error reading byte".to_string()) - } - - fn read_int(&mut self) -> Result { - match self.read_i32::() { - Ok(r) => Ok(r), - Err(_) => Err("Error reading int".to_string()), - } - } - - fn read_boolean(&mut self) -> Result { - match self.read_byte()? { - 0 => Ok(false), - 1 => Ok(true), - _ => Err("Error reading boolean".to_string()), - } - } - - fn read_nbt(&mut self) -> Result { - match azalea_nbt::Tag::read(self) { - Ok(r) => Ok(r), - // Err(e) => Err(e.to_string()), - Err(e) => Err(e.to_string()).unwrap(), - } - } - - fn read_long(&mut self) -> Result { - match self.read_i64::() { - Ok(r) => Ok(r), - Err(_) => Err("Error reading long".to_string()), - } - } - - fn read_resource_location(&mut self) -> Result { - // get the resource location from the string - let location_string = self.read_utf()?; - let location = ResourceLocation::new(&location_string)?; - Ok(location) - } - - fn read_short(&mut self) -> Result { - match self.read_i16::() { - Ok(r) => Ok(r), - Err(_) => Err("Error reading short".to_string()), - } - } - - fn read_float(&mut self) -> Result { - match self.read_f32::() { - Ok(r) => Ok(r), - Err(_) => Err("Error reading float".to_string()), - } - } - - fn read_double(&mut self) -> Result { - match self.read_f64::() { - Ok(r) => Ok(r), - Err(_) => Err("Error reading double".to_string()), - } - } - - fn read_uuid(&mut self) -> Result { - Ok(Uuid::from_int_array([ - Readable::read_int(self)? as u32, - Readable::read_int(self)? as u32, - Readable::read_int(self)? as u32, - Readable::read_int(self)? as u32, - ])) - } -} - -// fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 -/// Read a single varint from the reader and return the value, along with the number of bytes read -pub async fn read_varint_async(reader: &mut (dyn AsyncRead + Unpin + Send)) -> Result { - let mut buffer = [0]; - let mut ans = 0; - for i in 0..5 { - reader - .read_exact(&mut buffer) - .await - .map_err(|_| "Invalid VarInt".to_string())?; - ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); - if buffer[0] & 0b1000_0000 == 0 { - return Ok(ans); - } - } - Ok(ans) -} - -pub trait McBufReadable -where - Self: Sized, -{ - fn read_into(buf: &mut impl Read) -> Result; -} - -pub trait McBufVarReadable -where - Self: Sized, -{ - fn var_read_into(buf: &mut impl Read) -> Result; -} - -impl McBufReadable for i32 { - fn read_into(buf: &mut impl Read) -> Result { - Readable::read_int(buf) - } -} - -impl McBufVarReadable for i32 { - fn var_read_into(buf: &mut impl Read) -> Result { - buf.read_varint() - } -} - -impl McBufVarReadable for i64 { - // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L54 - fn var_read_into(buf: &mut impl Read) -> Result { - let mut buffer = [0]; - let mut ans = 0; - for i in 0..8 { - buf.read_exact(&mut buffer) - .map_err(|_| "Invalid VarLong".to_string())?; - ans |= ((buffer[0] & 0b0111_1111) as i64) << 7 * i; - if buffer[0] & 0b1000_0000 == 0 { - break; - } - } - Ok(ans) - } -} -impl McBufVarReadable for u64 { - fn var_read_into(buf: &mut impl Read) -> Result { - i64::var_read_into(buf).map(|i| i as u64) - } -} - -impl McBufReadable for UnsizedByteArray { - fn read_into(buf: &mut impl Read) -> Result { - Ok(buf.read_bytes()?.into()) - } -} - -impl McBufReadable for Vec { - default fn read_into(buf: &mut impl Read) -> Result { - let length = buf.read_varint()? as usize; - let mut contents = Vec::with_capacity(length); - for _ in 0..length { - contents.push(T::read_into(buf)?); - } - Ok(contents) - } -} - -impl McBufReadable for HashMap { - default fn read_into(buf: &mut impl Read) -> Result { - let length = buf.read_varint()? as usize; - let mut contents = HashMap::with_capacity(length); - for _ in 0..length { - contents.insert(K::read_into(buf)?, V::read_into(buf)?); - } - Ok(contents) - } -} - -impl McBufReadable for Vec { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_byte_array() - } -} - -impl McBufReadable for String { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_utf() - } -} - -impl McBufReadable for ResourceLocation { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_resource_location() - } -} - -impl McBufReadable for u32 { - fn read_into(buf: &mut impl Read) -> Result { - Readable::read_int(buf).map(|i| i as u32) - } -} - -impl McBufVarReadable for u32 { - fn var_read_into(buf: &mut impl Read) -> Result { - buf.read_varint().map(|i| i as u32) - } -} - -impl McBufReadable for u16 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_short().map(|i| i as u16) - } -} - -impl McBufReadable for i16 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_short() - } -} - -impl McBufVarReadable for u16 { - fn var_read_into(buf: &mut impl Read) -> Result { - buf.read_varint().map(|i| i as u16) - } -} - -impl McBufVarReadable for Vec { - fn var_read_into(buf: &mut impl Read) -> Result { - let length = buf.read_varint()? as usize; - let mut contents = Vec::with_capacity(length); - for _ in 0..length { - contents.push(T::var_read_into(buf)?); - } - Ok(contents) - } -} - -impl McBufReadable for i64 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_long() - } -} - -impl McBufReadable for u64 { - fn read_into(buf: &mut impl Read) -> Result { - i64::read_into(buf).map(|i| i as u64) - } -} - -impl McBufReadable for bool { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_boolean() - } -} - -impl McBufReadable for u8 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_byte() - } -} - -impl McBufReadable for i8 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_byte().map(|i| i as i8) - } -} - -impl McBufReadable for f32 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_float() - } -} - -impl McBufReadable for f64 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_double() - } -} - -impl McBufReadable for GameType { - fn read_into(buf: &mut impl Read) -> Result { - GameType::from_id(buf.read_byte()?) - } -} - -impl McBufReadable for Option { - fn read_into(buf: &mut impl Read) -> Result { - GameType::from_optional_id(buf.read_byte()? as i8) - } -} - -impl McBufReadable for Option { - default fn read_into(buf: &mut impl Read) -> Result { - let present = buf.read_boolean()?; - Ok(if present { - Some(T::read_into(buf)?) - } else { - None - }) - } -} - -impl McBufReadable for azalea_nbt::Tag { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_nbt() - } -} - -impl McBufReadable for Difficulty { - fn read_into(buf: &mut impl Read) -> Result { - Ok(Difficulty::by_id(u8::read_into(buf)?)) - } -} - -impl McBufReadable for Component { - fn read_into(buf: &mut impl Read) -> Result { - let string = buf.read_utf()?; - let json: serde_json::Value = serde_json::from_str(string.as_str()) - .map_err(|_| "Component isn't valid JSON".to_string())?; - let component = Component::deserialize(json).map_err(|e| e.to_string())?; - Ok(component) - } -} - -impl McBufReadable for Slot { - fn read_into(buf: &mut impl Read) -> Result { - let present = buf.read_boolean()?; - if !present { - return Ok(Slot::Empty); - } - let id = buf.read_varint()?; - let count = buf.read_byte()?; - let nbt = buf.read_nbt()?; - Ok(Slot::Present(SlotData { id, count, nbt })) - } -} - -impl McBufReadable for Uuid { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_uuid() - } -} - -impl McBufReadable for BlockPos { - fn read_into(buf: &mut impl Read) -> Result { - let val = u64::read_into(buf)?; - let x = (val >> 38) as i32; - let y = (val & 0xFFF) as i32; - let z = ((val >> 12) & 0x3FFFFFF) as i32; - Ok(BlockPos { x, y, z }) - } -} - -impl McBufReadable for GlobalPos { - fn read_into(buf: &mut impl Read) -> Result { - Ok(GlobalPos { - dimension: ResourceLocation::read_into(buf)?, - pos: BlockPos::read_into(buf)?, - }) - } -} - -impl McBufReadable for Direction { - fn read_into(buf: &mut impl Read) -> Result { - match buf.read_varint()? { - 0 => Ok(Self::Down), - 1 => Ok(Self::Up), - 2 => Ok(Self::North), - 3 => Ok(Self::South), - 4 => Ok(Self::West), - 5 => Ok(Self::East), - _ => Err("Invalid direction".to_string()), - } - } -} - -impl McBufReadable for ChunkSectionPos { - fn read_into(buf: &mut impl Read) -> Result { - let long = i64::read_into(buf)?; - Ok(ChunkSectionPos { - x: (long >> 42) as i32, - y: (long << 44 >> 44) as i32, - z: (long << 22 >> 42) as i32, - }) - } -} - -impl McBufReadable for SaltSignaturePair { - fn read_into(buf: &mut impl Read) -> Result { - let salt = u64::read_into(buf)?; - let signature = Vec::::read_into(buf)?; - Ok(SaltSignaturePair { salt, signature }) - } -} diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs deleted file mode 100644 index 945477d0..00000000 --- a/azalea-protocol/src/mc_buf/write.rs +++ /dev/null @@ -1,430 +0,0 @@ -use super::{UnsizedByteArray, MAX_STRING_LENGTH}; -use azalea_chat::component::Component; -use azalea_core::{ - difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, - serializable_uuid::SerializableUuid, BlockPos, ChunkSectionPos, Direction, GlobalPos, Slot, -}; -use azalea_crypto::SaltSignaturePair; -use byteorder::{BigEndian, WriteBytesExt}; -use std::{collections::HashMap, io::Write}; -use uuid::Uuid; - -pub trait Writable: Write { - fn write_list(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error> - where - F: FnOnce(&mut Self, &T) -> Result<(), std::io::Error> + Copy, - { - self.write_varint(list.len() as i32)?; - for item in list { - writer(self, item)?; - } - Ok(()) - } - - fn write_int_id_list(&mut self, list: &[i32]) -> Result<(), std::io::Error> { - self.write_list(list, |buf, n| buf.write_varint(*n)) - } - - fn write_map( - &mut self, - map: Vec<(KT, VT)>, - key_writer: KF, - value_writer: VF, - ) -> Result<(), std::io::Error> - where - KF: Fn(&mut Self, KT) -> Result<(), std::io::Error> + Copy, - VF: Fn(&mut Self, VT) -> Result<(), std::io::Error> + Copy, - { - self.write_varint(map.len() as i32)?; - for (key, value) in map { - key_writer(self, key)?; - value_writer(self, value)?; - } - Ok(()) - } - - fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error> { - WriteBytesExt::write_u8(self, n) - } - - fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { - self.write_all(bytes)?; - Ok(()) - } - - fn write_varint(&mut self, mut value: i32) -> Result<(), std::io::Error> { - let mut buffer = [0]; - if value == 0 { - self.write_all(&buffer).unwrap(); - } - while value != 0 { - buffer[0] = (value & 0b0111_1111) as u8; - value = (value >> 7) & (i32::max_value() >> 6); - if value != 0 { - buffer[0] |= 0b1000_0000; - } - self.write_all(&buffer)?; - } - Ok(()) - } - - fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error> { - if string.len() > len { - panic!( - "String too big (was {} bytes encoded, max {})", - string.len(), - len - ); - } - self.write_varint(string.len() as i32)?; - self.write_bytes(string.as_bytes()) - } - - fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error> { - self.write_utf_with_len(string, MAX_STRING_LENGTH.into()) - } - - fn write_short(&mut self, n: i16) -> Result<(), std::io::Error> { - WriteBytesExt::write_i16::(self, n) - } - - fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { - self.write_varint(bytes.len() as i32)?; - self.write_bytes(bytes) - } - - fn write_int(&mut self, n: i32) -> Result<(), std::io::Error> { - WriteBytesExt::write_i32::(self, n) - } - - fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error> { - self.write_byte(if b { 1 } else { 0 }) - } - - fn write_nbt(&mut self, nbt: &azalea_nbt::Tag) -> Result<(), std::io::Error> - where - Self: Sized, - { - nbt.write(self) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) - } - - fn write_long(&mut self, n: i64) -> Result<(), std::io::Error> { - WriteBytesExt::write_i64::(self, n) - } - - fn write_float(&mut self, n: f32) -> Result<(), std::io::Error> { - WriteBytesExt::write_f32::(self, n) - } - - fn write_double(&mut self, n: f64) -> Result<(), std::io::Error> { - WriteBytesExt::write_f64::(self, n) - } - - fn write_resource_location( - &mut self, - location: &ResourceLocation, - ) -> Result<(), std::io::Error> { - self.write_utf(&location.to_string()) - } - - fn write_uuid(&mut self, uuid: &Uuid) -> Result<(), std::io::Error> - where - Self: Sized, - { - let [a, b, c, d] = uuid.to_int_array(); - a.write_into(self)?; - b.write_into(self)?; - c.write_into(self)?; - d.write_into(self)?; - Ok(()) - } -} - -impl Writable for W {} - -pub trait McBufWritable { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>; -} - -pub trait McBufVarWritable { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>; -} - -impl McBufWritable for i32 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - Writable::write_int(buf, *self) - } -} - -impl McBufVarWritable for i32 { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_varint(*self) - } -} - -impl McBufWritable for UnsizedByteArray { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_bytes(self) - } -} - -impl McBufWritable for Vec { - default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_list(self, |buf, i| T::write_into(i, buf)) - } -} - -impl McBufWritable for HashMap { - default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - u32::var_write_into(&(self.len() as u32), buf)?; - for (key, value) in self { - key.write_into(buf)?; - value.write_into(buf)?; - } - - Ok(()) - } -} - -impl McBufWritable for Vec { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_byte_array(self) - } -} - -impl McBufWritable for String { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_utf(self) - } -} - -impl McBufWritable for ResourceLocation { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_resource_location(self) - } -} - -impl McBufWritable for u32 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i16::write_into(&(*self as i16), buf) - } -} - -impl McBufVarWritable for u32 { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i32::var_write_into(&(*self as i32), buf) - } -} - -impl McBufVarWritable for i64 { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let mut buffer = [0]; - let mut cnt = 0; - let mut value = *self; - while value != 0 { - buffer[0] = (value & 0b0111_1111) as u8; - value = (value >> 7) & (i64::max_value() >> 6); - if value != 0 { - buffer[0] |= 0b1000_0000; - } - cnt += buf.write(&mut buffer)?; - } - Ok(()) - } -} - -impl McBufVarWritable for u64 { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i64::var_write_into(&(*self as i64), buf) - } -} - -impl McBufWritable for u16 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i16::write_into(&(*self as i16), buf) - } -} - -impl McBufVarWritable for u16 { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i32::var_write_into(&(*self as i32), buf) - } -} - -impl McBufVarWritable for Vec { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - u32::var_write_into(&(self.len() as u32), buf)?; - for i in self { - i.var_write_into(buf)?; - } - Ok(()) - } -} - -impl McBufWritable for u8 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_byte(*self) - } -} - -impl McBufWritable for i16 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - Writable::write_short(buf, *self) - } -} - -impl McBufWritable for i64 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - Writable::write_long(buf, *self) - } -} - -impl McBufWritable for u64 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i64::write_into(&(*self as i64), buf) - } -} - -impl McBufWritable for bool { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_boolean(*self) - } -} - -impl McBufWritable for i8 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_byte(*self as u8) - } -} - -impl McBufWritable for f32 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_float(*self) - } -} - -impl McBufWritable for f64 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_double(*self) - } -} - -impl McBufWritable for GameType { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - u8::write_into(&self.to_id(), buf) - } -} - -impl McBufWritable for Option { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_byte(GameType::to_optional_id(self) as u8) - } -} - -impl McBufWritable for Option { - default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - if let Some(s) = self { - buf.write_boolean(true)?; - s.write_into(buf)?; - } else { - buf.write_boolean(false)?; - }; - Ok(()) - } -} - -impl McBufWritable for azalea_nbt::Tag { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_nbt(self) - } -} - -impl McBufWritable for Difficulty { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - u8::write_into(&self.id(), buf) - } -} - -impl McBufWritable for Component { - // async fn read_into(buf: &mut impl Read) -> Result - // where - // R: AsyncRead + std::marker::Unpin + std::marker::Send, - // { - // let string = buf.read_utf().await?; - // let json: serde_json::Value = serde_json::from_str(string.as_str()) - // .map_err(|e| "Component isn't valid JSON".to_string())?; - // let component = Component::deserialize(json).map_err(|e| e.to_string())?; - // Ok(component) - // } - fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> { - // component doesn't have serialize implemented yet - todo!() - } -} - -impl McBufWritable for Slot { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - match self { - Slot::Empty => buf.write_byte(0)?, - Slot::Present(i) => { - buf.write_varint(i.id)?; - buf.write_byte(i.count)?; - buf.write_nbt(&i.nbt)?; - } - } - - Ok(()) - } -} - -impl McBufWritable for Uuid { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_uuid(self)?; - - Ok(()) - } -} - -impl McBufWritable for BlockPos { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_long( - (((self.x & 0x3FFFFFF) as i64) << 38) - | (((self.z & 0x3FFFFFF) as i64) << 12) - | ((self.y & 0xFFF) as i64), - ) - } -} - -impl McBufWritable for GlobalPos { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - ResourceLocation::write_into(&self.dimension, buf)?; - BlockPos::write_into(&self.pos, buf)?; - - Ok(()) - } -} - -impl McBufWritable for Direction { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_varint(*self as i32) - } -} - -impl McBufWritable for ChunkSectionPos { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let long = (((self.x & 0x3FFFFF) as i64) << 42) - | (self.y & 0xFFFFF) as i64 - | (((self.z & 0x3FFFFF) as i64) << 20); - long.write_into(buf)?; - Ok(()) - } -} - -impl McBufWritable for SaltSignaturePair { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - self.salt.write_into(buf)?; - self.signature.write_into(buf)?; - Ok(()) - } -} diff --git a/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs b/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs index f1947d09..4dae88a5 100644 --- a/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs @@ -1,10 +1,11 @@ use packet_macros::{GamePacket, McBuf}; use uuid::Uuid; +/// This packet is sent by the server when a player comes into visible range, not when a player joins. #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundAddPlayerPacket { #[var] - pub id: i32, + pub id: u32, pub uuid: Uuid, pub x: f64, pub y: f64, diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs index 0fc0104a..714917b7 100644 --- a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs @@ -1,11 +1,10 @@ +use azalea_core::EntityPos; use packet_macros::{GamePacket, McBuf}; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundMoveEntityPosPacket { #[var] pub entity_id: i32, - pub xa: i16, - pub ya: i16, - pub za: i16, + pub delta: PositionDelta, pub on_ground: bool, } diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs index 5fde1b93..b02a2981 100644 --- a/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs @@ -1,12 +1,12 @@ +use super::clientbound_move_entity_pos_packet::PositionDelta; use packet_macros::{GamePacket, McBuf}; +/// This packet is sent by the server when an entity moves less then 8 blocks. #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundMoveEntityPosRotPacket { #[var] - pub entity_id: i32, - pub xa: i16, - pub ya: i16, - pub za: i16, + pub entity_id: u32, + pub delta: PositionDelta, pub y_rot: i8, pub x_rot: i8, pub on_ground: bool, diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index 10beb309..6e922bb9 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -72,6 +72,22 @@ impl World { Ok(()) } + pub fn move_entity_with_delta(&mut self, entity_id: u32, delta: PositionDelta) -> Result<(), String> { + let entity = self + .entity_storage + .get_mut_by_id(entity_id) + .ok_or_else(|| "Moving entity that doesn't exist".to_string())?; + let old_chunk = ChunkPos::from(entity.pos()); + let new_chunk = ChunkPos::from(&new_pos); + // this is fine because we update the chunk below + entity.unsafe_move(new_pos); + if old_chunk != new_chunk { + self.entity_storage + .update_entity_chunk(entity_id, &old_chunk, &new_chunk); + } + Ok(()) + } + pub fn add_entity(&mut self, entity: Entity) { self.entity_storage.insert(entity); } diff --git a/bot/src/main.rs b/bot/src/main.rs index 546a9244..1cb209e5 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -6,7 +6,7 @@ async fn main() -> Result<(), Box> { println!("Hello, world!"); // let address = "95.111.249.143:10000"; - let address = "localhost:57172"; + let address = "localhost:52722"; // let response = azalea_client::ping::ping_server(&address.try_into().unwrap()) // .await // .unwrap(); -- cgit v1.2.3