mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 06:16:04 +00:00
azalea-buf
This commit is contained in:
parent
c7b0c51274
commit
5ca49e680e
41 changed files with 804 additions and 736 deletions
23
Cargo.lock
generated
23
Cargo.lock
generated
|
@ -89,10 +89,20 @@ dependencies = [
|
||||||
name = "azalea-brigadier"
|
name = "azalea-brigadier"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "azalea-buf"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"buf-macros",
|
||||||
|
"byteorder",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "azalea-chat"
|
name = "azalea-chat"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"azalea-buf",
|
||||||
"azalea-language",
|
"azalea-language",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -117,6 +127,7 @@ dependencies = [
|
||||||
name = "azalea-core"
|
name = "azalea-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"azalea-buf",
|
||||||
"azalea-chat",
|
"azalea-chat",
|
||||||
"azalea-nbt",
|
"azalea-nbt",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
@ -127,6 +138,7 @@ name = "azalea-crypto"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes",
|
"aes",
|
||||||
|
"azalea-buf",
|
||||||
"cfb8",
|
"cfb8",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"rand",
|
"rand",
|
||||||
|
@ -156,6 +168,7 @@ dependencies = [
|
||||||
name = "azalea-nbt"
|
name = "azalea-nbt"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"azalea-buf",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"criterion",
|
"criterion",
|
||||||
"flate2",
|
"flate2",
|
||||||
|
@ -171,6 +184,7 @@ dependencies = [
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"azalea-auth",
|
"azalea-auth",
|
||||||
"azalea-brigadier",
|
"azalea-brigadier",
|
||||||
|
"azalea-buf",
|
||||||
"azalea-chat",
|
"azalea-chat",
|
||||||
"azalea-core",
|
"azalea-core",
|
||||||
"azalea-crypto",
|
"azalea-crypto",
|
||||||
|
@ -250,6 +264,15 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "buf-macros"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.10.0"
|
version = "3.10.0"
|
||||||
|
|
|
@ -13,6 +13,7 @@ members = [
|
||||||
"azalea-language",
|
"azalea-language",
|
||||||
"azalea-block",
|
"azalea-block",
|
||||||
"azalea-entity",
|
"azalea-entity",
|
||||||
|
"azalea-buf",
|
||||||
]
|
]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
|
11
azalea-buf/Cargo.toml
Normal file
11
azalea-buf/Cargo.toml
Normal file
|
@ -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"]}
|
3
azalea-buf/README.md
Normal file
3
azalea-buf/README.md
Normal file
|
@ -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.
|
13
azalea-buf/buf-macros/Cargo.toml
Normal file
13
azalea-buf/buf-macros/Cargo.toml
Normal file
|
@ -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"
|
177
azalea-buf/buf-macros/src/lib.rs
Normal file
177
azalea-buf/buf-macros/src/lib.rs
Normal file
|
@ -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::<Vec<_>>();
|
||||||
|
let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl crate::McBufReadable for #ident {
|
||||||
|
fn read_into(buf: &mut impl std::io::Read) -> Result<Self, String> {
|
||||||
|
#(#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<Self, String>
|
||||||
|
{
|
||||||
|
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::<Vec<_>>();
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
39
azalea-buf/src/definitions.rs
Normal file
39
azalea-buf/src/definitions.rs
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
use buf_macros::McBuf;
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
|
/// A Vec<u8> that isn't prefixed by a VarInt with the size.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
|
pub struct UnsizedByteArray(Vec<u8>);
|
||||||
|
|
||||||
|
impl Deref for UnsizedByteArray {
|
||||||
|
type Target = Vec<u8>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for UnsizedByteArray {
|
||||||
|
fn from(vec: Vec<u8>) -> 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<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,13 @@
|
||||||
//! Utilities for reading and writing for the Minecraft protocol
|
//! Utilities for reading and writing for the Minecraft protocol
|
||||||
|
|
||||||
|
#![feature(min_specialization)]
|
||||||
|
#![feature(arbitrary_enum_discriminant)]
|
||||||
|
|
||||||
mod definitions;
|
mod definitions;
|
||||||
mod read;
|
mod read;
|
||||||
mod write;
|
mod write;
|
||||||
|
|
||||||
pub use definitions::{BitSet, EntityMetadata, ParticleData, UnsizedByteArray};
|
pub use definitions::*;
|
||||||
pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable};
|
pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable};
|
||||||
pub use write::{McBufVarWritable, McBufWritable, Writable};
|
pub use write::{McBufVarWritable, McBufWritable, Writable};
|
||||||
|
|
||||||
|
@ -12,14 +15,9 @@ pub use write::{McBufVarWritable, McBufWritable, Writable};
|
||||||
const MAX_STRING_LENGTH: u16 = 32767;
|
const MAX_STRING_LENGTH: u16 = 32767;
|
||||||
// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144;
|
// 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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use azalea_core::resource_location::ResourceLocation;
|
|
||||||
use std::{collections::HashMap, io::Cursor};
|
use std::{collections::HashMap, io::Cursor};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -180,33 +178,6 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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]
|
#[test]
|
||||||
fn test_long() {
|
fn test_long() {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
|
@ -216,18 +187,4 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(buf.read_long().unwrap(), 123456);
|
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()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,16 +1,9 @@
|
||||||
use super::{UnsizedByteArray, MAX_STRING_LENGTH};
|
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 byteorder::{ReadBytesExt, BE};
|
||||||
use serde::Deserialize;
|
|
||||||
use std::{collections::HashMap, hash::Hash, io::Read};
|
use std::{collections::HashMap, hash::Hash, io::Read};
|
||||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||||
use uuid::Uuid;
|
|
||||||
|
// TODO: get rid of Readable and use McBufReadable everywhere
|
||||||
|
|
||||||
pub trait Readable {
|
pub trait Readable {
|
||||||
fn read_int_id_list(&mut self) -> Result<Vec<i32>, String>;
|
fn read_int_id_list(&mut self) -> Result<Vec<i32>, String>;
|
||||||
|
@ -25,13 +18,10 @@ pub trait Readable {
|
||||||
fn read_byte(&mut self) -> Result<u8, String>;
|
fn read_byte(&mut self) -> Result<u8, String>;
|
||||||
fn read_int(&mut self) -> Result<i32, String>;
|
fn read_int(&mut self) -> Result<i32, String>;
|
||||||
fn read_boolean(&mut self) -> Result<bool, String>;
|
fn read_boolean(&mut self) -> Result<bool, String>;
|
||||||
fn read_nbt(&mut self) -> Result<azalea_nbt::Tag, String>;
|
|
||||||
fn read_long(&mut self) -> Result<i64, String>;
|
fn read_long(&mut self) -> Result<i64, String>;
|
||||||
fn read_resource_location(&mut self) -> Result<ResourceLocation, String>;
|
|
||||||
fn read_short(&mut self) -> Result<i16, String>;
|
fn read_short(&mut self) -> Result<i16, String>;
|
||||||
fn read_float(&mut self) -> Result<f32, String>;
|
fn read_float(&mut self) -> Result<f32, String>;
|
||||||
fn read_double(&mut self) -> Result<f64, String>;
|
fn read_double(&mut self) -> Result<f64, String>;
|
||||||
fn read_uuid(&mut self) -> Result<Uuid, String>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R> Readable for R
|
impl<R> Readable for R
|
||||||
|
@ -160,14 +150,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_nbt(&mut self) -> Result<azalea_nbt::Tag, String> {
|
|
||||||
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<i64, String> {
|
fn read_long(&mut self) -> Result<i64, String> {
|
||||||
match self.read_i64::<BE>() {
|
match self.read_i64::<BE>() {
|
||||||
Ok(r) => Ok(r),
|
Ok(r) => Ok(r),
|
||||||
|
@ -175,13 +157,6 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_resource_location(&mut self) -> Result<ResourceLocation, String> {
|
|
||||||
// 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<i16, String> {
|
fn read_short(&mut self) -> Result<i16, String> {
|
||||||
match self.read_i16::<BE>() {
|
match self.read_i16::<BE>() {
|
||||||
Ok(r) => Ok(r),
|
Ok(r) => Ok(r),
|
||||||
|
@ -202,15 +177,6 @@ where
|
||||||
Err(_) => Err("Error reading double".to_string()),
|
Err(_) => Err("Error reading double".to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_uuid(&mut self) -> Result<Uuid, String> {
|
|
||||||
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
|
// fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67
|
||||||
|
@ -319,12 +285,6 @@ impl McBufReadable for String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl McBufReadable for ResourceLocation {
|
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
|
||||||
buf.read_resource_location()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McBufReadable for u32 {
|
impl McBufReadable for u32 {
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
Readable::read_int(buf).map(|i| i as u32)
|
Readable::read_int(buf).map(|i| i as u32)
|
||||||
|
@ -408,18 +368,6 @@ impl McBufReadable for f64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl McBufReadable for GameType {
|
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
|
||||||
GameType::from_id(buf.read_byte()?)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McBufReadable for Option<GameType> {
|
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
|
||||||
GameType::from_optional_id(buf.read_byte()? as i8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: McBufReadable> McBufReadable for Option<T> {
|
impl<T: McBufReadable> McBufReadable for Option<T> {
|
||||||
default fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
default fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
let present = buf.read_boolean()?;
|
let present = buf.read_boolean()?;
|
||||||
|
@ -430,96 +378,3 @@ impl<T: McBufReadable> McBufReadable for Option<T> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl McBufReadable for azalea_nbt::Tag {
|
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
|
||||||
buf.read_nbt()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McBufReadable for Difficulty {
|
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
|
||||||
Ok(Difficulty::by_id(u8::read_into(buf)?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McBufReadable for Component {
|
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
|
||||||
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<Self, String> {
|
|
||||||
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<Self, String> {
|
|
||||||
buf.read_uuid()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McBufReadable for BlockPos {
|
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
|
||||||
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<Self, String> {
|
|
||||||
Ok(GlobalPos {
|
|
||||||
dimension: ResourceLocation::read_into(buf)?,
|
|
||||||
pos: BlockPos::read_into(buf)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McBufReadable for Direction {
|
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
|
||||||
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<Self, String> {
|
|
||||||
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<Self, String> {
|
|
||||||
let salt = u64::read_into(buf)?;
|
|
||||||
let signature = Vec::<u8>::read_into(buf)?;
|
|
||||||
Ok(SaltSignaturePair { salt, signature })
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +1,8 @@
|
||||||
use super::{UnsizedByteArray, MAX_STRING_LENGTH};
|
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 byteorder::{BigEndian, WriteBytesExt};
|
||||||
use std::{collections::HashMap, io::Write};
|
use std::{collections::HashMap, io::Write};
|
||||||
use uuid::Uuid;
|
|
||||||
|
// TODO: get rid of Writable and use McBufWritable everywhere
|
||||||
|
|
||||||
pub trait Writable: Write {
|
pub trait Writable: Write {
|
||||||
fn write_list<F, T>(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error>
|
fn write_list<F, T>(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error>
|
||||||
|
@ -101,14 +96,6 @@ pub trait Writable: Write {
|
||||||
self.write_byte(if b { 1 } else { 0 })
|
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> {
|
fn write_long(&mut self, n: i64) -> Result<(), std::io::Error> {
|
||||||
WriteBytesExt::write_i64::<BigEndian>(self, n)
|
WriteBytesExt::write_i64::<BigEndian>(self, n)
|
||||||
}
|
}
|
||||||
|
@ -120,25 +107,6 @@ pub trait Writable: Write {
|
||||||
fn write_double(&mut self, n: f64) -> Result<(), std::io::Error> {
|
fn write_double(&mut self, n: f64) -> Result<(), std::io::Error> {
|
||||||
WriteBytesExt::write_f64::<BigEndian>(self, n)
|
WriteBytesExt::write_f64::<BigEndian>(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<W: Write + ?Sized> Writable for W {}
|
impl<W: Write + ?Sized> Writable for W {}
|
||||||
|
@ -199,12 +167,6 @@ impl McBufWritable for String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
impl McBufWritable for u32 {
|
||||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||||
i16::write_into(&(*self as i16), buf)
|
i16::write_into(&(*self as i16), buf)
|
||||||
|
@ -220,7 +182,6 @@ impl McBufVarWritable for u32 {
|
||||||
impl McBufVarWritable for i64 {
|
impl McBufVarWritable for i64 {
|
||||||
fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||||
let mut buffer = [0];
|
let mut buffer = [0];
|
||||||
let mut cnt = 0;
|
|
||||||
let mut value = *self;
|
let mut value = *self;
|
||||||
while value != 0 {
|
while value != 0 {
|
||||||
buffer[0] = (value & 0b0111_1111) as u8;
|
buffer[0] = (value & 0b0111_1111) as u8;
|
||||||
|
@ -228,7 +189,7 @@ impl McBufVarWritable for i64 {
|
||||||
if value != 0 {
|
if value != 0 {
|
||||||
buffer[0] |= 0b1000_0000;
|
buffer[0] |= 0b1000_0000;
|
||||||
}
|
}
|
||||||
cnt += buf.write(&mut buffer)?;
|
buf.write(&mut buffer)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -310,18 +271,6 @@ impl McBufWritable for f64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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<GameType> {
|
|
||||||
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
|
||||||
buf.write_byte(GameType::to_optional_id(self) as u8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: McBufWritable> McBufWritable for Option<T> {
|
impl<T: McBufWritable> McBufWritable for Option<T> {
|
||||||
default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||||
if let Some(s) = self {
|
if let Some(s) = self {
|
||||||
|
@ -333,98 +282,3 @@ impl<T: McBufWritable> McBufWritable for Option<T> {
|
||||||
Ok(())
|
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<Self, String>
|
|
||||||
// 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(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,6 +6,7 @@ version = "0.1.0"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
azalea-buf = {path = "../azalea-buf"}
|
||||||
azalea-language = {path = "../azalea-language"}
|
azalea-language = {path = "../azalea-language"}
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
serde = "^1.0.130"
|
serde = "^1.0.130"
|
||||||
|
|
|
@ -264,3 +264,30 @@ impl<'de> Deserialize<'de> for Component {
|
||||||
Ok(component)
|
Ok(component)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for Component {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
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<Self, String>
|
||||||
|
// 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!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{Account, Player};
|
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_entity::Entity;
|
||||||
use azalea_protocol::{
|
use azalea_protocol::{
|
||||||
connect::{GameConnection, HandshakeConnection},
|
connect::{GameConnection, HandshakeConnection},
|
||||||
|
@ -387,6 +387,13 @@ impl Client {
|
||||||
}
|
}
|
||||||
GamePacket::ClientboundAddPlayerPacket(p) => {
|
GamePacket::ClientboundAddPlayerPacket(p) => {
|
||||||
println!("Got add player packet {:?}", 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) => {
|
GamePacket::ClientboundInitializeBorderPacket(p) => {
|
||||||
println!("Got initialize border packet {:?}", p);
|
println!("Got initialize border packet {:?}", p);
|
||||||
|
@ -406,20 +413,18 @@ impl Client {
|
||||||
GamePacket::ClientboundSetExperiencePacket(p) => {
|
GamePacket::ClientboundSetExperiencePacket(p) => {
|
||||||
println!("Got set experience packet {:?}", p);
|
println!("Got set experience packet {:?}", p);
|
||||||
}
|
}
|
||||||
GamePacket::ClientboundTeleportEntityPacket(_p) => {
|
GamePacket::ClientboundTeleportEntityPacket(p) => {
|
||||||
// println!("Got teleport entity packet {:?}", p);
|
let mut state_lock = state.lock()?;
|
||||||
// let state_lock = state.lock()?;
|
let world = state_lock.world.as_mut().unwrap();
|
||||||
|
|
||||||
// let entity = state_lock
|
world.move_entity(
|
||||||
// .world
|
p.id,
|
||||||
// .unwrap()
|
EntityPos {
|
||||||
// .entity_by_id(p.id)
|
x: p.x,
|
||||||
// .ok_or("Teleporting entity that doesn't exist.".to_string())?;
|
y: p.y,
|
||||||
// state_lock
|
z: p.z,
|
||||||
// .world
|
},
|
||||||
// .as_mut()
|
)?;
|
||||||
// .expect("World doesn't exist! We should've gotten a login packet by now.")
|
|
||||||
// .move_entity(&mut entity, new_pos)
|
|
||||||
}
|
}
|
||||||
GamePacket::ClientboundUpdateAdvancementsPacket(p) => {
|
GamePacket::ClientboundUpdateAdvancementsPacket(p) => {
|
||||||
println!("Got update advancements packet {:?}", p);
|
println!("Got update advancements packet {:?}", p);
|
||||||
|
@ -427,11 +432,21 @@ impl Client {
|
||||||
GamePacket::ClientboundRotateHeadPacket(_p) => {
|
GamePacket::ClientboundRotateHeadPacket(_p) => {
|
||||||
// println!("Got rotate head packet {:?}", p);
|
// println!("Got rotate head packet {:?}", p);
|
||||||
}
|
}
|
||||||
GamePacket::ClientboundMoveEntityPosPacket(_p) => {
|
GamePacket::ClientboundMoveEntityPosPacket(p) => {
|
||||||
// println!("Got move entity pos packet {:?}", p);
|
// println!("Got move entity pos packet {:?}", p);
|
||||||
}
|
}
|
||||||
GamePacket::ClientboundMoveEntityPosRotPacket(_p) => {
|
GamePacket::ClientboundMoveEntityPosRotPacket(p) => {
|
||||||
// println!("Got move entity pos rot packet {:?}", 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) => {
|
GamePacket::ClientboundMoveEntityRotPacket(p) => {
|
||||||
println!("Got move entity rot packet {:?}", p);
|
println!("Got move entity rot packet {:?}", p);
|
||||||
|
|
|
@ -6,6 +6,7 @@ version = "0.1.0"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
azalea-buf = {path = "../azalea-buf"}
|
||||||
azalea-chat = {path = "../azalea-chat"}
|
azalea-chat = {path = "../azalea-chat"}
|
||||||
azalea-nbt = {path = "../azalea-nbt"}
|
azalea-nbt = {path = "../azalea-nbt"}
|
||||||
uuid = "^1.1.2"
|
uuid = "^1.1.2"
|
||||||
|
|
26
azalea-core/src/delta.rs
Normal file
26
azalea-core/src/delta.rs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -61,6 +61,18 @@ impl Difficulty {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for Difficulty {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, McBuf)]
|
||||||
pub enum Direction {
|
pub enum Direction {
|
||||||
Down = 0,
|
Down = 0,
|
||||||
Up = 1,
|
Up = 1,
|
||||||
|
|
|
@ -71,3 +71,27 @@ impl GameType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for GameType {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
GameType::from_id(buf.read_byte()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for Option<GameType> {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
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<GameType> {
|
||||||
|
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
|
||||||
|
buf.write_byte(GameType::to_optional_id(self) as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -15,3 +15,9 @@ pub use position::*;
|
||||||
|
|
||||||
mod direction;
|
mod direction;
|
||||||
pub use direction::Direction;
|
pub use direction::Direction;
|
||||||
|
|
||||||
|
mod delta;
|
||||||
|
pub use delta::*;
|
||||||
|
|
||||||
|
mod particle;
|
||||||
|
pub use particle::*;
|
||||||
|
|
|
@ -1,186 +1,3 @@
|
||||||
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<u8> that isn't prefixed by a VarInt with the size.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
||||||
pub struct UnsizedByteArray(Vec<u8>);
|
|
||||||
|
|
||||||
impl Deref for UnsizedByteArray {
|
|
||||||
type Target = Vec<u8>;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<u8>> for UnsizedByteArray {
|
|
||||||
fn from(vec: Vec<u8>) -> 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<u64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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<EntityDataItem>;
|
|
||||||
|
|
||||||
#[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<EntityDataItem> {
|
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
|
||||||
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<EntityDataItem> {
|
|
||||||
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<Component>),
|
|
||||||
ItemStack(Slot),
|
|
||||||
Boolean(bool),
|
|
||||||
Rotations { x: f32, y: f32, z: f32 },
|
|
||||||
BlockPos(BlockPos),
|
|
||||||
OptionalBlockPos(Option<BlockPos>),
|
|
||||||
Direction(Direction),
|
|
||||||
OptionalUuid(Option<Uuid>),
|
|
||||||
// 0 for absent (implies air); otherwise, a block state ID as per the global palette
|
|
||||||
// this is a varint
|
|
||||||
OptionalBlockState(Option<i32>),
|
|
||||||
CompoundTag(azalea_nbt::Tag),
|
|
||||||
Particle(Particle),
|
|
||||||
VillagerData(VillagerData),
|
|
||||||
// 0 for absent; 1 + actual value otherwise. Used for entity IDs.
|
|
||||||
OptionalUnsignedInt(Option<u32>),
|
|
||||||
Pose(Pose),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl McBufReadable for EntityDataValue {
|
|
||||||
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
|
||||||
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::<Component>::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::<BlockPos>::read_into(buf)?),
|
|
||||||
11 => EntityDataValue::Direction(Direction::read_into(buf)?),
|
|
||||||
12 => EntityDataValue::OptionalUuid(Option::<Uuid>::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)]
|
#[derive(Debug, Clone, McBuf)]
|
||||||
pub struct Particle {
|
pub struct Particle {
|
|
@ -170,6 +170,65 @@ impl From<&EntityPos> for ChunkPos {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for BlockPos {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
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<Self, String> {
|
||||||
|
Ok(GlobalPos {
|
||||||
|
dimension: ResourceLocation::read_into(buf)?,
|
||||||
|
pos: BlockPos::read_into(buf)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for ChunkSectionPos {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -42,6 +42,18 @@ impl std::fmt::Debug for ResourceLocation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for ResourceLocation {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -70,4 +82,18 @@ mod tests {
|
||||||
assert_eq!(r.namespace, "azalea");
|
assert_eq!(r.namespace, "azalea");
|
||||||
assert_eq!(r.path, "");
|
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()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,28 @@ impl SerializableUuid for Uuid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for Uuid {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
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)]
|
#[cfg(tests)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -12,3 +12,31 @@ pub struct SlotData {
|
||||||
pub count: u8,
|
pub count: u8,
|
||||||
pub nbt: azalea_nbt::Tag,
|
pub nbt: azalea_nbt::Tag,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for Slot {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aes = "0.8.1"
|
aes = "0.8.1"
|
||||||
|
azalea-buf = {path = "../azalea-buf"}
|
||||||
cfb8 = "0.8.1"
|
cfb8 = "0.8.1"
|
||||||
num-bigint = "^0.4.3"
|
num-bigint = "^0.4.3"
|
||||||
rand = {version = "^0.8.4", features = ["getrandom"]}
|
rand = {version = "^0.8.4", features = ["getrandom"]}
|
||||||
|
|
|
@ -79,6 +79,8 @@ pub fn decrypt_packet(cipher: &mut Aes128CfbDec, packet: &mut [u8]) {
|
||||||
cipher.decrypt_blocks_inout_mut(chunks);
|
cipher.decrypt_blocks_inout_mut(chunks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -3,3 +3,19 @@ pub struct SaltSignaturePair {
|
||||||
pub salt: u64,
|
pub salt: u64,
|
||||||
pub signature: Vec<u8>,
|
pub signature: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for SaltSignaturePair {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
let salt = u64::read_into(buf)?;
|
||||||
|
let signature = Vec::<u8>::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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
136
azalea-entity/src/data.rs
Normal file
136
azalea-entity/src/data.rs
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
pub type EntityMetadata = Vec<EntityDataItem>;
|
||||||
|
|
||||||
|
#[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<EntityDataItem> {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
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<EntityDataItem> {
|
||||||
|
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<Component>),
|
||||||
|
ItemStack(Slot),
|
||||||
|
Boolean(bool),
|
||||||
|
Rotations { x: f32, y: f32, z: f32 },
|
||||||
|
BlockPos(BlockPos),
|
||||||
|
OptionalBlockPos(Option<BlockPos>),
|
||||||
|
Direction(Direction),
|
||||||
|
OptionalUuid(Option<Uuid>),
|
||||||
|
// 0 for absent (implies air); otherwise, a block state ID as per the global palette
|
||||||
|
// this is a varint
|
||||||
|
OptionalBlockState(Option<i32>),
|
||||||
|
CompoundTag(azalea_nbt::Tag),
|
||||||
|
Particle(Particle),
|
||||||
|
VillagerData(VillagerData),
|
||||||
|
// 0 for absent; 1 + actual value otherwise. Used for entity IDs.
|
||||||
|
OptionalUnsignedInt(Option<u32>),
|
||||||
|
Pose(Pose),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for EntityDataValue {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
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::<Component>::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::<BlockPos>::read_into(buf)?),
|
||||||
|
11 => EntityDataValue::Direction(Direction::read_into(buf)?),
|
||||||
|
12 => EntityDataValue::OptionalUuid(Option::<Uuid>::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,
|
||||||
|
}
|
|
@ -1,6 +1,11 @@
|
||||||
|
mod data;
|
||||||
|
|
||||||
use azalea_core::EntityPos;
|
use azalea_core::EntityPos;
|
||||||
#[cfg(feature = "protocol")]
|
#[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;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[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)]
|
// #[cfg(test)]
|
||||||
// mod tests {
|
// mod tests {
|
||||||
// #[test]
|
// #[test]
|
||||||
|
|
|
@ -6,6 +6,7 @@ version = "0.1.0"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
azalea-buf = {path = "../azalea-buf"}
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
flate2 = "1.0.23"
|
flate2 = "1.0.23"
|
||||||
num-derive = "^0.3.3"
|
num-derive = "^0.3.3"
|
||||||
|
|
|
@ -136,3 +136,13 @@ impl Tag {
|
||||||
Tag::read(&mut gz)
|
Tag::read(&mut gz)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl McBufReadable for Tag {
|
||||||
|
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
|
||||||
|
match Tag::read(self) {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
// Err(e) => Err(e.to_string()),
|
||||||
|
Err(e) => Err(e.to_string()).unwrap(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -217,3 +217,10 @@ impl Tag {
|
||||||
self.write(&mut encoder)
|
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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,3 +5,36 @@ mod tag;
|
||||||
|
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
pub use tag::Tag;
|
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()),
|
||||||
|
)])),
|
||||||
|
)]))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,12 +6,13 @@ version = "0.1.0"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[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"
|
async-recursion = "^0.3.2"
|
||||||
azalea-auth = {path = "../azalea-auth"}
|
azalea-auth = {path = "../azalea-auth"}
|
||||||
azalea-brigadier = {path = "../azalea-brigadier"}
|
azalea-brigadier = {path = "../azalea-brigadier"}
|
||||||
|
azalea-buf = {path = "../azalea-buf"}
|
||||||
azalea-chat = {path = "../azalea-chat"}
|
azalea-chat = {path = "../azalea-chat"}
|
||||||
azalea-core = {path = "../azalea-core"}
|
azalea-core = {path = "../azalea-core", optional = true}
|
||||||
azalea-crypto = {path = "../azalea-crypto"}
|
azalea-crypto = {path = "../azalea-crypto"}
|
||||||
azalea-nbt = {path = "../azalea-nbt"}
|
azalea-nbt = {path = "../azalea-nbt"}
|
||||||
byteorder = "^1.4.3"
|
byteorder = "^1.4.3"
|
||||||
|
@ -27,3 +28,8 @@ tokio = {version = "^1.19.2", features = ["io-util", "net", "macros"]}
|
||||||
tokio-util = "^0.6.9"
|
tokio-util = "^0.6.9"
|
||||||
trust-dns-resolver = "^0.20.3"
|
trust-dns-resolver = "^0.20.3"
|
||||||
uuid = "^1.1.2"
|
uuid = "^1.1.2"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
connecting = []
|
||||||
|
default = ["packets"]
|
||||||
|
packets = ["connecting", "dep:async-compression", "dep:azalea-core"]
|
||||||
|
|
|
@ -6,180 +6,6 @@ use syn::{
|
||||||
parse_macro_input, Data, DeriveInput, FieldsNamed, Ident, LitInt, Token,
|
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::<Vec<_>>();
|
|
||||||
let read_field_names = named.iter().map(|f| &f.ident).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
quote! {
|
|
||||||
impl crate::mc_buf::McBufReadable for #ident {
|
|
||||||
fn read_into(buf: &mut impl std::io::Read) -> Result<Self, String> {
|
|
||||||
#(#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<Self, String>
|
|
||||||
{
|
|
||||||
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::<Vec<_>>();
|
|
||||||
|
|
||||||
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 {
|
fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> TokenStream {
|
||||||
let DeriveInput { ident, data, .. } = parse_macro_input!(input);
|
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"),
|
_ => 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! {
|
let contents = quote! {
|
||||||
impl #ident {
|
impl #ident {
|
||||||
pub fn get(self) -> #state {
|
pub fn get(self) -> #state {
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
//! This lib is responsible for parsing Minecraft packets.
|
//! This lib is responsible for parsing Minecraft packets.
|
||||||
|
|
||||||
#![feature(min_specialization)]
|
|
||||||
#![feature(arbitrary_enum_discriminant)]
|
|
||||||
|
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[cfg(feature = "connecting")]
|
||||||
pub mod connect;
|
pub mod connect;
|
||||||
pub mod mc_buf;
|
pub mod mc_buf;
|
||||||
|
#[cfg(feature = "packets")]
|
||||||
pub mod packets;
|
pub mod packets;
|
||||||
pub mod read;
|
pub mod read;
|
||||||
pub mod resolver;
|
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<dyn std::error::Error>> {
|
pub async fn connect(address: ServerAddress) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let resolved_address = resolver::resolve_address(&address).await;
|
let resolved_address = resolver::resolve_address(&address).await;
|
||||||
println!("Resolved address: {:?}", resolved_address);
|
println!("Resolved address: {:?}", resolved_address);
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use packet_macros::{GamePacket, McBuf};
|
use packet_macros::{GamePacket, McBuf};
|
||||||
use uuid::Uuid;
|
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)]
|
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||||
pub struct ClientboundAddPlayerPacket {
|
pub struct ClientboundAddPlayerPacket {
|
||||||
#[var]
|
#[var]
|
||||||
pub id: i32,
|
pub id: u32,
|
||||||
pub uuid: Uuid,
|
pub uuid: Uuid,
|
||||||
pub x: f64,
|
pub x: f64,
|
||||||
pub y: f64,
|
pub y: f64,
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
|
use azalea_core::EntityPos;
|
||||||
use packet_macros::{GamePacket, McBuf};
|
use packet_macros::{GamePacket, McBuf};
|
||||||
|
|
||||||
#[derive(Clone, Debug, McBuf, GamePacket)]
|
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||||
pub struct ClientboundMoveEntityPosPacket {
|
pub struct ClientboundMoveEntityPosPacket {
|
||||||
#[var]
|
#[var]
|
||||||
pub entity_id: i32,
|
pub entity_id: i32,
|
||||||
pub xa: i16,
|
pub delta: PositionDelta,
|
||||||
pub ya: i16,
|
|
||||||
pub za: i16,
|
|
||||||
pub on_ground: bool,
|
pub on_ground: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
|
use super::clientbound_move_entity_pos_packet::PositionDelta;
|
||||||
use packet_macros::{GamePacket, McBuf};
|
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)]
|
#[derive(Clone, Debug, McBuf, GamePacket)]
|
||||||
pub struct ClientboundMoveEntityPosRotPacket {
|
pub struct ClientboundMoveEntityPosRotPacket {
|
||||||
#[var]
|
#[var]
|
||||||
pub entity_id: i32,
|
pub entity_id: u32,
|
||||||
pub xa: i16,
|
pub delta: PositionDelta,
|
||||||
pub ya: i16,
|
|
||||||
pub za: i16,
|
|
||||||
pub y_rot: i8,
|
pub y_rot: i8,
|
||||||
pub x_rot: i8,
|
pub x_rot: i8,
|
||||||
pub on_ground: bool,
|
pub on_ground: bool,
|
||||||
|
|
|
@ -72,6 +72,22 @@ impl World {
|
||||||
Ok(())
|
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) {
|
pub fn add_entity(&mut self, entity: Entity) {
|
||||||
self.entity_storage.insert(entity);
|
self.entity_storage.insert(entity);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
|
|
||||||
// let address = "95.111.249.143:10000";
|
// 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())
|
// let response = azalea_client::ping::ping_server(&address.try_into().unwrap())
|
||||||
// .await
|
// .await
|
||||||
// .unwrap();
|
// .unwrap();
|
||||||
|
|
Loading…
Add table
Reference in a new issue