1
2
Fork 0
mirror of https://github.com/mat-1/azalea.git synced 2025-08-02 14:26:04 +00:00

Merge pull request #6 from mat-1/chunk-decoding

Chunk decoding
This commit is contained in:
mat 2022-05-15 01:46:11 +00:00 committed by GitHub
commit d0ac62d852
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
85 changed files with 1260 additions and 252 deletions

1
.gitignore vendored Executable file → Normal file
View file

@ -4,6 +4,7 @@ flamegraph.svg
perf.data perf.data
perf.data.old perf.data.old
code-generator/Burger code-generator/Burger
code-generator/client.jar code-generator/client.jar
code-generator/burger.json code-generator/burger.json

11
Cargo.lock generated
View file

@ -100,6 +100,7 @@ dependencies = [
"azalea-core", "azalea-core",
"azalea-crypto", "azalea-crypto",
"azalea-protocol", "azalea-protocol",
"azalea-world",
"tokio", "tokio",
] ]
@ -171,6 +172,15 @@ dependencies = [
"uuid", "uuid",
] ]
[[package]]
name = "azalea-world"
version = "0.1.0"
dependencies = [
"azalea-core",
"azalea-nbt",
"azalea-protocol",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -191,6 +201,7 @@ name = "bot"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"azalea-client", "azalea-client",
"azalea-core",
"azalea-protocol", "azalea-protocol",
"tokio", "tokio",
] ]

1
Cargo.toml Executable file → Normal file
View file

@ -9,6 +9,7 @@ members = [
"azalea-nbt", "azalea-nbt",
"azalea-brigadier", "azalea-brigadier",
"azalea-crypto", "azalea-crypto",
"azalea-world",
"azalea-language", "azalea-language",
] ]

8
README.md Executable file → Normal file
View file

@ -1,13 +1,17 @@
# Azalea # Azalea
A Rust crate for creating Minecraft bots.
<p align="center"> <p align="center">
<img src="https://cdn.matdoes.dev/images/flowering_azalea.webp" alt="Azalea" height="200"> <img src="https://cdn.matdoes.dev/images/flowering_azalea.webp" alt="Azalea" height="200">
</p> </p>
A Rust crate for creating Minecraft bots.
I named this Azalea because it sounds like a cool word and this is a cool library. This project was heavily inspired by PrismarineJS. I named this Azalea because it sounds like a cool word and this is a cool library. This project was heavily inspired by PrismarineJS.
## Why
I wanted a fun excuse to do something cool with Rust, and I also felt like I could do better than [Mineflayer](https://github.com/prismarinejs/mineflayer) in some areas.
## Goals ## Goals
- Do everything a vanilla client can do. - Do everything a vanilla client can do.

View file

@ -10,4 +10,5 @@ azalea-auth = {path = "../azalea-auth"}
azalea-core = {path = "../azalea-core"} azalea-core = {path = "../azalea-core"}
azalea-crypto = {path = "../azalea-crypto"} azalea-crypto = {path = "../azalea-crypto"}
azalea-protocol = {path = "../azalea-protocol"} azalea-protocol = {path = "../azalea-protocol"}
azalea-world = {path = "../azalea-world"}
tokio = {version = "1.18.0", features = ["sync"]} tokio = {version = "1.18.0", features = ["sync"]}

View file

@ -1,5 +1,5 @@
use crate::Player; use crate::Player;
use azalea_core::resource_location::ResourceLocation; use azalea_core::{resource_location::ResourceLocation, ChunkPos};
use azalea_protocol::{ use azalea_protocol::{
connect::{GameConnection, HandshakeConnection}, connect::{GameConnection, HandshakeConnection},
packets::{ packets::{
@ -17,7 +17,8 @@ use azalea_protocol::{
}, },
resolver, ServerAddress, resolver, ServerAddress,
}; };
use std::sync::Arc; use azalea_world::{ChunkStorage, World};
use std::{fmt::Debug, sync::Arc};
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
use tokio::sync::Mutex; use tokio::sync::Mutex;
@ -30,15 +31,15 @@ pub struct Account {
#[derive(Default)] #[derive(Default)]
pub struct ClientState { pub struct ClientState {
// placeholder
pub player: Player, pub player: Player,
pub world: Option<World>,
} }
/// A player that you can control that is currently in a Minecraft server. /// A player that you can control that is currently in a Minecraft server.
pub struct Client { pub struct Client {
event_receiver: UnboundedReceiver<Event>, event_receiver: UnboundedReceiver<Event>,
conn: Arc<Mutex<GameConnection>>, pub conn: Arc<Mutex<GameConnection>>,
state: Arc<Mutex<ClientState>>, pub state: Arc<Mutex<ClientState>>,
// game_loop // game_loop
} }
@ -137,10 +138,9 @@ impl Client {
// just start up the game loop and we're ready! // just start up the game loop and we're ready!
// tokio::spawn(Self::game_loop(conn, tx, handler, state)) // tokio::spawn(Self::game_loop(conn, tx, handler, state))
let game_loop_conn = conn.clone();
let game_loop_state = client.state.clone(); let game_loop_state = client.state.clone();
tokio::spawn(Self::game_loop(game_loop_conn, tx, game_loop_state)); tokio::spawn(Self::game_loop(conn, tx, game_loop_state));
Ok(client) Ok(client)
} }
@ -178,7 +178,37 @@ impl Client {
GamePacket::ClientboundLoginPacket(p) => { GamePacket::ClientboundLoginPacket(p) => {
println!("Got login packet {:?}", p); println!("Got login packet {:?}", p);
state.lock().await.player.entity.id = p.player_id; let mut state = state.lock().await;
state.player.entity.id = p.player_id;
let dimension_type = p
.dimension_type
.as_compound()
.expect("Dimension type is not compound")
.get("")
.expect("No \"\" tag")
.as_compound()
.expect("\"\" tag is not compound");
let height = (*dimension_type
.get("height")
.expect("No height tag")
.as_int()
.expect("height tag is not int"))
.try_into()
.expect("height is not a u32");
let min_y = (*dimension_type
.get("min_y")
.expect("No min_y tag")
.as_int()
.expect("min_y tag is not int"))
.try_into()
.expect("min_y is not an i32");
state.world = Some(World {
height,
min_y,
storage: ChunkStorage::new(16),
});
conn.lock() conn.lock()
.await .await
.write( .write(
@ -202,7 +232,7 @@ impl Client {
GamePacket::ClientboundChangeDifficultyPacket(p) => { GamePacket::ClientboundChangeDifficultyPacket(p) => {
println!("Got difficulty packet {:?}", p); println!("Got difficulty packet {:?}", p);
} }
GamePacket::ClientboundDeclareCommandsPacket(p) => { GamePacket::ClientboundDeclareCommandsPacket(_p) => {
println!("Got declare commands packet"); println!("Got declare commands packet");
} }
GamePacket::ClientboundPlayerAbilitiesPacket(p) => { GamePacket::ClientboundPlayerAbilitiesPacket(p) => {
@ -211,19 +241,19 @@ impl Client {
GamePacket::ClientboundSetCarriedItemPacket(p) => { GamePacket::ClientboundSetCarriedItemPacket(p) => {
println!("Got set carried item packet {:?}", p); println!("Got set carried item packet {:?}", p);
} }
GamePacket::ClientboundUpdateTagsPacket(p) => { GamePacket::ClientboundUpdateTagsPacket(_p) => {
println!("Got update tags packet"); println!("Got update tags packet");
} }
GamePacket::ClientboundDisconnectPacket(p) => { GamePacket::ClientboundDisconnectPacket(p) => {
println!("Got disconnect packet {:?}", p); println!("Got disconnect packet {:?}", p);
} }
GamePacket::ClientboundUpdateRecipesPacket(p) => { GamePacket::ClientboundUpdateRecipesPacket(_p) => {
println!("Got update recipes packet"); println!("Got update recipes packet");
} }
GamePacket::ClientboundEntityEventPacket(p) => { GamePacket::ClientboundEntityEventPacket(p) => {
// println!("Got entity event packet {:?}", p); // println!("Got entity event packet {:?}", p);
} }
GamePacket::ClientboundRecipePacket(p) => { GamePacket::ClientboundRecipePacket(_p) => {
println!("Got recipe packet"); println!("Got recipe packet");
} }
GamePacket::ClientboundPlayerPositionPacket(p) => { GamePacket::ClientboundPlayerPositionPacket(p) => {
@ -235,9 +265,27 @@ impl Client {
} }
GamePacket::ClientboundSetChunkCacheCenterPacket(p) => { GamePacket::ClientboundSetChunkCacheCenterPacket(p) => {
println!("Got chunk cache center packet {:?}", p); println!("Got chunk cache center packet {:?}", p);
state
.lock()
.await
.world
.as_mut()
.unwrap()
.update_view_center(&ChunkPos::new(p.x, p.z));
} }
GamePacket::ClientboundLevelChunkWithLightPacket(p) => { GamePacket::ClientboundLevelChunkWithLightPacket(p) => {
println!("Got chunk with light packet {} {}", p.x, p.z); println!("Got chunk with light packet {} {}", p.x, p.z);
let pos = ChunkPos::new(p.x, p.z);
// let chunk = Chunk::read_with_world_height(&mut p.chunk_data);
// println("chunk {:?}")
state
.lock()
.await
.world
.as_mut()
.expect("World doesn't exist! We should've gotten a login packet by now.")
.replace_with_packet_data(&pos, &mut p.chunk_data.data.as_slice())
.unwrap();
} }
GamePacket::ClientboundLightUpdatePacket(p) => { GamePacket::ClientboundLightUpdatePacket(p) => {
println!("Got light update packet {:?}", p); println!("Got light update packet {:?}", p);
@ -321,10 +369,21 @@ impl Client {
} }
GamePacket::ClientboundBlockUpdatePacket(p) => { GamePacket::ClientboundBlockUpdatePacket(p) => {
println!("Got block update packet {:?}", p); println!("Got block update packet {:?}", p);
// TODO: update world
} }
GamePacket::ClientboundAnimatePacket(p) => { GamePacket::ClientboundAnimatePacket(p) => {
println!("Got animate packet {:?}", p); println!("Got animate packet {:?}", p);
} }
GamePacket::ClientboundSectionBlocksUpdatePacket(p) => {
println!("Got section blocks update packet {:?}", p);
// TODO: update world
}
GamePacket::ClientboundGameEventPacket(p) => {
println!("Got game event packet {:?}", p);
}
GamePacket::ClientboundLevelParticlesPacket(p) => {
println!("Got level particles packet {:?}", p);
}
_ => panic!("Unexpected packet {:?}", packet), _ => panic!("Unexpected packet {:?}", packet),
} }
} }

View file

@ -1,6 +0,0 @@
#[derive(Clone, Copy, Debug)]
pub struct BlockPos {
pub x: i32,
pub y: i32,
pub z: i32,
}

View file

@ -83,7 +83,6 @@ mod tests {
assert_eq!(1, Difficulty::EASY.id()); assert_eq!(1, Difficulty::EASY.id());
assert_eq!(2, Difficulty::NORMAL.id()); assert_eq!(2, Difficulty::NORMAL.id());
assert_eq!(3, Difficulty::HARD.id()); assert_eq!(3, Difficulty::HARD.id());
assert_eq!(4, Difficulty::PEACEFUL.id());
} }
#[test] #[test]

View file

@ -1,5 +1,7 @@
//! Random miscellaneous things like UUIDs that don't deserve their own crate. //! Random miscellaneous things like UUIDs that don't deserve their own crate.
#![feature(int_roundings)]
pub mod difficulty; pub mod difficulty;
pub mod game_type; pub mod game_type;
pub mod resource_location; pub mod resource_location;
@ -8,8 +10,8 @@ pub mod serializable_uuid;
mod slot; mod slot;
pub use slot::{Slot, SlotData}; pub use slot::{Slot, SlotData};
mod block_pos; mod position;
pub use block_pos::BlockPos; pub use position::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos, ChunkSectionPos};
mod direction; mod direction;
pub use direction::Direction; pub use direction::Direction;

157
azalea-core/src/position.rs Normal file
View file

@ -0,0 +1,157 @@
use std::ops::Rem;
#[derive(Clone, Copy, Debug, Default)]
pub struct BlockPos {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl BlockPos {
pub fn new(x: i32, y: i32, z: i32) -> Self {
BlockPos { x, y, z }
}
}
impl Rem<i32> for BlockPos {
type Output = Self;
fn rem(self, rhs: i32) -> Self {
BlockPos {
x: self.x % rhs,
y: self.y % rhs,
z: self.z % rhs,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct ChunkPos {
pub x: i32,
pub z: i32,
}
impl ChunkPos {
pub fn new(x: i32, z: i32) -> Self {
ChunkPos { x, z }
}
}
impl From<&BlockPos> for ChunkPos {
fn from(pos: &BlockPos) -> Self {
ChunkPos {
x: pos.x.div_floor(16),
z: pos.z.div_floor(16),
}
}
}
/// The coordinates of a chunk section in the world.
#[derive(Clone, Copy, Debug, Default)]
pub struct ChunkSectionPos {
pub x: i32,
pub y: i32,
pub z: i32,
}
impl ChunkSectionPos {
pub fn new(x: i32, y: i32, z: i32) -> Self {
ChunkSectionPos { x, y, z }
}
}
impl From<BlockPos> for ChunkSectionPos {
fn from(pos: BlockPos) -> Self {
ChunkSectionPos {
x: pos.x.div_floor(16),
y: pos.y.div_floor(16),
z: pos.z.div_floor(16),
}
}
}
impl From<ChunkSectionPos> for ChunkPos {
fn from(pos: ChunkSectionPos) -> Self {
ChunkPos { x: pos.x, z: pos.z }
}
}
/// The coordinates of a block inside a chunk.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct ChunkBlockPos {
pub x: u8,
pub y: i32,
pub z: u8,
}
impl ChunkBlockPos {
pub fn new(x: u8, y: i32, z: u8) -> Self {
ChunkBlockPos { x, y, z }
}
}
impl From<&BlockPos> for ChunkBlockPos {
fn from(pos: &BlockPos) -> Self {
ChunkBlockPos {
x: pos.x.rem_euclid(16).abs() as u8,
y: pos.y,
z: pos.z.rem_euclid(16).abs() as u8,
}
}
}
/// The coordinates of a block inside a chunk section.
#[derive(Clone, Copy, Debug, Default)]
pub struct ChunkSectionBlockPos {
/// A number between 0 and 16.
pub x: u8,
/// A number between 0 and 16.
pub y: u8,
/// A number between 0 and 16.
pub z: u8,
}
impl ChunkSectionBlockPos {
pub fn new(x: u8, y: u8, z: u8) -> Self {
ChunkSectionBlockPos { x, y, z }
}
}
impl From<&BlockPos> for ChunkSectionBlockPos {
fn from(pos: &BlockPos) -> Self {
ChunkSectionBlockPos {
x: pos.x.rem(16).abs() as u8,
y: pos.y.rem(16).abs() as u8,
z: pos.z.rem(16).abs() as u8,
}
}
}
impl From<&ChunkBlockPos> for ChunkSectionBlockPos {
fn from(pos: &ChunkBlockPos) -> Self {
ChunkSectionBlockPos {
x: pos.x,
y: pos.y.rem(16).abs() as u8,
z: pos.z,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_block_pos_to_chunk_pos() {
let block_pos = BlockPos::new(5, 78, -2);
let chunk_pos = ChunkPos::from(&block_pos);
assert_eq!(chunk_pos, ChunkPos::new(0, -1));
}
#[test]
fn test_from_block_pos_to_chunk_block_pos() {
let block_pos = BlockPos::new(5, 78, -2);
let chunk_block_pos = ChunkBlockPos::from(&block_pos);
assert_eq!(chunk_block_pos, ChunkBlockPos::new(5, 78, 14));
}
}

View file

@ -1,9 +1,6 @@
use aes::cipher::inout::InOutBuf; use aes::cipher::inout::InOutBuf;
use aes::cipher::BlockEncrypt;
use aes::{ use aes::{
cipher::{ cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit},
generic_array::GenericArray, AsyncStreamCipher, BlockDecryptMut, BlockEncryptMut, KeyIvInit,
},
Aes128, Aes128,
}; };
use rand::{rngs::OsRng, RngCore}; use rand::{rngs::OsRng, RngCore};
@ -15,7 +12,7 @@ fn generate_secret_key() -> [u8; 16] {
key key
} }
fn digest_data(server_id: &[u8], public_key: &[u8], private_key: &[u8]) -> Vec<u8> { pub fn digest_data(server_id: &[u8], public_key: &[u8], private_key: &[u8]) -> Vec<u8> {
let mut digest = Sha1::new(); let mut digest = Sha1::new();
digest.update(server_id); digest.update(server_id);
digest.update(public_key); digest.update(public_key);
@ -23,7 +20,7 @@ fn digest_data(server_id: &[u8], public_key: &[u8], private_key: &[u8]) -> Vec<u
digest.finalize().to_vec() digest.finalize().to_vec()
} }
fn hex_digest(digest: &[u8]) -> String { pub fn hex_digest(digest: &[u8]) -> String {
// Note that the Sha1.hexdigest() method used by minecraft is non standard. // Note that the Sha1.hexdigest() method used by minecraft is non standard.
// It doesn't match the digest method found in most programming languages // It doesn't match the digest method found in most programming languages
// and libraries. It works by treating the sha1 output bytes as one large // and libraries. It works by treating the sha1 output bytes as one large
@ -48,9 +45,8 @@ pub fn encrypt(public_key: &[u8], nonce: &[u8]) -> Result<EncryptResult, String>
// this.keybytes = Crypt.encryptUsingKey(publicKey, secretKey.getEncoded()); // this.keybytes = Crypt.encryptUsingKey(publicKey, secretKey.getEncoded());
// this.nonce = Crypt.encryptUsingKey(publicKey, arrby); // this.nonce = Crypt.encryptUsingKey(publicKey, arrby);
let encrypted_public_key: Vec<u8> = let encrypted_public_key: Vec<u8> = rsa_public_encrypt_pkcs1::encrypt(public_key, &secret_key)?;
rsa_public_encrypt_pkcs1::encrypt(&public_key, &secret_key)?; let encrypted_nonce: Vec<u8> = rsa_public_encrypt_pkcs1::encrypt(public_key, nonce)?;
let encrypted_nonce: Vec<u8> = rsa_public_encrypt_pkcs1::encrypt(&public_key, &nonce)?;
Ok(EncryptResult { Ok(EncryptResult {
secret_key, secret_key,

View file

@ -1,6 +1,7 @@
use lazy_static::lazy_static; use lazy_static::lazy_static;
use std::collections::HashMap; use std::io::Read;
use std::path::Path;
use std::{collections::HashMap, fs::File};
// use tokio::fs::File; // use tokio::fs::File;
// pub struct Language { // pub struct Language {
@ -29,10 +30,26 @@ use std::collections::HashMap;
// The code above is kept in case I come up with a better solution // The code above is kept in case I come up with a better solution
lazy_static! { lazy_static! {
pub static ref STORAGE: HashMap<String, String> = pub static ref STORAGE: HashMap<String, String> = serde_json::from_str(&{
serde_json::from_str(include_str!("en_us.json")).unwrap(); let src_dir = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/src/en_us.json"));
let mut file = File::open(src_dir).unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
contents
})
.unwrap();
} }
pub fn get(key: &str) -> Option<&str> { pub fn get(key: &str) -> Option<&str> {
STORAGE.get(key).map(|s| s.as_str()) STORAGE.get(key).map(|s| s.as_str())
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get() {
assert_eq!(get("translation.test.none"), Some("Hello, world!"));
}
}

View file

@ -98,7 +98,7 @@ fn write_compound(
if end_tag { if end_tag {
writer.write_u8(Tag::End.id())?; writer.write_u8(Tag::End.id())?;
} }
return Ok(()); Ok(())
} }
#[inline] #[inline]

View file

@ -17,6 +17,12 @@ pub enum Tag {
LongArray(Vec<i64>), // 12 LongArray(Vec<i64>), // 12
} }
impl Default for Tag {
fn default() -> Self {
Tag::End
}
}
impl Tag { impl Tag {
#[inline] #[inline]
pub fn id(&self) -> u8 { pub fn id(&self) -> u8 {

4
azalea-protocol/README.md Executable file → Normal file
View file

@ -2,11 +2,11 @@
Sent and receive Minecraft packets. You should probably use `azalea` or `azalea-client` instead. Sent and receive Minecraft packets. You should probably use `azalea` or `azalea-client` instead.
The goal is to **only** support the latest Minecraft version in order to ease development. The goal is to only support the latest Minecraft version in order to ease development.
This is not yet complete, search for `TODO` in the code for things that need to be done. This is not yet complete, search for `TODO` in the code for things that need to be done.
Unfortunately, compiling the crate requires Rust nightly because specialization is not stable yet. Unfortunately, using azalea-protocol requires Rust nightly because [specialization](https://github.com/rust-lang/rust/issues/31844) is not stable yet. Use `rustup default nightly` to enable it.
## Adding a new packet ## Adding a new packet

View file

@ -157,6 +157,19 @@ pub fn derive_mcbufwritable(input: TokenStream) -> TokenStream {
create_impl_mcbufwritable(&ident, &data).into() 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);
@ -169,8 +182,8 @@ 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 _mcbufreadable_impl = create_impl_mcbufreadable(&ident, &data);
let mcbufwritable_impl = create_impl_mcbufwritable(&ident, &data); let _mcbufwritable_impl = create_impl_mcbufwritable(&ident, &data);
let contents = quote! { let contents = quote! {
impl #ident { impl #ident {
@ -189,10 +202,6 @@ fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> Toke
Ok(Self::read_into(buf)?.get()) Ok(Self::read_into(buf)?.get())
} }
} }
#mcbufreadable_impl
#mcbufwritable_impl
}; };
contents.into() contents.into()
@ -232,13 +241,12 @@ struct PacketIdMap {
impl Parse for PacketIdMap { impl Parse for PacketIdMap {
fn parse(input: ParseStream) -> Result<Self> { fn parse(input: ParseStream) -> Result<Self> {
let mut packets = vec![]; let mut packets = vec![];
loop {
// 0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, // example:
// 0x0e // 0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket,
let packet_id: LitInt = match input.parse() {
Ok(i) => i, // 0x0e
Err(_) => break, while let Ok(packet_id) = input.parse::<LitInt>() {
};
let packet_id = packet_id.base10_parse::<u32>()?; let packet_id = packet_id.base10_parse::<u32>()?;
// : // :
input.parse::<Token![:]>()?; input.parse::<Token![:]>()?;

View file

@ -1,8 +1,9 @@
use crate::mc_buf::read::{McBufReadable, Readable}; use crate::mc_buf::read::{McBufReadable, Readable};
use crate::mc_buf::write::{McBufWritable, Writable}; use crate::mc_buf::write::{McBufWritable, Writable};
use crate::mc_buf::McBufVarReadable;
use azalea_chat::component::Component; use azalea_chat::component::Component;
use azalea_core::{BlockPos, Direction, Slot}; use azalea_core::{BlockPos, Direction, Slot};
use packet_macros::{McBufReadable, McBufWritable}; use packet_macros::McBuf;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::ops::Deref; use std::ops::Deref;
use uuid::Uuid; use uuid::Uuid;
@ -32,7 +33,7 @@ impl From<&str> for UnsizedByteArray {
} }
/// Represents Java's BitSet, a list of bits. /// Represents Java's BitSet, a list of bits.
#[derive(Debug, Clone, PartialEq, Eq, Hash, McBufReadable, McBufWritable)] #[derive(Debug, Clone, PartialEq, Eq, Hash, McBuf)]
pub struct BitSet { pub struct BitSet {
data: Vec<u64>, data: Vec<u64>,
} }
@ -159,7 +160,7 @@ impl McBufWritable for EntityDataValue {
} }
} }
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)] #[derive(Clone, Debug, Copy, McBuf)]
pub enum Pose { pub enum Pose {
Standing = 0, Standing = 0,
FallFlying = 1, FallFlying = 1,
@ -171,7 +172,7 @@ pub enum Pose {
Dying = 7, Dying = 7,
} }
#[derive(Debug, Clone, McBufReadable, McBufWritable)] #[derive(Debug, Clone, McBuf)]
pub struct VillagerData { pub struct VillagerData {
#[var] #[var]
type_: u32, type_: u32,
@ -181,7 +182,7 @@ pub struct VillagerData {
level: u32, level: u32,
} }
#[derive(Debug, Clone, McBufReadable, McBufWritable)] #[derive(Debug, Clone, McBuf)]
pub struct Particle { pub struct Particle {
#[var] #[var]
pub id: i32, pub id: i32,
@ -280,12 +281,12 @@ pub enum ParticleData {
Scrape, Scrape,
} }
#[derive(Debug, Clone, McBufReadable, McBufWritable)] #[derive(Debug, Clone, McBuf)]
pub struct BlockParticle { pub struct BlockParticle {
#[var] #[var]
pub block_state: i32, pub block_state: i32,
} }
#[derive(Debug, Clone, McBufReadable, McBufWritable)] #[derive(Debug, Clone, McBuf)]
pub struct DustParticle { pub struct DustParticle {
/// Red value, 0-1 /// Red value, 0-1
pub red: f32, pub red: f32,
@ -297,7 +298,7 @@ pub struct DustParticle {
pub scale: f32, pub scale: f32,
} }
#[derive(Debug, Clone, McBufReadable, McBufWritable)] #[derive(Debug, Clone, McBuf)]
pub struct DustColorTransitionParticle { pub struct DustColorTransitionParticle {
/// Red value, 0-1 /// Red value, 0-1
pub from_red: f32, pub from_red: f32,
@ -315,12 +316,12 @@ pub struct DustColorTransitionParticle {
pub to_blue: f32, pub to_blue: f32,
} }
#[derive(Debug, Clone, McBufReadable, McBufWritable)] #[derive(Debug, Clone, McBuf)]
pub struct ItemParticle { pub struct ItemParticle {
pub item: Slot, pub item: Slot,
} }
#[derive(Debug, Clone, McBufReadable, McBufWritable)] #[derive(Debug, Clone, McBuf)]
pub struct VibrationParticle { pub struct VibrationParticle {
pub origin: BlockPos, pub origin: BlockPos,
pub position_type: String, pub position_type: String,
@ -331,9 +332,8 @@ pub struct VibrationParticle {
pub ticks: u32, pub ticks: u32,
} }
impl McBufReadable for ParticleData { impl ParticleData {
fn read_into(buf: &mut impl Read) -> Result<Self, String> { pub fn read_from_particle_id(buf: &mut impl Read, id: u32) -> Result<Self, String> {
let id = buf.read_varint()?;
Ok(match id { Ok(match id {
0 => ParticleData::AmbientEntityEffect, 0 => ParticleData::AmbientEntityEffect,
1 => ParticleData::AngryVillager, 1 => ParticleData::AngryVillager,
@ -428,6 +428,13 @@ impl McBufReadable for ParticleData {
} }
} }
impl McBufReadable for ParticleData {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let id = u32::var_read_into(buf)?;
ParticleData::read_from_particle_id(buf, id)
}
}
impl McBufWritable for ParticleData { impl McBufWritable for ParticleData {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
todo!() todo!()

6
azalea-protocol/src/mc_buf/mod.rs Executable file → Normal file
View file

@ -4,16 +4,16 @@ mod definitions;
mod read; mod read;
mod write; mod write;
pub use definitions::{BitSet, EntityMetadata, UnsizedByteArray}; pub use definitions::{BitSet, EntityMetadata, ParticleData, UnsizedByteArray};
use packet_macros::{McBufReadable, McBufWritable};
pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable}; pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable};
use std::ops::Deref;
pub use write::{McBufVarWritable, McBufWritable, Writable}; pub use write::{McBufVarWritable, McBufWritable, Writable};
// const DEFAULT_NBT_QUOTA: u32 = 2097152; // const DEFAULT_NBT_QUOTA: u32 = 2097152;
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 // TODO: have a definitions.rs in mc_buf that contains UnsizedByteArray and BitSet
#[cfg(test)] #[cfg(test)]

20
azalea-protocol/src/mc_buf/read.rs Executable file → Normal file
View file

@ -1,10 +1,10 @@
use super::{BitSet, UnsizedByteArray, MAX_STRING_LENGTH}; use super::{UnsizedByteArray, MAX_STRING_LENGTH};
use azalea_chat::component::Component; use azalea_chat::component::Component;
use azalea_core::{ use azalea_core::{
difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation,
serializable_uuid::SerializableUuid, BlockPos, Direction, Slot, SlotData, serializable_uuid::SerializableUuid, BlockPos, ChunkSectionPos, Direction, Slot, SlotData,
}; };
use byteorder::{ReadBytesExt, WriteBytesExt, BE}; use byteorder::{ReadBytesExt, BE};
use serde::Deserialize; 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};
@ -466,7 +466,7 @@ impl McBufReadable for Component {
fn read_into(buf: &mut impl Read) -> Result<Self, String> { fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let string = buf.read_utf()?; let string = buf.read_utf()?;
let json: serde_json::Value = serde_json::from_str(string.as_str()) let json: serde_json::Value = serde_json::from_str(string.as_str())
.map_err(|e| "Component isn't valid JSON".to_string())?; .map_err(|_| "Component isn't valid JSON".to_string())?;
let component = Component::deserialize(json).map_err(|e| e.to_string())?; let component = Component::deserialize(json).map_err(|e| e.to_string())?;
Ok(component) Ok(component)
} }
@ -518,3 +518,15 @@ impl McBufReadable for Direction {
} }
} }
} }
// ChunkSectionPos
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,
})
}
}

21
azalea-protocol/src/mc_buf/write.rs Executable file → Normal file
View file

@ -2,7 +2,7 @@ use super::{UnsizedByteArray, MAX_STRING_LENGTH};
use azalea_chat::component::Component; use azalea_chat::component::Component;
use azalea_core::{ use azalea_core::{
difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation,
serializable_uuid::SerializableUuid, BlockPos, Direction, Slot, serializable_uuid::SerializableUuid, BlockPos, ChunkSectionPos, Direction, Slot,
}; };
use byteorder::{BigEndian, WriteBytesExt}; use byteorder::{BigEndian, WriteBytesExt};
use std::{collections::HashMap, io::Write}; use std::{collections::HashMap, io::Write};
@ -20,8 +20,8 @@ pub trait Writable: Write {
Ok(()) Ok(())
} }
fn write_int_id_list(&mut self, list: &Vec<i32>) -> Result<(), std::io::Error> { fn write_int_id_list(&mut self, list: &[i32]) -> Result<(), std::io::Error> {
self.write_list(&list, |buf, n| buf.write_varint(*n)) self.write_list(list, |buf, n| buf.write_varint(*n))
} }
fn write_map<KF, VF, KT, VT>( fn write_map<KF, VF, KT, VT>(
@ -47,7 +47,7 @@ pub trait Writable: Write {
} }
fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> {
self.write_all(bytes); self.write_all(bytes)?;
Ok(()) Ok(())
} }
@ -377,7 +377,7 @@ impl McBufWritable for Component {
// let component = Component::deserialize(json).map_err(|e| e.to_string())?; // let component = Component::deserialize(json).map_err(|e| e.to_string())?;
// Ok(component) // Ok(component)
// } // }
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
// component doesn't have serialize implemented yet // component doesn't have serialize implemented yet
todo!() todo!()
} }
@ -425,3 +425,14 @@ impl McBufWritable for Direction {
buf.write_varint(*self as i32) buf.write_varint(*self as i32)
} }
} }
// ChunkSectionPos
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(())
}
}

View file

@ -1,7 +1,7 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
use uuid::Uuid; use uuid::Uuid;
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundAddEntityPacket { pub struct ClientboundAddEntityPacket {
#[var] #[var]
pub id: i32, pub id: i32,

View file

@ -1,7 +1,7 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
use uuid::Uuid; use uuid::Uuid;
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundAddMobPacket { pub struct ClientboundAddMobPacket {
#[var] #[var]
pub id: i32, pub id: i32,

View file

@ -1,7 +1,7 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
use uuid::Uuid; use uuid::Uuid;
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundAddPlayerPacket { pub struct ClientboundAddPlayerPacket {
#[var] #[var]
pub id: i32, pub id: i32,

View file

@ -1,6 +1,6 @@
use packet_macros::{GamePacket, McBufReadable, McBufWritable}; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundAnimatePacket { pub struct ClientboundAnimatePacket {
#[var] #[var]
pub id: u32, pub id: u32,
@ -9,7 +9,7 @@ pub struct ClientboundAnimatePacket {
// minecraft actually uses a u8 for this, but a varint still works and makes it // minecraft actually uses a u8 for this, but a varint still works and makes it
// so i don't have to add a special handler // so i don't have to add a special handler
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)] #[derive(Clone, Debug, Copy, McBuf)]
pub enum AnimationAction { pub enum AnimationAction {
SwingMainHand = 0, SwingMainHand = 0,
Hurt = 1, Hurt = 1,

View file

@ -1,7 +1,7 @@
use azalea_core::BlockPos; use azalea_core::BlockPos;
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundBlockUpdatePacket { pub struct ClientboundBlockUpdatePacket {
pub pos: BlockPos, pub pos: BlockPos,
// TODO: in vanilla this is a BlockState, but here we just have it as a number. // TODO: in vanilla this is a BlockState, but here we just have it as a number.

View file

@ -1,7 +1,7 @@
use azalea_core::difficulty::Difficulty; use azalea_core::difficulty::Difficulty;
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundChangeDifficultyPacket { pub struct ClientboundChangeDifficultyPacket {
pub difficulty: Difficulty, pub difficulty: Difficulty,
pub locked: bool, pub locked: bool,

View file

@ -1,15 +1,15 @@
use azalea_chat::component::Component; use azalea_chat::component::Component;
use packet_macros::{GamePacket, McBufReadable, McBufWritable}; use packet_macros::{GamePacket, McBuf};
use uuid::Uuid; use uuid::Uuid;
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundChatPacket { pub struct ClientboundChatPacket {
pub message: Component, pub message: Component,
pub type_: ChatType, pub type_: ChatType,
pub sender: Uuid, pub sender: Uuid,
} }
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)] #[derive(Clone, Debug, Copy, McBuf)]
pub enum ChatType { pub enum ChatType {
Chat = 0, Chat = 0,
System = 1, System = 1,

View file

@ -1,7 +1,7 @@
use azalea_core::Slot; use azalea_core::Slot;
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundContainerSetContentPacket { pub struct ClientboundContainerSetContentPacket {
pub container_id: u8, pub container_id: u8,
#[var] #[var]

View file

@ -1,8 +1,8 @@
use crate::mc_buf::UnsizedByteArray; use crate::mc_buf::UnsizedByteArray;
use azalea_core::resource_location::ResourceLocation; use azalea_core::resource_location::ResourceLocation;
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundCustomPayloadPacket { pub struct ClientboundCustomPayloadPacket {
pub identifier: ResourceLocation, pub identifier: ResourceLocation,
pub data: UnsizedByteArray, pub data: UnsizedByteArray,

View file

@ -281,7 +281,7 @@ impl McBufReadable for BrigadierParser {
} }
} }
// azalea_brigadier::tree::CommandNode // TODO: BrigadierNodeStub should have more stuff
impl McBufReadable for BrigadierNodeStub { impl McBufReadable for BrigadierNodeStub {
fn read_into(buf: &mut impl Read) -> Result<Self, String> { fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let flags = u8::read_into(buf)?; let flags = u8::read_into(buf)?;
@ -292,20 +292,18 @@ impl McBufReadable for BrigadierNodeStub {
} }
let node_type = flags & 0x03; let node_type = flags & 0x03;
let is_executable = flags & 0x04 != 0; let _is_executable = flags & 0x04 != 0;
let has_redirect = flags & 0x08 != 0; let has_redirect = flags & 0x08 != 0;
let has_suggestions_type = flags & 0x10 != 0; let has_suggestions_type = flags & 0x10 != 0;
let children = buf.read_int_id_list()?; let _children = buf.read_int_id_list()?;
let redirect_node = if has_redirect { buf.read_varint()? } else { 0 }; let _redirect_node = if has_redirect { buf.read_varint()? } else { 0 };
// argument node // argument node
if node_type == 2 { if node_type == 2 {
let name = buf.read_utf()?; let _name = buf.read_utf()?;
let _parser = BrigadierParser::read_into(buf)?;
let parser = BrigadierParser::read_into(buf)?; let _suggestions_type = if has_suggestions_type {
let suggestions_type = if has_suggestions_type {
Some(buf.read_resource_location()?) Some(buf.read_resource_location()?)
} else { } else {
None None
@ -314,7 +312,7 @@ impl McBufReadable for BrigadierNodeStub {
} }
// literal node // literal node
if node_type == 1 { if node_type == 1 {
let name = buf.read_utf()?; let _name = buf.read_utf()?;
return Ok(BrigadierNodeStub {}); return Ok(BrigadierNodeStub {});
} }
Ok(BrigadierNodeStub {}) Ok(BrigadierNodeStub {})

View file

@ -1,7 +1,7 @@
use azalea_chat::component::Component; use azalea_chat::component::Component;
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundDisconnectPacket { pub struct ClientboundDisconnectPacket {
pub reason: Component, pub reason: Component,
} }

View file

@ -1,7 +1,7 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
// we can't identify the status in azalea-protocol since they vary depending on the entity // we can't identify the status in azalea-protocol since they vary depending on the entity
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundEntityEventPacket { pub struct ClientboundEntityEventPacket {
pub entity_id: i32, pub entity_id: i32,
pub entity_status: i8, pub entity_status: i8,

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundEntityVelocityPacket { pub struct ClientboundEntityVelocityPacket {
#[var] #[var]
pub entity_id: u32, pub entity_id: u32,

View file

@ -0,0 +1,23 @@
use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundGameEventPacket {
pub event: EventType,
pub param: f32,
}
#[derive(Clone, Debug, Copy, McBuf)]
pub enum EventType {
NoRespawnBlockAvailable = 0,
StartRaining = 1,
StopRaining = 2,
ChangeGameMode = 3,
WinGame = 4,
DemoEvent = 5,
ArrowHitPlayer = 6,
RainLevelChange = 7,
ThunderLevelChange = 8,
PufferFishSting = 9,
GuardianElderEffect = 10,
ImmediateRespawn = 11,
}

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundInitializeBorderPacket { pub struct ClientboundInitializeBorderPacket {
pub new_center_x: f64, pub new_center_x: f64,
pub new_center_z: f64, pub new_center_z: f64,

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundKeepAlivePacket { pub struct ClientboundKeepAlivePacket {
pub id: u64, pub id: u64,
} }

View file

@ -1,8 +1,8 @@
use packet_macros::{GamePacket, McBufReadable, McBufWritable}; use packet_macros::{GamePacket, McBuf};
use super::clientbound_light_update_packet::ClientboundLightUpdatePacketData; use super::clientbound_light_update_packet::ClientboundLightUpdatePacketData;
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundLevelChunkWithLightPacket { pub struct ClientboundLevelChunkWithLightPacket {
pub x: i32, pub x: i32,
pub z: i32, pub z: i32,
@ -10,25 +10,19 @@ pub struct ClientboundLevelChunkWithLightPacket {
pub light_data: ClientboundLightUpdatePacketData, pub light_data: ClientboundLightUpdatePacketData,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct ClientboundLevelChunkPacketData { pub struct ClientboundLevelChunkPacketData {
heightmaps: azalea_nbt::Tag, pub heightmaps: azalea_nbt::Tag,
// we can't parse the data in azalea-protocol because it dependso on context from other packets // we can't parse the data in azalea-protocol because it dependso on context from other packets
data: Vec<u8>, pub data: Vec<u8>,
block_entities: Vec<BlockEntity>, pub block_entities: Vec<BlockEntity>,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct BlockEntity { pub struct BlockEntity {
packed_xz: u8, pub packed_xz: u8,
y: u16, pub y: u16,
#[var] #[var]
type_: i32, pub type_: i32,
data: azalea_nbt::Tag, pub data: azalea_nbt::Tag,
}
pub struct ChunkSection {}
impl ClientboundLevelChunkPacketData {
pub fn read(world_height: u32) {}
} }

View file

@ -1,7 +1,7 @@
use azalea_core::BlockPos; use azalea_core::BlockPos;
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundLevelEventPacket { pub struct ClientboundLevelEventPacket {
pub type_: i32, pub type_: i32,
pub pos: BlockPos, pub pos: BlockPos,

View file

@ -0,0 +1,56 @@
use std::io::{Read, Write};
use crate::mc_buf::{McBufReadable, McBufWritable, ParticleData};
use packet_macros::GamePacket;
#[derive(Clone, Debug, GamePacket)]
pub struct ClientboundLevelParticlesPacket {
pub particle_id: u32,
pub override_limiter: bool,
pub x: f64,
pub y: f64,
pub z: f64,
pub x_dist: f32,
pub y_dist: f32,
pub z_dist: f32,
pub max_speed: f32,
pub count: i32,
pub data: ParticleData,
}
impl McBufReadable for ClientboundLevelParticlesPacket {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let particle_id = u32::read_into(buf)?;
let override_limiter = bool::read_into(buf)?;
let x = f64::read_into(buf)?;
let y = f64::read_into(buf)?;
let z = f64::read_into(buf)?;
let x_dist = f32::read_into(buf)?;
let y_dist = f32::read_into(buf)?;
let z_dist = f32::read_into(buf)?;
let max_speed = f32::read_into(buf)?;
let count = i32::read_into(buf)?;
let data = ParticleData::read_from_particle_id(buf, particle_id)?;
Ok(Self {
particle_id,
override_limiter,
x,
y,
z,
x_dist,
y_dist,
z_dist,
max_speed,
count,
data,
})
}
}
impl McBufWritable for ClientboundLevelParticlesPacket {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
todo!();
}
}

View file

@ -1,15 +1,14 @@
use crate::mc_buf::BitSet; use crate::mc_buf::BitSet;
use azalea_core::{game_type::GameType, resource_location::ResourceLocation}; use packet_macros::{GamePacket, McBuf};
use packet_macros::{GamePacket, McBufReadable, McBufWritable};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundLightUpdatePacket { pub struct ClientboundLightUpdatePacket {
pub x: i32, pub x: i32,
pub z: i32, pub z: i32,
pub light_data: ClientboundLightUpdatePacketData, pub light_data: ClientboundLightUpdatePacketData,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct ClientboundLightUpdatePacketData { pub struct ClientboundLightUpdatePacketData {
trust_edges: bool, trust_edges: bool,
sky_y_mask: BitSet, sky_y_mask: BitSet,

View file

@ -1,7 +1,7 @@
use azalea_core::{game_type::GameType, resource_location::ResourceLocation}; use azalea_core::{game_type::GameType, resource_location::ResourceLocation};
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundLoginPacket { pub struct ClientboundLoginPacket {
pub player_id: u32, pub player_id: u32,
pub hardcore: bool, pub hardcore: bool,

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundMoveEntityPosPacket { pub struct ClientboundMoveEntityPosPacket {
#[var] #[var]
pub entity_id: i32, pub entity_id: i32,

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundMoveEntityPosRotPacket { pub struct ClientboundMoveEntityPosRotPacket {
#[var] #[var]
pub entity_id: i32, pub entity_id: i32,

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundMoveEntityRotPacket { pub struct ClientboundMoveEntityRotPacket {
#[var] #[var]
pub entity_id: i32, pub entity_id: i32,

View file

@ -1,8 +1,8 @@
use crate::mc_buf::{McBufReadable, McBufWritable, Readable}; use crate::mc_buf::{McBufReadable, McBufWritable, Readable};
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
use std::io::{Read, Write}; use std::io::{Read, Write};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundPlayerAbilitiesPacket { pub struct ClientboundPlayerAbilitiesPacket {
pub flags: PlayerAbilitiesFlags, pub flags: PlayerAbilitiesFlags,
pub flying_speed: f32, pub flying_speed: f32,
@ -34,16 +34,16 @@ impl McBufWritable for PlayerAbilitiesFlags {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
let mut byte = 0; let mut byte = 0;
if self.invulnerable { if self.invulnerable {
byte = byte | 1; byte |= 0b1;
} }
if self.flying { if self.flying {
byte = byte | 2; byte |= 0b10;
} }
if self.can_fly { if self.can_fly {
byte = byte | 4; byte |= 0b100;
} }
if self.instant_break { if self.instant_break {
byte = byte | 8; byte |= 0b1000;
} }
u8::write_into(&byte, buf) u8::write_into(&byte, buf)
} }

View file

@ -1,10 +1,10 @@
use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
use azalea_chat::component::Component; use azalea_chat::component::Component;
use packet_macros::{GamePacket, McBufReadable, McBufWritable}; use packet_macros::{GamePacket, McBuf};
use std::io::{Read, Write}; use std::io::{Read, Write};
use uuid::Uuid; use uuid::Uuid;
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundPlayerInfoPacket { pub struct ClientboundPlayerInfoPacket {
pub action: Action, pub action: Action,
} }
@ -18,14 +18,14 @@ pub enum Action {
RemovePlayer(Vec<RemovePlayer>), RemovePlayer(Vec<RemovePlayer>),
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct PlayerProperty { pub struct PlayerProperty {
name: String, name: String,
value: String, value: String,
signature: Option<String>, signature: Option<String>,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct AddPlayer { pub struct AddPlayer {
uuid: Uuid, uuid: Uuid,
name: String, name: String,
@ -37,26 +37,26 @@ pub struct AddPlayer {
display_name: Option<Component>, display_name: Option<Component>,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct UpdateGameMode { pub struct UpdateGameMode {
uuid: Uuid, uuid: Uuid,
#[var] #[var]
gamemode: u32, gamemode: u32,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct UpdateLatency { pub struct UpdateLatency {
uuid: Uuid, uuid: Uuid,
#[var] #[var]
ping: i32, ping: i32,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct UpdateDisplayName { pub struct UpdateDisplayName {
uuid: Uuid, uuid: Uuid,
display_name: Option<Component>, display_name: Option<Component>,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct RemovePlayer { pub struct RemovePlayer {
uuid: Uuid, uuid: Uuid,
} }

View file

@ -1,8 +1,8 @@
use crate::mc_buf::{McBufReadable, McBufWritable, Readable}; use crate::mc_buf::{McBufReadable, McBufWritable, Readable};
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
use std::io::{Read, Write}; use std::io::{Read, Write};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundPlayerPositionPacket { pub struct ClientboundPlayerPositionPacket {
pub x: f64, pub x: f64,
pub y: f64, pub y: f64,
@ -43,19 +43,19 @@ impl McBufWritable for RelativeArguments {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
let mut byte = 0; let mut byte = 0;
if self.x { if self.x {
byte = byte | 0b1; byte |= 0b1;
} }
if self.y { if self.y {
byte = byte | 0b10; byte |= 0b10;
} }
if self.z { if self.z {
byte = byte | 0b100; byte |= 0b100;
} }
if self.y_rot { if self.y_rot {
byte = byte | 0b1000; byte |= 0b1000;
} }
if self.x_rot { if self.x_rot {
byte = byte | 0b10000; byte |= 0b10000;
} }
u8::write_into(&byte, buf) u8::write_into(&byte, buf)
} }

View file

@ -1,9 +1,9 @@
use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
use azalea_core::{resource_location::ResourceLocation, Slot}; use azalea_core::resource_location::ResourceLocation;
use packet_macros::{GamePacket, McBufReadable, McBufWritable}; use packet_macros::{GamePacket, McBuf};
use std::io::{Read, Write}; use std::io::{Read, Write};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundRecipePacket { pub struct ClientboundRecipePacket {
pub action: State, pub action: State,
pub settings: RecipeBookSettings, pub settings: RecipeBookSettings,
@ -11,7 +11,7 @@ pub struct ClientboundRecipePacket {
pub to_highlight: Vec<ResourceLocation>, pub to_highlight: Vec<ResourceLocation>,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct RecipeBookSettings { pub struct RecipeBookSettings {
pub gui_open: bool, pub gui_open: bool,
pub filtering_craftable: bool, pub filtering_craftable: bool,
@ -41,7 +41,7 @@ impl McBufWritable for State {
} }
impl McBufReadable for State { impl McBufReadable for State {
fn read_into(buf: &mut impl Read) -> Result<Self, String> { fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let state = buf.read_varint()?.try_into().unwrap(); let state = buf.read_varint()?;
Ok(match state { Ok(match state {
0 => State::Init, 0 => State::Init,
1 => State::Add, 1 => State::Add,

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundRemoveEntitiesPacket { pub struct ClientboundRemoveEntitiesPacket {
#[var] #[var]
pub entity_ids: Vec<u32>, pub entity_ids: Vec<u32>,

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundRotateHeadPacket { pub struct ClientboundRotateHeadPacket {
#[var] #[var]
pub entity_id: u32, pub entity_id: u32,

View file

@ -0,0 +1,43 @@
use crate::mc_buf::{McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable};
use azalea_core::{ChunkSectionBlockPos, ChunkSectionPos};
use packet_macros::{GamePacket, McBuf};
use std::io::{Read, Write};
#[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundSectionBlocksUpdatePacket {
pub section_pos: ChunkSectionPos,
pub suppress_light_updates: bool,
pub states: Vec<BlockStateWithPosition>,
}
#[derive(Clone, Debug)]
pub struct BlockStateWithPosition {
pub pos: ChunkSectionBlockPos,
pub state: u32,
}
impl McBufReadable for BlockStateWithPosition {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let data = u64::var_read_into(buf)?;
let position_part = data & 4095;
let state = (data >> 12) as u32;
let position = ChunkSectionBlockPos {
x: (position_part >> 8 & 15) as u8,
y: (position_part >> 0 & 15) as u8,
z: (position_part >> 4 & 15) as u8,
};
Ok(BlockStateWithPosition {
pos: position,
state: state,
})
}
}
impl McBufWritable for BlockStateWithPosition {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
let data = (self.state as u64) << 12
| ((self.pos.x as u64) << 8 | (self.pos.z as u64) << 4 | (self.pos.y as u64));
u64::var_write_into(&data, buf)?;
Ok(())
}
}

View file

@ -1,7 +1,7 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
/// Sent to change the player's slot selection. /// Sent to change the player's slot selection.
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundSetCarriedItemPacket { pub struct ClientboundSetCarriedItemPacket {
pub slot: u8, pub slot: u8,
} }

View file

@ -1,9 +1,9 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundSetChunkCacheCenterPacket { pub struct ClientboundSetChunkCacheCenterPacket {
#[var] #[var]
pub x: i32, pub x: i32,
#[var] #[var]
pub y: i32, pub z: i32,
} }

View file

@ -1,7 +1,7 @@
use azalea_core::BlockPos; use azalea_core::BlockPos;
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundSetDefaultSpawnPositionPacket { pub struct ClientboundSetDefaultSpawnPositionPacket {
pub pos: BlockPos, pub pos: BlockPos,
pub angle: f32, pub angle: f32,

View file

@ -1,7 +1,7 @@
use crate::mc_buf::EntityMetadata; use crate::mc_buf::EntityMetadata;
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundSetEntityDataPacket { pub struct ClientboundSetEntityDataPacket {
#[var] #[var]
pub id: i32, pub id: i32,

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundSetEntityLinkPacket { pub struct ClientboundSetEntityLinkPacket {
pub source_id: u32, pub source_id: u32,
pub dest_id: u32, pub dest_id: u32,

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundSetExperiencePacket { pub struct ClientboundSetExperiencePacket {
pub experience_progress: f32, pub experience_progress: f32,
#[var] #[var]

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundSetHealthPacket { pub struct ClientboundSetHealthPacket {
pub health: f32, pub health: f32,
#[var] #[var]

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundSetTimePacket { pub struct ClientboundSetTimePacket {
pub game_time: u64, pub game_time: u64,
pub day_time: u64, pub day_time: u64,

View file

@ -1,6 +1,6 @@
use packet_macros::{GamePacket, McBufReadable, McBufWritable}; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundSoundPacket { pub struct ClientboundSoundPacket {
#[var] #[var]
/// TODO: use the sound registry instead of just being a u32 /// TODO: use the sound registry instead of just being a u32
@ -13,7 +13,7 @@ pub struct ClientboundSoundPacket {
pub pitch: f32, pub pitch: f32,
} }
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)] #[derive(Clone, Debug, Copy, McBuf)]
pub enum SoundSource { pub enum SoundSource {
Master = 0, Master = 0,
Music = 1, Music = 1,

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundTeleportEntityPacket { pub struct ClientboundTeleportEntityPacket {
#[var] #[var]
pub id: u32, pub id: u32,

View file

@ -1,13 +1,13 @@
use crate::packets::{McBufReadable, McBufWritable}; use crate::packets::{McBufReadable, McBufWritable};
use azalea_chat::component::Component; use azalea_chat::component::Component;
use azalea_core::{resource_location::ResourceLocation, Slot}; use azalea_core::{resource_location::ResourceLocation, Slot};
use packet_macros::{GamePacket, McBufReadable, McBufWritable}; use packet_macros::{GamePacket, McBuf};
use std::{ use std::{
collections::HashMap, collections::HashMap,
io::{Read, Write}, io::{Read, Write},
}; };
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundUpdateAdvancementsPacket { pub struct ClientboundUpdateAdvancementsPacket {
pub reset: bool, pub reset: bool,
pub added: HashMap<ResourceLocation, Advancement>, pub added: HashMap<ResourceLocation, Advancement>,
@ -15,7 +15,7 @@ pub struct ClientboundUpdateAdvancementsPacket {
pub progress: HashMap<ResourceLocation, AdvancementProgress>, pub progress: HashMap<ResourceLocation, AdvancementProgress>,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct Advancement { pub struct Advancement {
parent_id: Option<ResourceLocation>, parent_id: Option<ResourceLocation>,
display: Option<DisplayInfo>, display: Option<DisplayInfo>,
@ -25,7 +25,7 @@ pub struct Advancement {
// requirements_strategy: RequirementsStrategy.AND // requirements_strategy: RequirementsStrategy.AND
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct DisplayInfo { pub struct DisplayInfo {
pub title: Component, pub title: Component,
pub description: Component, pub description: Component,
@ -71,7 +71,7 @@ impl McBufWritable for DisplayFlags {
} }
} }
#[derive(Clone, Debug, Copy, McBufReadable, McBufWritable)] #[derive(Clone, Debug, Copy, McBuf)]
pub enum FrameType { pub enum FrameType {
Task = 0, Task = 0,
Challenge = 1, Challenge = 1,
@ -79,12 +79,12 @@ pub enum FrameType {
} }
// nothing is written here // nothing is written here
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct Criterion {} pub struct Criterion {}
pub type AdvancementProgress = HashMap<ResourceLocation, CriterionProgress>; pub type AdvancementProgress = HashMap<ResourceLocation, CriterionProgress>;
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct CriterionProgress { pub struct CriterionProgress {
date: Option<u64>, date: Option<u64>,
} }

View file

@ -1,24 +1,24 @@
use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
use azalea_core::resource_location::ResourceLocation; use azalea_core::resource_location::ResourceLocation;
use packet_macros::{GamePacket, McBufReadable, McBufWritable}; use packet_macros::{GamePacket, McBuf};
use std::io::{Read, Write}; use std::io::{Read, Write};
use uuid::Uuid; use uuid::Uuid;
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundUpdateAttributesPacket { pub struct ClientboundUpdateAttributesPacket {
#[var] #[var]
pub entity_id: u32, pub entity_id: u32,
pub attributes: Vec<AttributeSnapshot>, pub attributes: Vec<AttributeSnapshot>,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct AttributeSnapshot { pub struct AttributeSnapshot {
pub attribute: ResourceLocation, pub attribute: ResourceLocation,
pub base: f64, pub base: f64,
pub modifiers: Vec<Modifier>, pub modifiers: Vec<Modifier>,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct Modifier { pub struct Modifier {
pub uuid: Uuid, pub uuid: Uuid,
pub amount: f64, pub amount: f64,

View file

@ -1,11 +1,11 @@
use std::io::{Read, Write}; use std::io::{Read, Write};
use azalea_core::{resource_location::ResourceLocation, Slot}; use azalea_core::{resource_location::ResourceLocation, Slot};
use packet_macros::{GamePacket, McBufReadable, McBufWritable}; use packet_macros::{GamePacket, McBuf};
use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundUpdateRecipesPacket { pub struct ClientboundUpdateRecipesPacket {
pub recipes: Vec<Recipe>, pub recipes: Vec<Recipe>,
} }
@ -16,7 +16,7 @@ pub struct Recipe {
pub data: RecipeData, pub data: RecipeData,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct ShapelessRecipe { pub struct ShapelessRecipe {
/// Used to group similar recipes together in the recipe book. /// Used to group similar recipes together in the recipe book.
/// Tag is present in recipe JSON /// Tag is present in recipe JSON
@ -68,7 +68,7 @@ impl McBufReadable for ShapedRecipe {
} }
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct CookingRecipe { pub struct CookingRecipe {
group: String, group: String,
ingredient: Ingredient, ingredient: Ingredient,
@ -77,13 +77,13 @@ pub struct CookingRecipe {
#[var] #[var]
cooking_time: u32, cooking_time: u32,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct StoneCuttingRecipe { pub struct StoneCuttingRecipe {
group: String, group: String,
ingredient: Ingredient, ingredient: Ingredient,
result: Slot, result: Slot,
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct SmithingRecipe { pub struct SmithingRecipe {
base: Ingredient, base: Ingredient,
addition: Ingredient, addition: Ingredient,
@ -116,13 +116,13 @@ pub enum RecipeData {
Smithing(SmithingRecipe), Smithing(SmithingRecipe),
} }
#[derive(Clone, Debug, McBufReadable, McBufWritable)] #[derive(Clone, Debug, McBuf)]
pub struct Ingredient { pub struct Ingredient {
pub allowed: Vec<Slot>, pub allowed: Vec<Slot>,
} }
impl McBufWritable for Recipe { impl McBufWritable for Recipe {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> {
todo!() todo!()
} }
} }

View file

@ -1,12 +1,12 @@
use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable};
use azalea_core::resource_location::ResourceLocation; use azalea_core::resource_location::ResourceLocation;
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
use std::{ use std::{
collections::HashMap, collections::HashMap,
io::{Read, Write}, io::{Read, Write},
}; };
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundUpdateTagsPacket { pub struct ClientboundUpdateTagsPacket {
pub tags: HashMap<ResourceLocation, Vec<Tags>>, pub tags: HashMap<ResourceLocation, Vec<Tags>>,
} }

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ClientboundUpdateViewDistancePacket { pub struct ClientboundUpdateViewDistancePacket {
#[var] #[var]
pub view_distance: i32, pub view_distance: i32,

View file

@ -11,10 +11,12 @@ pub mod clientbound_declare_commands_packet;
pub mod clientbound_disconnect_packet; pub mod clientbound_disconnect_packet;
pub mod clientbound_entity_event_packet; pub mod clientbound_entity_event_packet;
pub mod clientbound_entity_velocity_packet; pub mod clientbound_entity_velocity_packet;
pub mod clientbound_game_event_packet;
pub mod clientbound_initialize_border_packet; pub mod clientbound_initialize_border_packet;
pub mod clientbound_keep_alive_packet; pub mod clientbound_keep_alive_packet;
pub mod clientbound_level_chunk_with_light_packet; pub mod clientbound_level_chunk_with_light_packet;
pub mod clientbound_level_event_packet; pub mod clientbound_level_event_packet;
pub mod clientbound_level_particles_packet;
pub mod clientbound_light_update_packet; pub mod clientbound_light_update_packet;
pub mod clientbound_login_packet; pub mod clientbound_login_packet;
pub mod clientbound_move_entity_pos_packet; pub mod clientbound_move_entity_pos_packet;
@ -26,6 +28,7 @@ pub mod clientbound_player_position_packet;
pub mod clientbound_recipe_packet; pub mod clientbound_recipe_packet;
pub mod clientbound_remove_entities_packet; pub mod clientbound_remove_entities_packet;
pub mod clientbound_rotate_head_packet; pub mod clientbound_rotate_head_packet;
pub mod clientbound_section_blocks_update_packet;
pub mod clientbound_set_carried_item_packet; pub mod clientbound_set_carried_item_packet;
pub mod clientbound_set_chunk_cache_center; pub mod clientbound_set_chunk_cache_center;
pub mod clientbound_set_default_spawn_position_packet; pub mod clientbound_set_default_spawn_position_packet;
@ -56,19 +59,21 @@ declare_state_packets!(
0x00: clientbound_add_entity_packet::ClientboundAddEntityPacket, 0x00: clientbound_add_entity_packet::ClientboundAddEntityPacket,
0x02: clientbound_add_mob_packet::ClientboundAddMobPacket, 0x02: clientbound_add_mob_packet::ClientboundAddMobPacket,
0x04: clientbound_add_player_packet::ClientboundAddPlayerPacket, 0x04: clientbound_add_player_packet::ClientboundAddPlayerPacket,
0x6: clientbound_animate_packet::ClientboundAnimatePacket, 0x06: clientbound_animate_packet::ClientboundAnimatePacket,
0xc: clientbound_block_update_packet::ClientboundBlockUpdatePacket, 0x0c: clientbound_block_update_packet::ClientboundBlockUpdatePacket,
0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket, 0x0e: clientbound_change_difficulty_packet::ClientboundChangeDifficultyPacket,
0xf: clientbound_chat_packet::ClientboundChatPacket, 0x0f: clientbound_chat_packet::ClientboundChatPacket,
0x12: clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket, 0x12: clientbound_declare_commands_packet::ClientboundDeclareCommandsPacket,
0x14: clientbound_container_set_content_packet::ClientboundContainerSetContentPacket, 0x14: clientbound_container_set_content_packet::ClientboundContainerSetContentPacket,
0x1a: clientbound_disconnect_packet::ClientboundDisconnectPacket, 0x1a: clientbound_disconnect_packet::ClientboundDisconnectPacket,
0x1b: clientbound_entity_event_packet::ClientboundEntityEventPacket, 0x1b: clientbound_entity_event_packet::ClientboundEntityEventPacket,
0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, 0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket,
0x1e: clientbound_game_event_packet::ClientboundGameEventPacket,
0x20: clientbound_initialize_border_packet::ClientboundInitializeBorderPacket, 0x20: clientbound_initialize_border_packet::ClientboundInitializeBorderPacket,
0x21: clientbound_keep_alive_packet::ClientboundKeepAlivePacket, 0x21: clientbound_keep_alive_packet::ClientboundKeepAlivePacket,
0x22: clientbound_level_chunk_with_light_packet::ClientboundLevelChunkWithLightPacket, 0x22: clientbound_level_chunk_with_light_packet::ClientboundLevelChunkWithLightPacket,
0x23: clientbound_level_event_packet::ClientboundLevelEventPacket, 0x23: clientbound_level_event_packet::ClientboundLevelEventPacket,
0x24: clientbound_level_particles_packet::ClientboundLevelParticlesPacket,
0x25: clientbound_light_update_packet::ClientboundLightUpdatePacket, 0x25: clientbound_light_update_packet::ClientboundLightUpdatePacket,
0x26: clientbound_login_packet::ClientboundLoginPacket, 0x26: clientbound_login_packet::ClientboundLoginPacket,
0x29: clientbound_move_entity_pos_packet::ClientboundMoveEntityPosPacket, 0x29: clientbound_move_entity_pos_packet::ClientboundMoveEntityPosPacket,
@ -80,6 +85,7 @@ declare_state_packets!(
0x39: clientbound_recipe_packet::ClientboundRecipePacket, 0x39: clientbound_recipe_packet::ClientboundRecipePacket,
0x3a: clientbound_remove_entities_packet::ClientboundRemoveEntitiesPacket, 0x3a: clientbound_remove_entities_packet::ClientboundRemoveEntitiesPacket,
0x3e: clientbound_rotate_head_packet::ClientboundRotateHeadPacket, 0x3e: clientbound_rotate_head_packet::ClientboundRotateHeadPacket,
0x3f: clientbound_section_blocks_update_packet::ClientboundSectionBlocksUpdatePacket,
0x48: clientbound_set_carried_item_packet::ClientboundSetCarriedItemPacket, 0x48: clientbound_set_carried_item_packet::ClientboundSetCarriedItemPacket,
0x49: clientbound_set_chunk_cache_center::ClientboundSetChunkCacheCenterPacket, 0x49: clientbound_set_chunk_cache_center::ClientboundSetChunkCacheCenterPacket,
0x4a: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket, 0x4a: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket,

View file

@ -1,8 +1,8 @@
use crate::mc_buf::UnsizedByteArray; use crate::mc_buf::UnsizedByteArray;
use azalea_core::resource_location::ResourceLocation; use azalea_core::resource_location::ResourceLocation;
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ServerboundCustomPayloadPacket { pub struct ServerboundCustomPayloadPacket {
pub identifier: ResourceLocation, pub identifier: ResourceLocation,
pub data: UnsizedByteArray, pub data: UnsizedByteArray,

View file

@ -1,6 +1,6 @@
use packet_macros::GamePacket; use packet_macros::{GamePacket, McBuf};
#[derive(Clone, Debug, GamePacket)] #[derive(Clone, Debug, McBuf, GamePacket)]
pub struct ServerboundKeepAlivePacket { pub struct ServerboundKeepAlivePacket {
pub id: u64, pub id: u64,
} }

View file

@ -1,8 +1,8 @@
use crate::packets::ConnectionProtocol; use crate::packets::ConnectionProtocol;
use packet_macros::HandshakePacket; use packet_macros::{HandshakePacket, McBuf};
use std::hash::Hash; use std::hash::Hash;
#[derive(Hash, Clone, Debug, HandshakePacket)] #[derive(Hash, Clone, Debug, McBuf, HandshakePacket)]
pub struct ClientIntentionPacket { pub struct ClientIntentionPacket {
#[var] #[var]
pub protocol_version: u32, pub protocol_version: u32,

View file

@ -1,9 +1,9 @@
use crate::mc_buf::UnsizedByteArray; use crate::mc_buf::UnsizedByteArray;
use azalea_core::resource_location::ResourceLocation; use azalea_core::resource_location::ResourceLocation;
use packet_macros::LoginPacket; use packet_macros::{LoginPacket, McBuf};
use std::hash::Hash; use std::hash::Hash;
#[derive(Hash, Clone, Debug, LoginPacket)] #[derive(Hash, Clone, Debug, McBuf, LoginPacket)]
pub struct ClientboundCustomQueryPacket { pub struct ClientboundCustomQueryPacket {
#[var] #[var]
pub transaction_id: u32, pub transaction_id: u32,

View file

@ -1,7 +1,7 @@
use azalea_chat::component::Component; use azalea_chat::component::Component;
use packet_macros::LoginPacket; use packet_macros::{LoginPacket, McBuf};
#[derive(Clone, Debug, LoginPacket)] #[derive(Clone, Debug, McBuf, LoginPacket)]
pub struct ClientboundLoginDisconnectPacket { pub struct ClientboundLoginDisconnectPacket {
pub reason: Component, pub reason: Component,
} }

View file

@ -1,7 +1,7 @@
use packet_macros::LoginPacket; use packet_macros::{LoginPacket, McBuf};
use std::hash::Hash; use std::hash::Hash;
#[derive(Hash, Clone, Debug, LoginPacket)] #[derive(Hash, Clone, Debug, McBuf, LoginPacket)]
pub struct ServerboundHelloPacket { pub struct ServerboundHelloPacket {
pub username: String, pub username: String,
} }

View file

@ -1,9 +1,7 @@
use super::LoginPacket; use packet_macros::{LoginPacket, McBuf};
use crate::mc_buf::Writable;
use packet_macros::LoginPacket;
use std::hash::Hash; use std::hash::Hash;
#[derive(Hash, Clone, Debug, LoginPacket)] #[derive(Hash, Clone, Debug, McBuf, LoginPacket)]
pub struct ServerboundKeyPacket { pub struct ServerboundKeyPacket {
pub shared_secret: Vec<u8>, pub shared_secret: Vec<u8>,
pub nonce: Vec<u8>, pub nonce: Vec<u8>,

View file

@ -43,7 +43,7 @@ where
fn write(&self, buf: &mut impl Write) -> Result<(), std::io::Error>; fn write(&self, buf: &mut impl Write) -> Result<(), std::io::Error>;
} }
impl McBufReadable for ConnectionProtocol { impl crate::mc_buf::McBufReadable for ConnectionProtocol {
fn read_into(buf: &mut impl Read) -> Result<Self, String> { fn read_into(buf: &mut impl Read) -> Result<Self, String> {
ConnectionProtocol::from_i32(buf.read_varint()?) ConnectionProtocol::from_i32(buf.read_varint()?)
.ok_or_else(|| "Invalid intention".to_string()) .ok_or_else(|| "Invalid intention".to_string())

View file

@ -1,4 +1,4 @@
use packet_macros::StatusPacket; use packet_macros::{McBuf, StatusPacket};
#[derive(Clone, Debug, StatusPacket)] #[derive(Clone, Debug, McBuf, StatusPacket)]
pub struct ServerboundStatusRequestPacket {} pub struct ServerboundStatusRequestPacket {}

View file

@ -2,10 +2,7 @@ use crate::{mc_buf::Writable, packets::ProtocolPacket, read::MAXIMUM_UNCOMPRESSE
use async_compression::tokio::bufread::ZlibEncoder; use async_compression::tokio::bufread::ZlibEncoder;
use azalea_crypto::Aes128CfbEnc; use azalea_crypto::Aes128CfbEnc;
use std::fmt::Debug; use std::fmt::Debug;
use tokio::{ use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt};
io::{AsyncReadExt, AsyncWrite, AsyncWriteExt},
net::TcpStream,
};
fn frame_prepender(data: &mut Vec<u8>) -> Result<Vec<u8>, String> { fn frame_prepender(data: &mut Vec<u8>) -> Result<Vec<u8>, String> {
let mut buf = Vec::new(); let mut buf = Vec::new();

11
azalea-world/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
edition = "2021"
name = "azalea-world"
version = "0.1.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
azalea-core = {path = "../azalea-core"}
azalea-nbt = {path = "../azalea-nbt"}
azalea-protocol = {path = "../azalea-protocol"}

3
azalea-world/README.md Normal file
View file

@ -0,0 +1,3 @@
# Azalea World
The Minecraft world representation used in Azalea.

View file

@ -0,0 +1,209 @@
use std::{error::Error, fmt};
// this is from minecraft's code
// yeah idk either
const MAGIC: [(i32, i32, i32); 64] = [
(-1, -1, 0),
(-2147483648, 0, 0),
(1431655765, 1431655765, 0),
(-2147483648, 0, 1),
(858993459, 858993459, 0),
(715827882, 715827882, 0),
(613566756, 613566756, 0),
(-2147483648, 0, 2),
(477218588, 477218588, 0),
(429496729, 429496729, 0),
(390451572, 390451572, 0),
(357913941, 357913941, 0),
(330382099, 330382099, 0),
(306783378, 306783378, 0),
(286331153, 286331153, 0),
(-2147483648, 0, 3),
(252645135, 252645135, 0),
(238609294, 238609294, 0),
(226050910, 226050910, 0),
(214748364, 214748364, 0),
(204522252, 204522252, 0),
(195225786, 195225786, 0),
(186737708, 186737708, 0),
(178956970, 178956970, 0),
(171798691, 171798691, 0),
(165191049, 165191049, 0),
(159072862, 159072862, 0),
(153391689, 153391689, 0),
(148102320, 148102320, 0),
(143165576, 143165576, 0),
(138547332, 138547332, 0),
(-2147483648, 0, 4),
(130150524, 130150524, 0),
(126322567, 126322567, 0),
(122713351, 122713351, 0),
(119304647, 119304647, 0),
(116080197, 116080197, 0),
(113025455, 113025455, 0),
(110127366, 110127366, 0),
(107374182, 107374182, 0),
(104755299, 104755299, 0),
(102261126, 102261126, 0),
(99882960, 99882960, 0),
(97612893, 97612893, 0),
(95443717, 95443717, 0),
(93368854, 93368854, 0),
(91382282, 91382282, 0),
(89478485, 89478485, 0),
(87652393, 87652393, 0),
(85899345, 85899345, 0),
(84215045, 84215045, 0),
(82595524, 82595524, 0),
(81037118, 81037118, 0),
(79536431, 79536431, 0),
(78090314, 78090314, 0),
(76695844, 76695844, 0),
(75350303, 75350303, 0),
(74051160, 74051160, 0),
(72796055, 72796055, 0),
(71582788, 71582788, 0),
(70409299, 70409299, 0),
(69273666, 69273666, 0),
(68174084, 68174084, 0),
(-2147483648, 0, 5),
];
/// A compact list of integers with the given number of bits per entry.
#[derive(Clone, Debug, Default)]
pub struct BitStorage {
pub data: Vec<u64>,
bits: usize,
mask: u64,
size: usize,
values_per_long: u8,
divide_mul: i32,
divide_add: i32,
divide_shift: i32,
}
#[derive(Debug)]
pub enum BitStorageError {
InvalidLength { got: usize, expected: usize },
}
impl fmt::Display for BitStorageError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
BitStorageError::InvalidLength { got, expected } => write!(
f,
"Invalid length given for storage, got: {}, but expected: {}",
got, expected
),
}
}
}
impl Error for BitStorageError {}
impl BitStorage {
/// Create a new BitStorage with the given number of bits per entry.
/// `size` is the number of entries in the BitStorage.
pub fn new(bits: usize, size: usize, data: Option<Vec<u64>>) -> Result<Self, BitStorageError> {
// vanilla has this assert but it's not always true for some reason??
// assert!(bits >= 1 && bits <= 32);
if let Some(data) = &data {
if data.len() == 0 {
// TODO: make 0 bit storage actually work
return Ok(BitStorage::default());
}
}
let values_per_long = 64 / bits;
let magic_index = values_per_long - 1;
let (divide_mul, divide_add, divide_shift) = MAGIC[magic_index as usize];
let calculated_length = (size + values_per_long - 1) / values_per_long;
let mask = (1 << bits) - 1;
let using_data = if let Some(data) = data {
if data.len() != calculated_length as usize {
return Err(BitStorageError::InvalidLength {
got: data.len(),
expected: calculated_length as usize,
});
}
data
} else {
vec![0; calculated_length as usize]
};
Ok(BitStorage {
data: using_data,
bits,
mask,
size,
values_per_long: values_per_long as u8,
divide_mul,
divide_add,
divide_shift,
})
}
pub fn cell_index(&self, index: u64) -> usize {
// as unsigned wrap
let first = self.divide_mul as u32 as u64;
let second = self.divide_add as u64;
(((index * first) + second) >> 32 >> self.divide_shift)
.try_into()
.unwrap()
}
pub fn get(&self, index: usize) -> u64 {
// Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)var1);
// int var2 = this.cellIndex(var1);
// long var3 = this.data[var2];
// int var5 = (var1 - var2 * this.valuesPerLong) * this.bits;
// return (int)(var3 >> var5 & this.mask);
assert!(
index <= self.size - 1,
"Index {} out of bounds (max is {})",
index,
self.size - 1
);
let cell_index = self.cell_index(index as u64);
let cell = &self.data[cell_index as usize];
let bit_index = (index - cell_index * self.values_per_long as usize) * self.bits;
cell >> bit_index & self.mask
}
pub fn set(&mut self, index: usize, value: u64) {
// Validate.inclusiveBetween(0L, (long)(this.size - 1), (long)var1);
// Validate.inclusiveBetween(0L, this.mask, (long)var2);
// int var3 = this.cellIndex(var1);
// long var4 = this.data[var3];
// int var6 = (var1 - var3 * this.valuesPerLong) * this.bits;
// this.data[var3] = var4 & ~(this.mask << var6) | ((long)var2 & this.mask) << var6;
assert!(index <= self.size - 1);
assert!(value <= self.mask);
let cell_index = self.cell_index(index as u64);
let cell = &mut self.data[cell_index as usize];
let bit_index = (index - cell_index * self.values_per_long as usize) * self.bits;
*cell = *cell & !(self.mask << bit_index) | (value & self.mask) << bit_index;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wikivg_example() {
let data = [
1, 2, 2, 3, 4, 4, 5, 6, 6, 4, 8, 0, 7, 4, 3, 13, 15, 16, 9, 14, 10, 12, 0, 2,
];
let compact_data: [u64; 2] = [0x0020863148418841, 0x01018A7260F68C87];
let storage = BitStorage::new(5, data.len(), Some(compact_data.to_vec())).unwrap();
for (i, expected) in data.iter().enumerate() {
assert_eq!(storage.get(i), *expected);
}
}
}

237
azalea-world/src/lib.rs Normal file
View file

@ -0,0 +1,237 @@
#![feature(int_roundings)]
mod bit_storage;
mod palette;
use crate::palette::PalettedContainerType;
use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos};
use azalea_protocol::mc_buf::{McBufReadable, McBufWritable};
pub use bit_storage::BitStorage;
use palette::PalettedContainer;
use std::{
io::{Read, Write},
ops::{Index, IndexMut},
sync::{Arc, Mutex},
};
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
let result = 2 + 2;
assert_eq!(result, 4);
}
}
const SECTION_HEIGHT: u32 = 16;
pub struct World {
pub storage: ChunkStorage,
pub height: u32,
pub min_y: i32,
}
impl World {
pub fn replace_with_packet_data(
&mut self,
pos: &ChunkPos,
data: &mut impl Read,
) -> Result<(), String> {
if !self.storage.in_range(pos) {
println!(
"Ignoring chunk since it's not in the view range: {}, {}",
pos.x, pos.z
);
return Ok(());
}
// let existing_chunk = &self.storage[pos];
let chunk = Arc::new(Mutex::new(Chunk::read_with_world(data, self)?));
println!("Loaded chunk {:?}", pos);
self.storage[pos] = Some(chunk);
Ok(())
}
pub fn update_view_center(&mut self, pos: &ChunkPos) {
self.storage.view_center = *pos;
}
pub fn get_block_state(&self, pos: &BlockPos) -> Option<u32> {
self.storage.get_block_state(pos, self.min_y)
}
}
impl Index<&ChunkPos> for World {
type Output = Option<Arc<Mutex<Chunk>>>;
fn index(&self, pos: &ChunkPos) -> &Self::Output {
&self.storage[pos]
}
}
impl IndexMut<&ChunkPos> for World {
fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output {
&mut self.storage[pos]
}
}
// impl Index<&BlockPos> for World {
// type Output = Option<Arc<Mutex<Chunk>>>;
// fn index(&self, pos: &BlockPos) -> &Self::Output {
// let chunk = &self[ChunkPos::from(pos)];
// // chunk.
// }
// }
pub struct ChunkStorage {
view_center: ChunkPos,
chunk_radius: u32,
view_range: u32,
// chunks is a list of size chunk_radius * chunk_radius
chunks: Vec<Option<Arc<Mutex<Chunk>>>>,
}
// java moment
// it might be possible to replace this with just a modulo, but i copied java's floorMod just in case
fn floor_mod(x: i32, y: u32) -> u32 {
if x < 0 {
y - ((-x) as u32 % y)
} else {
x as u32 % y
}
}
impl ChunkStorage {
pub fn new(chunk_radius: u32) -> Self {
let view_range = chunk_radius * 2 + 1;
ChunkStorage {
view_center: ChunkPos::new(0, 0),
chunk_radius,
view_range,
chunks: vec![None; (view_range * view_range) as usize],
}
}
fn get_index(&self, chunk_pos: &ChunkPos) -> usize {
(floor_mod(chunk_pos.x, self.view_range) * self.view_range
+ floor_mod(chunk_pos.z, self.view_range)) as usize
}
pub fn in_range(&self, chunk_pos: &ChunkPos) -> bool {
(chunk_pos.x - self.view_center.x).unsigned_abs() <= self.chunk_radius
&& (chunk_pos.z - self.view_center.z).unsigned_abs() <= self.chunk_radius
}
pub fn get_block_state(&self, pos: &BlockPos, min_y: i32) -> Option<u32> {
let chunk_pos = ChunkPos::from(pos);
println!("chunk_pos {:?} block_pos {:?}", chunk_pos, pos);
let chunk = &self[&chunk_pos];
match chunk {
Some(chunk) => Some(chunk.lock().unwrap().get(&ChunkBlockPos::from(pos), min_y)),
None => None,
}
}
}
impl Index<&ChunkPos> for ChunkStorage {
type Output = Option<Arc<Mutex<Chunk>>>;
fn index(&self, pos: &ChunkPos) -> &Self::Output {
&self.chunks[self.get_index(pos)]
}
}
impl IndexMut<&ChunkPos> for ChunkStorage {
fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output {
let index = self.get_index(pos);
&mut self.chunks[index]
}
}
#[derive(Debug)]
pub struct Chunk {
pub sections: Vec<Section>,
}
impl Chunk {
pub fn read_with_world(buf: &mut impl Read, data: &World) -> Result<Self, String> {
Self::read_with_world_height(buf, data.height)
}
pub fn read_with_world_height(buf: &mut impl Read, world_height: u32) -> Result<Self, String> {
let section_count = world_height / SECTION_HEIGHT;
let mut sections = Vec::with_capacity(section_count as usize);
for _ in 0..section_count {
let section = Section::read_into(buf)?;
sections.push(section);
}
Ok(Chunk { sections })
}
pub fn section_index(&self, y: i32, min_y: i32) -> u32 {
// TODO: check the build height and stuff, this code will be broken if the min build height is 0
// (LevelHeightAccessor.getMinSection in vanilla code)
assert!(y >= 0);
let min_section_index = min_y.div_floor(16);
(y.div_floor(16) - min_section_index) as u32
}
pub fn get(&self, pos: &ChunkBlockPos, min_y: i32) -> u32 {
let section_index = self.section_index(pos.y, min_y);
// TODO: make sure the section exists
let section = &self.sections[section_index as usize];
let chunk_section_pos = ChunkSectionBlockPos::from(pos);
let block_state = section.get(chunk_section_pos);
block_state
}
}
impl McBufWritable for Chunk {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
for section in &self.sections {
section.write_into(buf)?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub struct Section {
pub block_count: u16,
pub states: PalettedContainer,
pub biomes: PalettedContainer,
}
impl McBufReadable for Section {
fn read_into(buf: &mut impl Read) -> Result<Self, String> {
let block_count = u16::read_into(buf)?;
// this is commented out because the vanilla server is wrong
// assert!(
// block_count <= 16 * 16 * 16,
// "A section has more blocks than what should be possible. This is a bug!"
// );
let states = PalettedContainer::read_with_type(buf, &PalettedContainerType::BlockStates)?;
let biomes = PalettedContainer::read_with_type(buf, &PalettedContainerType::Biomes)?;
Ok(Section {
block_count,
states,
biomes,
})
}
}
impl McBufWritable for Section {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
self.block_count.write_into(buf)?;
self.states.write_into(buf)?;
self.biomes.write_into(buf)?;
Ok(())
}
}
impl Section {
// TODO: return a BlockState instead of a u32
fn get(&self, pos: ChunkSectionBlockPos) -> u32 {
self.states
.get(pos.x as usize, pos.y as usize, pos.z as usize)
}
}

139
azalea-world/src/palette.rs Normal file
View file

@ -0,0 +1,139 @@
use azalea_protocol::mc_buf::{McBufReadable, McBufVarReadable, McBufWritable, Readable, Writable};
use std::io::{Read, Write};
use crate::BitStorage;
#[derive(Clone, Debug, Copy)]
pub enum PalettedContainerType {
Biomes,
BlockStates,
}
#[derive(Clone, Debug)]
pub struct PalettedContainer {
pub bits_per_entry: u8,
pub palette: Palette,
/// Compacted list of indices pointing to entry IDs in the Palette.
pub storage: BitStorage,
pub container_type: PalettedContainerType,
}
impl PalettedContainer {
pub fn read_with_type(
buf: &mut impl Read,
type_: &'static PalettedContainerType,
) -> Result<Self, String> {
let bits_per_entry = buf.read_byte()?;
let palette = match type_ {
PalettedContainerType::BlockStates => {
Palette::block_states_read_with_bits_per_entry(buf, bits_per_entry)?
}
PalettedContainerType::Biomes => {
Palette::biomes_read_with_bits_per_entry(buf, bits_per_entry)?
}
};
let size = match type_ {
PalettedContainerType::BlockStates => 4096,
PalettedContainerType::Biomes => 64,
};
let data = Vec::<u64>::read_into(buf)?;
debug_assert!(
bits_per_entry != 0 || data.is_empty(),
"Bits per entry is 0 but data is not empty."
);
let storage = BitStorage::new(bits_per_entry.into(), size, Some(data)).unwrap();
Ok(PalettedContainer {
bits_per_entry,
palette,
storage,
container_type: *type_,
})
}
pub fn get_index(&self, x: usize, y: usize, z: usize) -> usize {
let size_bits = match self.container_type {
PalettedContainerType::BlockStates => 4,
PalettedContainerType::Biomes => 2,
};
(((y << size_bits) | z) << size_bits) | x
}
pub fn get(&self, x: usize, y: usize, z: usize) -> u32 {
let paletted_value = self.storage.get(self.get_index(x, y, z));
println!("palette: {:?}", self.palette);
self.palette.value_for(paletted_value as usize)
}
}
impl McBufWritable for PalettedContainer {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
buf.write_byte(self.bits_per_entry)?;
self.palette.write_into(buf)?;
self.storage.data.write_into(buf)?;
Ok(())
}
}
#[derive(Clone, Debug)]
pub enum Palette {
/// ID of the corresponding entry in its global palette
SingleValue(u32),
Linear(Vec<u32>),
Hashmap(Vec<u32>),
Global,
}
impl Palette {
pub fn block_states_read_with_bits_per_entry(
buf: &mut impl Read,
bits_per_entry: u8,
) -> Result<Palette, String> {
Ok(match bits_per_entry {
0 => Palette::SingleValue(u32::var_read_into(buf)?),
1..=4 => Palette::Linear(Vec::<u32>::var_read_into(buf)?),
5..=8 => Palette::Hashmap(Vec::<u32>::var_read_into(buf)?),
_ => Palette::Global,
})
}
pub fn biomes_read_with_bits_per_entry(
buf: &mut impl Read,
bits_per_entry: u8,
) -> Result<Palette, String> {
Ok(match bits_per_entry {
0 => Palette::SingleValue(u32::var_read_into(buf)?),
1..=3 => Palette::Linear(Vec::<u32>::var_read_into(buf)?),
_ => Palette::Global,
})
}
pub fn value_for(&self, value: usize) -> u32 {
match self {
Palette::SingleValue(v) => *v,
Palette::Linear(v) => v[value],
Palette::Hashmap(v) => v[value],
Palette::Global => value as u32,
}
}
}
impl McBufWritable for Palette {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
match self {
Palette::SingleValue(value) => {
value.write_into(buf)?;
}
Palette::Linear(values) => {
values.write_into(buf)?;
}
Palette::Hashmap(values) => {
values.write_into(buf)?;
}
Palette::Global => {}
}
Ok(())
}
}

View file

@ -7,5 +7,6 @@ version = "0.1.0"
[dependencies] [dependencies]
azalea-client = {path = "../azalea-client"} azalea-client = {path = "../azalea-client"}
azalea-core = {path = "../azalea-core"}
azalea-protocol = {path = "../azalea-protocol"} azalea-protocol = {path = "../azalea-protocol"}
tokio = "^1.14.0" tokio = "^1.14.0"

View file

@ -1,11 +1,12 @@
use azalea_client::{Account, Event}; use azalea_client::{Account, Event};
use azalea_core::BlockPos;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
println!("Hello, world!"); println!("Hello, world!");
// let address = "95.111.249.143:10000"; // let address = "95.111.249.143:10000";
let address = "172.23.192.1:61385"; let address = "192.168.2.234:50736";
// 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();
@ -17,9 +18,16 @@ async fn main() {
while let Some(e) = client.next().await { while let Some(e) = client.next().await {
match e { match e {
// TODO: have a "loaded" or "ready" event that fires when all chunks are loaded
Event::Login => {} Event::Login => {}
Event::Chat(p) => { Event::Chat(p) => {
println!("{}", p.message.to_ansi(None)); println!("{}", p.message.to_ansi(None));
if p.message.to_ansi(None) == "<py5> ok" {
let state = client.state.lock().await;
let world = state.world.as_ref().unwrap();
let c = world.get_block_state(&BlockPos::new(5, 78, -2)).unwrap();
println!("block state: {:?}", c);
}
} }
} }
} }

View file

@ -84,8 +84,8 @@ def generate(burger_packets, mappings: Mappings, target_packet_id, target_packet
generated_packet_code = [] generated_packet_code = []
uses = set() uses = set()
generated_packet_code.append( generated_packet_code.append(
f'#[derive(Clone, Debug, {to_camel_case(state)}Packet)]') f'#[derive(Clone, Debug, McBuf, {to_camel_case(state)}Packet)]')
uses.add(f'packet_macros::{to_camel_case(state)}Packet') uses.add(f'packet_macros::{{{to_camel_case(state)}Packet, McBuf}}')
obfuscated_class_name = packet['class'].split('.')[0].split('$')[0] obfuscated_class_name = packet['class'].split('.')[0].split('$')[0]
class_name = mappings.get_class( class_name = mappings.get_class(