From f993e79a7e6acc7aadd1e6cf9462d7a3e2c3ac3e Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 17 Jun 2022 20:46:16 -0500 Subject: Create azalea-entity --- azalea-client/src/entity.rs | 8 -------- azalea-client/src/lib.rs | 2 -- azalea-client/src/player.rs | 2 +- 3 files changed, 1 insertion(+), 11 deletions(-) delete mode 100644 azalea-client/src/entity.rs (limited to 'azalea-client/src') diff --git a/azalea-client/src/entity.rs b/azalea-client/src/entity.rs deleted file mode 100644 index d91f556f..00000000 --- a/azalea-client/src/entity.rs +++ /dev/null @@ -1,8 +0,0 @@ -use azalea_core::EntityPos; - -#[derive(Default, Debug)] -pub struct Entity { - /// The incremental numerical id of the entity. - pub id: u32, - pub pos: EntityPos, -} diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs index 5814687a..db935897 100755 --- a/azalea-client/src/lib.rs +++ b/azalea-client/src/lib.rs @@ -1,12 +1,10 @@ //! Significantly abstract azalea-protocol so it's actually useable for bots. mod connect; -mod entity; pub mod ping; mod player; pub use connect::{Account, Client, Event}; -pub use entity::Entity; pub use player::Player; #[cfg(test)] diff --git a/azalea-client/src/player.rs b/azalea-client/src/player.rs index 04d34f6d..9a5ba8ab 100644 --- a/azalea-client/src/player.rs +++ b/azalea-client/src/player.rs @@ -1,4 +1,4 @@ -use crate::Entity; +use azalea_entity::Entity; #[derive(Default, Debug)] pub struct Player { -- cgit v1.2.3 From 614b21129804930159f041ce3f51202bc3e1c0b6 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 17 Jun 2022 23:55:11 -0500 Subject: EntityStorage --- Cargo.lock | 8 ++ azalea-client/src/connect.rs | 4 +- azalea-entity/src/lib.rs | 13 +++- azalea-world/Cargo.toml | 7 +- azalea-world/src/chunk.rs | 182 +++++++++++++++++++++++++++++++++++++++++++ azalea-world/src/entity.rs | 44 +++++++++++ azalea-world/src/lib.rs | 177 ++--------------------------------------- 7 files changed, 260 insertions(+), 175 deletions(-) create mode 100644 azalea-world/src/chunk.rs create mode 100644 azalea-world/src/entity.rs (limited to 'azalea-client/src') diff --git a/Cargo.lock b/Cargo.lock index 85dc7a98..c63ef7a8 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -193,8 +193,10 @@ version = "0.1.0" dependencies = [ "azalea-block", "azalea-core", + "azalea-entity", "azalea-nbt", "azalea-protocol", + "nohash-hasher", ] [[package]] @@ -771,6 +773,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + [[package]] name = "num" version = "0.4.0" diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs index 1551ca69..2deaab68 100755 --- a/azalea-client/src/connect.rs +++ b/azalea-client/src/connect.rs @@ -19,7 +19,7 @@ use azalea_protocol::{ }, resolver, ServerAddress, }; -use azalea_world::{ChunkStorage, World}; +use azalea_world::{ChunkStorage, EntityStorage, World}; use std::{fmt::Debug, sync::Arc}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; use tokio::sync::Mutex; @@ -266,6 +266,7 @@ impl Client { height, min_y, storage: ChunkStorage::new(16), + entities: EntityStorage::new(), }); conn.lock() @@ -356,6 +357,7 @@ impl Client { y: p.y, z: p.z, }; + p.id; } GamePacket::ClientboundSetEntityDataPacket(p) => { // println!("Got set entity data packet {:?}", p); diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index 506f5780..0e0178b5 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -4,7 +4,18 @@ use azalea_core::EntityPos; pub struct Entity { /// The incrementing numerical id of the entity. pub id: u32, - pub pos: EntityPos, + pos: EntityPos, +} + +impl Entity { + pub fn pos(&self) -> &EntityPos { + &self.pos + } + + pub fn set_pos(&mut self, pos: EntityPos) { + // TODO: check if it moved to another chunk + self.pos = pos; + } } // #[cfg(test)] diff --git a/azalea-world/Cargo.toml b/azalea-world/Cargo.toml index 79e6155d..e5e9da1d 100644 --- a/azalea-world/Cargo.toml +++ b/azalea-world/Cargo.toml @@ -6,7 +6,12 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +azalea-block = {path = "../azalea-block"} azalea-core = {path = "../azalea-core"} +azalea-entity = {path = "../azalea-entity"} azalea-nbt = {path = "../azalea-nbt"} azalea-protocol = {path = "../azalea-protocol"} -azalea-block = {path = "../azalea-block"} +nohash-hasher = "0.2.0" + +[profile.release] +lto = true diff --git a/azalea-world/src/chunk.rs b/azalea-world/src/chunk.rs new file mode 100644 index 00000000..96bbd922 --- /dev/null +++ b/azalea-world/src/chunk.rs @@ -0,0 +1,182 @@ +use crate::bit_storage::BitStorage; +use crate::palette::PalettedContainer; +use crate::palette::PalettedContainerType; +use crate::World; +use azalea_block::BlockState; +use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos}; +use azalea_protocol::mc_buf::{McBufReadable, McBufWritable}; +use std::{ + io::{Read, Write}, + ops::{Index, IndexMut}, + sync::{Arc, Mutex}, +}; + +const SECTION_HEIGHT: u32 = 16; + +pub struct ChunkStorage { + pub view_center: ChunkPos, + chunk_radius: u32, + view_range: u32, + // chunks is a list of size chunk_radius * chunk_radius + chunks: Vec>>>, +} + +// 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 { + 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>>; + + 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
, +} + +impl Chunk { + pub fn read_with_world(buf: &mut impl Read, data: &World) -> Result { + Self::read_with_world_height(buf, data.height) + } + + pub fn read_with_world_height(buf: &mut impl Read, world_height: u32) -> Result { + 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) -> BlockState { + 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 { + 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)?; + + for i in 0..states.storage.size() { + if !BlockState::is_valid_state(states.storage.get(i) as u32) { + return Err(format!( + "Invalid block state {} (index {}) found in section.", + states.storage.get(i), + i + )); + } + } + + 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 { + fn get(&self, pos: ChunkSectionBlockPos) -> BlockState { + // TODO: use the unsafe method and do the check earlier + self.states + .get(pos.x as usize, pos.y as usize, pos.z as usize) + .try_into() + .expect("Invalid block state.") + } +} diff --git a/azalea-world/src/entity.rs b/azalea-world/src/entity.rs new file mode 100644 index 00000000..a25b1f40 --- /dev/null +++ b/azalea-world/src/entity.rs @@ -0,0 +1,44 @@ +use std::collections::HashMap; + +use azalea_core::ChunkPos; +use azalea_entity::Entity; +use nohash_hasher::IntMap; + +pub struct EntityStorage { + by_id: IntMap, + // TODO: this doesn't work yet (should be updated in the set_pos method in azalea-entity) + by_chunk: HashMap, +} + +impl EntityStorage { + pub fn new() -> Self { + Self { + by_id: IntMap::default(), + by_chunk: HashMap::default(), + } + } + + /// Add an entity to the storage. + #[inline] + pub fn insert(&mut self, entity: Entity) { + self.by_id.insert(entity.id, entity); + } + + /// Remove an entity from the storage by its id. + #[inline] + pub fn remove_by_id(&mut self, id: u32) { + self.by_id.remove(&id); + } + + /// Get a reference to an entity by its id. + #[inline] + pub fn get_by_id(&self, id: u32) -> Option<&Entity> { + self.by_id.get(&id) + } + + /// Get a mutable reference to an entity by its id. + #[inline] + pub fn get_mut_by_id(&mut self, id: u32) -> Option<&mut Entity> { + self.by_id.get_mut(&id) + } +} diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index e651e455..b47126d4 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -1,14 +1,16 @@ #![feature(int_roundings)] mod bit_storage; +mod chunk; +mod entity; mod palette; -use crate::palette::PalettedContainerType; use azalea_block::BlockState; use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos}; use azalea_protocol::mc_buf::{McBufReadable, McBufWritable}; pub use bit_storage::BitStorage; -use palette::PalettedContainer; +pub use chunk::{Chunk, ChunkStorage}; +pub use entity::EntityStorage; use std::{ io::{Read, Write}, ops::{Index, IndexMut}, @@ -24,10 +26,9 @@ mod tests { } } -const SECTION_HEIGHT: u32 = 16; - pub struct World { pub storage: ChunkStorage, + pub entities: EntityStorage, pub height: u32, pub min_y: i32, } @@ -83,171 +84,3 @@ impl IndexMut<&ChunkPos> for World { // } // } - -pub struct ChunkStorage { - view_center: ChunkPos, - chunk_radius: u32, - view_range: u32, - // chunks is a list of size chunk_radius * chunk_radius - chunks: Vec>>>, -} - -// 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 { - 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>>; - - 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
, -} - -impl Chunk { - pub fn read_with_world(buf: &mut impl Read, data: &World) -> Result { - Self::read_with_world_height(buf, data.height) - } - - pub fn read_with_world_height(buf: &mut impl Read, world_height: u32) -> Result { - 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) -> BlockState { - 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 { - 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)?; - - for i in 0..states.storage.size() { - if !BlockState::is_valid_state(states.storage.get(i) as u32) { - return Err(format!( - "Invalid block state {} (index {}) found in section.", - states.storage.get(i), - i - )); - } - } - - 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 { - fn get(&self, pos: ChunkSectionBlockPos) -> BlockState { - // TODO: use the unsafe method and do the check earlier - self.states - .get(pos.x as usize, pos.y as usize, pos.z as usize) - .try_into() - .expect("Invalid block state.") - } -} -- cgit v1.2.3 From d8e0457b62f5826183671e5b05890b400be35709 Mon Sep 17 00:00:00 2001 From: mat Date: Sat, 18 Jun 2022 14:08:34 -0500 Subject: from for Entity --- Cargo.lock | 6 ++++-- azalea-auth/Cargo.toml | 2 +- azalea-client/src/connect.rs | 16 ++++++++++------ azalea-core/Cargo.toml | 2 +- azalea-entity/Cargo.toml | 6 ++++++ azalea-entity/src/lib.rs | 23 +++++++++++++++++++++++ azalea-protocol/Cargo.toml | 2 +- bot/src/main.rs | 4 ++-- 8 files changed, 48 insertions(+), 13 deletions(-) (limited to 'azalea-client/src') diff --git a/Cargo.lock b/Cargo.lock index c63ef7a8..74375949 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,6 +138,8 @@ name = "azalea-entity" version = "0.1.0" dependencies = [ "azalea-core", + "azalea-protocol", + "uuid", ] [[package]] @@ -1436,9 +1438,9 @@ dependencies = [ [[package]] name = "uuid" -version = "0.8.2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" [[package]] name = "version_check" diff --git a/azalea-auth/Cargo.toml b/azalea-auth/Cargo.toml index aa9b7bdb..a7a11b53 100755 --- a/azalea-auth/Cargo.toml +++ b/azalea-auth/Cargo.toml @@ -6,4 +6,4 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -uuid = "^0.8.2" +uuid = "^1.1.2" diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs index 2deaab68..dd3162eb 100755 --- a/azalea-client/src/connect.rs +++ b/azalea-client/src/connect.rs @@ -1,5 +1,6 @@ use crate::Player; use azalea_core::{resource_location::ResourceLocation, ChunkPos, EntityPos}; +use azalea_entity::Entity; use azalea_protocol::{ connect::{GameConnection, HandshakeConnection}, packets::{ @@ -352,12 +353,15 @@ impl Client { } GamePacket::ClientboundAddEntityPacket(p) => { println!("Got add entity packet {:?}", p); - let pos = EntityPos { - x: p.x, - y: p.y, - z: p.z, - }; - p.id; + let entity = Entity::from(p); + state + .lock() + .await + .world + .as_mut() + .expect("World doesn't exist! We should've gotten a login packet by now.") + .entities + .insert(entity); } GamePacket::ClientboundSetEntityDataPacket(p) => { // println!("Got set entity data packet {:?}", p); diff --git a/azalea-core/Cargo.toml b/azalea-core/Cargo.toml index d652a46e..27112b21 100755 --- a/azalea-core/Cargo.toml +++ b/azalea-core/Cargo.toml @@ -8,4 +8,4 @@ version = "0.1.0" [dependencies] azalea-chat = {path = "../azalea-chat"} azalea-nbt = {path = "../azalea-nbt"} -uuid = "^0.8.2" +uuid = "^1.1.2" diff --git a/azalea-entity/Cargo.toml b/azalea-entity/Cargo.toml index bf1e64bc..ae23267e 100644 --- a/azalea-entity/Cargo.toml +++ b/azalea-entity/Cargo.toml @@ -7,3 +7,9 @@ version = "0.1.0" [dependencies] azalea-core = {path = "../azalea-core"} +azalea-protocol = {path = "../azalea-protocol", optional = true} +uuid = "^1.1.2" + +[features] +default = ["protocol"] +protocol = ["dep:azalea-protocol"] diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index 0e0178b5..f9d808c2 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -1,9 +1,13 @@ use azalea_core::EntityPos; +#[cfg(feature = "protocol")] +use azalea_protocol::packets::game::clientbound_add_entity_packet::ClientboundAddEntityPacket; +use uuid::Uuid; #[derive(Default, Debug)] pub struct Entity { /// The incrementing numerical id of the entity. pub id: u32, + pub uuid: Uuid, pos: EntityPos, } @@ -18,6 +22,25 @@ impl Entity { } } +#[cfg(feature = "protocol")] +impl From<&azalea_protocol::packets::game::clientbound_add_entity_packet::ClientboundAddEntityPacket> + for Entity +{ + fn from( + p: &azalea_protocol::packets::game::clientbound_add_entity_packet::ClientboundAddEntityPacket, + ) -> Self { + Self { + id: p.id, + uuid: p.uuid, + pos: EntityPos { + x: p.x, + y: p.y, + z: p.z, + }, + } + } +} + // #[cfg(test)] // mod tests { // #[test] diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml index 899b44a8..41879b61 100755 --- a/azalea-protocol/Cargo.toml +++ b/azalea-protocol/Cargo.toml @@ -26,4 +26,4 @@ thiserror = "^1.0.30" tokio = {version = "^1.14.0", features = ["io-util", "net", "macros"]} tokio-util = "^0.6.9" trust-dns-resolver = "^0.20.3" -uuid = "^0.8.2" +uuid = "^1.1.2" diff --git a/bot/src/main.rs b/bot/src/main.rs index 2618675e..287dd4f6 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -6,7 +6,7 @@ async fn main() { println!("Hello, world!"); // let address = "95.111.249.143:10000"; - let address = "localhost:51028"; + let address = "localhost:52909"; // let response = azalea_client::ping::ping_server(&address.try_into().unwrap()) // .await // .unwrap(); @@ -23,7 +23,7 @@ async fn main() { Event::Chat(p) => { let state = client.state.lock().await; let world = state.world.as_ref().unwrap(); - // println!("{:?}", state.player.entity); + println!("{:?}", state.player.entity()); // world.get_block_state(state.player.entity.pos); // println!("{}", p.message.to_ansi(None)); // if p.message.to_ansi(None) == " ok" { -- cgit v1.2.3 From fc3151f89db1cf018bfebebb8f102e20911e64d3 Mon Sep 17 00:00:00 2001 From: mat Date: Sat, 18 Jun 2022 16:54:49 -0500 Subject: account.rs and client.rs --- azalea-client/src/account.rs | 27 +++ azalea-client/src/client.rs | 465 +++++++++++++++++++++++++++++++++++++++++ azalea-client/src/connect.rs | 483 ------------------------------------------- azalea-client/src/lib.rs | 6 +- azalea-world/src/entity.rs | 1 + bot/src/main.rs | 5 +- 6 files changed, 499 insertions(+), 488 deletions(-) create mode 100644 azalea-client/src/account.rs create mode 100644 azalea-client/src/client.rs delete mode 100755 azalea-client/src/connect.rs (limited to 'azalea-client/src') diff --git a/azalea-client/src/account.rs b/azalea-client/src/account.rs new file mode 100644 index 00000000..00a5d0f6 --- /dev/null +++ b/azalea-client/src/account.rs @@ -0,0 +1,27 @@ +use crate::Client; +use azalea_protocol::{ + packets::game::{ + clientbound_player_chat_packet::ClientboundPlayerChatPacket, + clientbound_system_chat_packet::ClientboundSystemChatPacket, + }, + ServerAddress, +}; +use std::fmt::Debug; + +///! Connect to Minecraft servers. + +/// Something that can join Minecraft servers. +pub struct Account { + pub username: String, +} +impl Account { + pub fn offline(username: &str) -> Self { + Self { + username: username.to_string(), + } + } + + pub async fn join(&self, address: &ServerAddress) -> Result { + Client::join(self, address).await + } +} diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs new file mode 100644 index 00000000..ff8729cb --- /dev/null +++ b/azalea-client/src/client.rs @@ -0,0 +1,465 @@ +use crate::{Account, Player}; +use azalea_core::{resource_location::ResourceLocation, ChunkPos}; +use azalea_entity::Entity; +use azalea_protocol::{ + connect::{GameConnection, HandshakeConnection}, + packets::{ + game::{ + clientbound_player_chat_packet::ClientboundPlayerChatPacket, + clientbound_system_chat_packet::ClientboundSystemChatPacket, + serverbound_custom_payload_packet::ServerboundCustomPayloadPacket, + serverbound_keep_alive_packet::ServerboundKeepAlivePacket, GamePacket, + }, + handshake::client_intention_packet::ClientIntentionPacket, + login::{ + serverbound_hello_packet::ServerboundHelloPacket, + serverbound_key_packet::{NonceOrSaltSignature, ServerboundKeyPacket}, + LoginPacket, + }, + ConnectionProtocol, PROTOCOL_VERSION, + }, + resolver, ServerAddress, +}; +use azalea_world::{ChunkStorage, EntityStorage, World}; +use std::{fmt::Debug, sync::Arc}; +use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use tokio::sync::Mutex; + +#[derive(Default)] +pub struct ClientState { + pub player: Player, + pub world: Option, +} + +#[derive(Debug, Clone)] +pub enum Event { + Login, + Chat(ChatPacket), +} + +#[derive(Debug, Clone)] +pub enum ChatPacket { + System(ClientboundSystemChatPacket), + Player(ClientboundPlayerChatPacket), +} + +// impl ChatPacket { +// pub fn message(&self) -> &str { +// match self { +// ChatPacket::System(p) => &p.content, +// ChatPacket::Player(p) => &p.message, +// } +// } +// } + +/// A player that you can control that is currently in a Minecraft server. +pub struct Client { + event_receiver: UnboundedReceiver, + pub conn: Arc>, + pub state: Arc>, + // game_loop +} + +/// Whether we should ignore errors when decoding packets. +const IGNORE_ERRORS: bool = !cfg!(debug_assertions); + +impl Client { + /// Connect to a Minecraft server with an account. + pub async fn join(account: &Account, address: &ServerAddress) -> Result { + let resolved_address = resolver::resolve_address(address).await?; + + let mut conn = HandshakeConnection::new(&resolved_address).await?; + + // handshake + conn.write( + ClientIntentionPacket { + protocol_version: PROTOCOL_VERSION, + hostname: address.host.clone(), + port: address.port, + intention: ConnectionProtocol::Login, + } + .get(), + ) + .await; + let mut conn = conn.login(); + + // login + conn.write( + ServerboundHelloPacket { + username: account.username.clone(), + public_key: None, + } + .get(), + ) + .await; + + let conn = loop { + let packet_result = conn.read().await; + match packet_result { + Ok(packet) => match packet { + LoginPacket::ClientboundHelloPacket(p) => { + println!("Got encryption request"); + let e = azalea_crypto::encrypt(&p.public_key, &p.nonce).unwrap(); + + // TODO: authenticate with the server here (authenticateServer) + + conn.write( + ServerboundKeyPacket { + nonce_or_salt_signature: NonceOrSaltSignature::Nonce( + e.encrypted_nonce, + ), + key_bytes: e.encrypted_public_key, + } + .get(), + ) + .await; + conn.set_encryption_key(e.secret_key); + } + LoginPacket::ClientboundLoginCompressionPacket(p) => { + println!("Got compression request {:?}", p.compression_threshold); + conn.set_compression_threshold(p.compression_threshold); + } + LoginPacket::ClientboundGameProfilePacket(p) => { + println!("Got profile {:?}", p.game_profile); + break conn.game(); + } + LoginPacket::ClientboundLoginDisconnectPacket(p) => { + println!("Got disconnect {:?}", p); + } + LoginPacket::ClientboundCustomQueryPacket(p) => { + println!("Got custom query {:?}", p); + } + _ => panic!("Unexpected packet {:?}", packet), + }, + Err(e) => { + panic!("Error: {:?}", e); + } + } + }; + + let conn = Arc::new(Mutex::new(conn)); + + let (tx, rx) = mpsc::unbounded_channel(); + + // we got the GameConnection, so the server is now connected :) + let client = Client { + event_receiver: rx, + conn: conn.clone(), + state: Arc::new(Mutex::new(ClientState::default())), + }; + // let client = Arc::new(Mutex::new(client)); + // let weak_client = Arc::<_>::downgrade(&client); + + // just start up the game loop and we're ready! + // tokio::spawn(Self::game_loop(conn, tx, handler, state)) + + let game_loop_state = client.state.clone(); + + tokio::spawn(Self::game_loop(conn, tx, game_loop_state)); + + Ok(client) + } + + async fn game_loop( + conn: Arc>, + tx: UnboundedSender, + state: Arc>, + ) { + loop { + let r = conn.lock().await.read().await; + match r { + Ok(packet) => Self::handle(&packet, &tx, &state, &conn).await, + Err(e) => { + if IGNORE_ERRORS { + println!("Error: {:?}", e); + if e == "length wider than 21-bit" { + panic!(); + } + } else { + panic!("Error: {:?}", e); + } + } + }; + } + } + + async fn handle( + packet: &GamePacket, + tx: &UnboundedSender, + state: &Arc>, + conn: &Arc>, + ) { + match packet { + GamePacket::ClientboundLoginPacket(p) => { + println!("Got login packet {:?}", p); + + let mut state = state.lock().await; + + // // write p into login.txt + // std::io::Write::write_all( + // &mut std::fs::File::create("login.txt").unwrap(), + // format!("{:#?}", p).as_bytes(), + // ) + // .unwrap(); + + state.player.entity.id = p.player_id; + + // TODO: have registry_holder be a struct because this sucks rn + // best way would be to add serde support to azalea-nbt + + let registry_holder = p + .registry_holder + .as_compound() + .expect("Registry holder is not a compound") + .get("") + .expect("No \"\" tag") + .as_compound() + .expect("\"\" tag is not a compound"); + let dimension_types = registry_holder + .get("minecraft:dimension_type") + .expect("No dimension_type tag") + .as_compound() + .expect("dimension_type is not a compound") + .get("value") + .expect("No dimension_type value") + .as_list() + .expect("dimension_type value is not a list"); + let dimension_type = dimension_types + .iter() + .find(|t| { + t.as_compound() + .expect("dimension_type value is not a compound") + .get("name") + .expect("No name tag") + .as_string() + .expect("name is not a string") + == p.dimension_type.to_string() + }) + .expect(&format!("No dimension_type with name {}", p.dimension_type)) + .as_compound() + .unwrap() + .get("element") + .expect("No element tag") + .as_compound() + .expect("element is not a compound"); + let height = (*dimension_type + .get("height") + .expect("No height tag") + .as_int() + .expect("height tag is not an 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 an int")) + .try_into() + .expect("min_y is not an i32"); + + state.world = Some(World { + height, + min_y, + storage: ChunkStorage::new(16), + entities: EntityStorage::new(), + }); + + conn.lock() + .await + .write( + ServerboundCustomPayloadPacket { + identifier: ResourceLocation::new("brand").unwrap(), + // they don't have to know :) + data: "vanilla".into(), + } + .get(), + ) + .await; + + tx.send(Event::Login).unwrap(); + } + GamePacket::ClientboundUpdateViewDistancePacket(p) => { + println!("Got view distance packet {:?}", p); + } + GamePacket::ClientboundCustomPayloadPacket(p) => { + println!("Got custom payload packet {:?}", p); + } + GamePacket::ClientboundChangeDifficultyPacket(p) => { + println!("Got difficulty packet {:?}", p); + } + GamePacket::ClientboundDeclareCommandsPacket(_p) => { + println!("Got declare commands packet"); + } + GamePacket::ClientboundPlayerAbilitiesPacket(p) => { + println!("Got player abilities packet {:?}", p); + } + GamePacket::ClientboundSetCarriedItemPacket(p) => { + println!("Got set carried item packet {:?}", p); + } + GamePacket::ClientboundUpdateTagsPacket(_p) => { + println!("Got update tags packet"); + } + GamePacket::ClientboundDisconnectPacket(p) => { + println!("Got disconnect packet {:?}", p); + } + GamePacket::ClientboundUpdateRecipesPacket(_p) => { + println!("Got update recipes packet"); + } + GamePacket::ClientboundEntityEventPacket(p) => { + // println!("Got entity event packet {:?}", p); + } + GamePacket::ClientboundRecipePacket(_p) => { + println!("Got recipe packet"); + } + GamePacket::ClientboundPlayerPositionPacket(p) => { + // TODO: reply with teleport confirm + println!("Got player position packet {:?}", p); + } + GamePacket::ClientboundPlayerInfoPacket(p) => { + println!("Got player info packet {:?}", p); + } + GamePacket::ClientboundSetChunkCacheCenterPacket(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) => { + 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) => { + println!("Got light update packet {:?}", p); + } + GamePacket::ClientboundAddEntityPacket(p) => { + println!("Got add entity packet {:?}", p); + let entity = Entity::from(p); + state + .lock() + .await + .world + .as_mut() + .expect("World doesn't exist! We should've gotten a login packet by now.") + .entities + .insert(entity); + } + GamePacket::ClientboundSetEntityDataPacket(p) => { + // println!("Got set entity data packet {:?}", p); + } + GamePacket::ClientboundUpdateAttributesPacket(p) => { + // println!("Got update attributes packet {:?}", p); + } + GamePacket::ClientboundEntityVelocityPacket(p) => { + // println!("Got entity velocity packet {:?}", p); + } + GamePacket::ClientboundSetEntityLinkPacket(p) => { + println!("Got set entity link packet {:?}", p); + } + GamePacket::ClientboundAddPlayerPacket(p) => { + println!("Got add player packet {:?}", p); + } + GamePacket::ClientboundInitializeBorderPacket(p) => { + println!("Got initialize border packet {:?}", p); + } + GamePacket::ClientboundSetTimePacket(p) => { + println!("Got set time packet {:?}", p); + } + GamePacket::ClientboundSetDefaultSpawnPositionPacket(p) => { + println!("Got set default spawn position packet {:?}", p); + } + GamePacket::ClientboundContainerSetContentPacket(p) => { + println!("Got container set content packet {:?}", p); + } + GamePacket::ClientboundSetHealthPacket(p) => { + println!("Got set health packet {:?}", p); + } + GamePacket::ClientboundSetExperiencePacket(p) => { + println!("Got set experience packet {:?}", p); + } + GamePacket::ClientboundTeleportEntityPacket(p) => { + // println!("Got teleport entity packet {:?}", p); + } + GamePacket::ClientboundUpdateAdvancementsPacket(p) => { + println!("Got update advancements packet {:?}", p); + } + GamePacket::ClientboundRotateHeadPacket(p) => { + // println!("Got rotate head packet {:?}", p); + } + GamePacket::ClientboundMoveEntityPosPacket(p) => { + // println!("Got move entity pos packet {:?}", p); + } + GamePacket::ClientboundMoveEntityPosRotPacket(p) => { + // println!("Got move entity pos rot packet {:?}", p); + } + GamePacket::ClientboundMoveEntityRotPacket(p) => { + println!("Got move entity rot packet {:?}", p); + } + GamePacket::ClientboundKeepAlivePacket(p) => { + println!("Got keep alive packet {:?}", p); + conn.lock() + .await + .write(ServerboundKeepAlivePacket { id: p.id }.get()) + .await; + } + GamePacket::ClientboundRemoveEntitiesPacket(p) => { + println!("Got remove entities packet {:?}", p); + } + GamePacket::ClientboundPlayerChatPacket(p) => { + println!("Got player chat packet {:?}", p); + tx.send(Event::Chat(ChatPacket::Player(p.clone()))).unwrap(); + } + GamePacket::ClientboundSystemChatPacket(p) => { + println!("Got system chat packet {:?}", p); + tx.send(Event::Chat(ChatPacket::System(p.clone()))).unwrap(); + } + GamePacket::ClientboundSoundPacket(p) => { + println!("Got sound packet {:?}", p); + } + GamePacket::ClientboundLevelEventPacket(p) => { + println!("Got level event packet {:?}", p); + } + GamePacket::ClientboundBlockUpdatePacket(p) => { + println!("Got block update packet {:?}", p); + // TODO: update world + } + GamePacket::ClientboundAnimatePacket(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); + } + GamePacket::ClientboundServerDataPacket(p) => { + println!("Got server data packet {:?}", p); + } + GamePacket::ClientboundSetEquipmentPacket(p) => { + println!("Got set equipment packet {:?}", p); + } + _ => panic!("Unexpected packet {:?}", packet), + } + } + + pub async fn next(&mut self) -> Option { + self.event_receiver.recv().await + } +} diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs deleted file mode 100755 index dd3162eb..00000000 --- a/azalea-client/src/connect.rs +++ /dev/null @@ -1,483 +0,0 @@ -use crate::Player; -use azalea_core::{resource_location::ResourceLocation, ChunkPos, EntityPos}; -use azalea_entity::Entity; -use azalea_protocol::{ - connect::{GameConnection, HandshakeConnection}, - packets::{ - game::{ - clientbound_player_chat_packet::ClientboundPlayerChatPacket, - clientbound_system_chat_packet::ClientboundSystemChatPacket, - serverbound_custom_payload_packet::ServerboundCustomPayloadPacket, - serverbound_keep_alive_packet::ServerboundKeepAlivePacket, GamePacket, - }, - handshake::client_intention_packet::ClientIntentionPacket, - login::{ - serverbound_hello_packet::ServerboundHelloPacket, - serverbound_key_packet::{NonceOrSaltSignature, ServerboundKeyPacket}, - LoginPacket, - }, - ConnectionProtocol, PROTOCOL_VERSION, - }, - resolver, ServerAddress, -}; -use azalea_world::{ChunkStorage, EntityStorage, World}; -use std::{fmt::Debug, sync::Arc}; -use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; -use tokio::sync::Mutex; - -///! Connect to Minecraft servers. - -/// Something that can join Minecraft servers. -pub struct Account { - username: String, -} - -#[derive(Default)] -pub struct ClientState { - pub player: Player, - pub world: Option, -} - -/// A player that you can control that is currently in a Minecraft server. -pub struct Client { - event_receiver: UnboundedReceiver, - pub conn: Arc>, - pub state: Arc>, - // game_loop -} - -#[derive(Debug, Clone)] -pub enum ChatPacket { - System(ClientboundSystemChatPacket), - Player(ClientboundPlayerChatPacket), -} - -// impl ChatPacket { -// pub fn message(&self) -> &str { -// match self { -// ChatPacket::System(p) => &p.content, -// ChatPacket::Player(p) => &p.message, -// } -// } -// } - -#[derive(Debug, Clone)] -pub enum Event { - Login, - Chat(ChatPacket), -} - -/// Whether we should ignore errors when decoding packets. -const IGNORE_ERRORS: bool = false; - -impl Client { - async fn join(account: &Account, address: &ServerAddress) -> Result { - let resolved_address = resolver::resolve_address(address).await?; - - let mut conn = HandshakeConnection::new(&resolved_address).await?; - - // handshake - conn.write( - ClientIntentionPacket { - protocol_version: PROTOCOL_VERSION, - hostname: address.host.clone(), - port: address.port, - intention: ConnectionProtocol::Login, - } - .get(), - ) - .await; - let mut conn = conn.login(); - - // login - conn.write( - ServerboundHelloPacket { - username: account.username.clone(), - public_key: None, - } - .get(), - ) - .await; - - let conn = loop { - let packet_result = conn.read().await; - match packet_result { - Ok(packet) => match packet { - LoginPacket::ClientboundHelloPacket(p) => { - println!("Got encryption request"); - let e = azalea_crypto::encrypt(&p.public_key, &p.nonce).unwrap(); - - // TODO: authenticate with the server here (authenticateServer) - - conn.write( - ServerboundKeyPacket { - nonce_or_salt_signature: NonceOrSaltSignature::Nonce( - e.encrypted_nonce, - ), - key_bytes: e.encrypted_public_key, - } - .get(), - ) - .await; - conn.set_encryption_key(e.secret_key); - } - LoginPacket::ClientboundLoginCompressionPacket(p) => { - println!("Got compression request {:?}", p.compression_threshold); - conn.set_compression_threshold(p.compression_threshold); - } - LoginPacket::ClientboundGameProfilePacket(p) => { - println!("Got profile {:?}", p.game_profile); - break conn.game(); - } - LoginPacket::ClientboundLoginDisconnectPacket(p) => { - println!("Got disconnect {:?}", p); - } - LoginPacket::ClientboundCustomQueryPacket(p) => { - println!("Got custom query {:?}", p); - } - _ => panic!("Unexpected packet {:?}", packet), - }, - Err(e) => { - panic!("Error: {:?}", e); - } - } - }; - - let conn = Arc::new(Mutex::new(conn)); - - let (tx, rx) = mpsc::unbounded_channel(); - - // we got the GameConnection, so the server is now connected :) - let client = Client { - event_receiver: rx, - conn: conn.clone(), - state: Arc::new(Mutex::new(ClientState::default())), - }; - // let client = Arc::new(Mutex::new(client)); - // let weak_client = Arc::<_>::downgrade(&client); - - // just start up the game loop and we're ready! - // tokio::spawn(Self::game_loop(conn, tx, handler, state)) - - let game_loop_state = client.state.clone(); - - tokio::spawn(Self::game_loop(conn, tx, game_loop_state)); - - Ok(client) - } - - async fn game_loop( - conn: Arc>, - tx: UnboundedSender, - state: Arc>, - ) { - loop { - let r = conn.lock().await.read().await; - match r { - Ok(packet) => Self::handle(&packet, &tx, &state, &conn).await, - Err(e) => { - if IGNORE_ERRORS { - println!("Error: {:?}", e); - if e == "length wider than 21-bit" { - panic!(); - } - } else { - panic!("Error: {:?}", e); - } - } - }; - } - } - - async fn handle( - packet: &GamePacket, - tx: &UnboundedSender, - state: &Arc>, - conn: &Arc>, - ) { - match packet { - GamePacket::ClientboundLoginPacket(p) => { - println!("Got login packet {:?}", p); - - let mut state = state.lock().await; - - // // write p into login.txt - // std::io::Write::write_all( - // &mut std::fs::File::create("login.txt").unwrap(), - // format!("{:#?}", p).as_bytes(), - // ) - // .unwrap(); - - state.player.entity.id = p.player_id; - - // TODO: have registry_holder be a struct because this sucks rn - // best way would be to add serde support to azalea-nbt - - let registry_holder = p - .registry_holder - .as_compound() - .expect("Registry holder is not a compound") - .get("") - .expect("No \"\" tag") - .as_compound() - .expect("\"\" tag is not a compound"); - let dimension_types = registry_holder - .get("minecraft:dimension_type") - .expect("No dimension_type tag") - .as_compound() - .expect("dimension_type is not a compound") - .get("value") - .expect("No dimension_type value") - .as_list() - .expect("dimension_type value is not a list"); - let dimension_type = dimension_types - .iter() - .find(|t| { - t.as_compound() - .expect("dimension_type value is not a compound") - .get("name") - .expect("No name tag") - .as_string() - .expect("name is not a string") - == p.dimension_type.to_string() - }) - .expect(&format!("No dimension_type with name {}", p.dimension_type)) - .as_compound() - .unwrap() - .get("element") - .expect("No element tag") - .as_compound() - .expect("element is not a compound"); - let height = (*dimension_type - .get("height") - .expect("No height tag") - .as_int() - .expect("height tag is not an 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 an int")) - .try_into() - .expect("min_y is not an i32"); - - state.world = Some(World { - height, - min_y, - storage: ChunkStorage::new(16), - entities: EntityStorage::new(), - }); - - conn.lock() - .await - .write( - ServerboundCustomPayloadPacket { - identifier: ResourceLocation::new("brand").unwrap(), - // they don't have to know :) - data: "vanilla".into(), - } - .get(), - ) - .await; - - tx.send(Event::Login).unwrap(); - } - GamePacket::ClientboundUpdateViewDistancePacket(p) => { - println!("Got view distance packet {:?}", p); - } - GamePacket::ClientboundCustomPayloadPacket(p) => { - println!("Got custom payload packet {:?}", p); - } - GamePacket::ClientboundChangeDifficultyPacket(p) => { - println!("Got difficulty packet {:?}", p); - } - GamePacket::ClientboundDeclareCommandsPacket(_p) => { - println!("Got declare commands packet"); - } - GamePacket::ClientboundPlayerAbilitiesPacket(p) => { - println!("Got player abilities packet {:?}", p); - } - GamePacket::ClientboundSetCarriedItemPacket(p) => { - println!("Got set carried item packet {:?}", p); - } - GamePacket::ClientboundUpdateTagsPacket(_p) => { - println!("Got update tags packet"); - } - GamePacket::ClientboundDisconnectPacket(p) => { - println!("Got disconnect packet {:?}", p); - } - GamePacket::ClientboundUpdateRecipesPacket(_p) => { - println!("Got update recipes packet"); - } - GamePacket::ClientboundEntityEventPacket(p) => { - // println!("Got entity event packet {:?}", p); - } - GamePacket::ClientboundRecipePacket(_p) => { - println!("Got recipe packet"); - } - GamePacket::ClientboundPlayerPositionPacket(p) => { - // TODO: reply with teleport confirm - println!("Got player position packet {:?}", p); - } - GamePacket::ClientboundPlayerInfoPacket(p) => { - println!("Got player info packet {:?}", p); - } - GamePacket::ClientboundSetChunkCacheCenterPacket(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) => { - 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) => { - println!("Got light update packet {:?}", p); - } - GamePacket::ClientboundAddEntityPacket(p) => { - println!("Got add entity packet {:?}", p); - let entity = Entity::from(p); - state - .lock() - .await - .world - .as_mut() - .expect("World doesn't exist! We should've gotten a login packet by now.") - .entities - .insert(entity); - } - GamePacket::ClientboundSetEntityDataPacket(p) => { - // println!("Got set entity data packet {:?}", p); - } - GamePacket::ClientboundUpdateAttributesPacket(p) => { - // println!("Got update attributes packet {:?}", p); - } - GamePacket::ClientboundEntityVelocityPacket(p) => { - // println!("Got entity velocity packet {:?}", p); - } - GamePacket::ClientboundSetEntityLinkPacket(p) => { - println!("Got set entity link packet {:?}", p); - } - GamePacket::ClientboundAddPlayerPacket(p) => { - println!("Got add player packet {:?}", p); - } - GamePacket::ClientboundInitializeBorderPacket(p) => { - println!("Got initialize border packet {:?}", p); - } - GamePacket::ClientboundSetTimePacket(p) => { - println!("Got set time packet {:?}", p); - } - GamePacket::ClientboundSetDefaultSpawnPositionPacket(p) => { - println!("Got set default spawn position packet {:?}", p); - } - GamePacket::ClientboundContainerSetContentPacket(p) => { - println!("Got container set content packet {:?}", p); - } - GamePacket::ClientboundSetHealthPacket(p) => { - println!("Got set health packet {:?}", p); - } - GamePacket::ClientboundSetExperiencePacket(p) => { - println!("Got set experience packet {:?}", p); - } - GamePacket::ClientboundTeleportEntityPacket(p) => { - // println!("Got teleport entity packet {:?}", p); - } - GamePacket::ClientboundUpdateAdvancementsPacket(p) => { - println!("Got update advancements packet {:?}", p); - } - GamePacket::ClientboundRotateHeadPacket(p) => { - // println!("Got rotate head packet {:?}", p); - } - GamePacket::ClientboundMoveEntityPosPacket(p) => { - // println!("Got move entity pos packet {:?}", p); - } - GamePacket::ClientboundMoveEntityPosRotPacket(p) => { - // println!("Got move entity pos rot packet {:?}", p); - } - GamePacket::ClientboundMoveEntityRotPacket(p) => { - println!("Got move entity rot packet {:?}", p); - } - GamePacket::ClientboundKeepAlivePacket(p) => { - println!("Got keep alive packet {:?}", p); - conn.lock() - .await - .write(ServerboundKeepAlivePacket { id: p.id }.get()) - .await; - } - GamePacket::ClientboundRemoveEntitiesPacket(p) => { - println!("Got remove entities packet {:?}", p); - } - GamePacket::ClientboundPlayerChatPacket(p) => { - println!("Got player chat packet {:?}", p); - tx.send(Event::Chat(ChatPacket::Player(p.clone()))).unwrap(); - } - GamePacket::ClientboundSystemChatPacket(p) => { - println!("Got system chat packet {:?}", p); - tx.send(Event::Chat(ChatPacket::System(p.clone()))).unwrap(); - } - GamePacket::ClientboundSoundPacket(p) => { - println!("Got sound packet {:?}", p); - } - GamePacket::ClientboundLevelEventPacket(p) => { - println!("Got level event packet {:?}", p); - } - GamePacket::ClientboundBlockUpdatePacket(p) => { - println!("Got block update packet {:?}", p); - // TODO: update world - } - GamePacket::ClientboundAnimatePacket(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); - } - GamePacket::ClientboundServerDataPacket(p) => { - println!("Got server data packet {:?}", p); - } - GamePacket::ClientboundSetEquipmentPacket(p) => { - println!("Got set equipment packet {:?}", p); - } - _ => panic!("Unexpected packet {:?}", packet), - } - } - - pub async fn next(&mut self) -> Option { - self.event_receiver.recv().await - } -} - -impl Account { - pub fn offline(username: &str) -> Self { - Self { - username: username.to_string(), - } - } - - pub async fn join(&self, address: &ServerAddress) -> Result { - Client::join(self, address).await - } -} diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs index db935897..867f05a1 100755 --- a/azalea-client/src/lib.rs +++ b/azalea-client/src/lib.rs @@ -1,10 +1,12 @@ //! Significantly abstract azalea-protocol so it's actually useable for bots. -mod connect; +mod account; +mod client; pub mod ping; mod player; -pub use connect::{Account, Client, Event}; +pub use account::Account; +pub use client::{Client, Event}; pub use player::Player; #[cfg(test)] diff --git a/azalea-world/src/entity.rs b/azalea-world/src/entity.rs index a25b1f40..49e1ae73 100644 --- a/azalea-world/src/entity.rs +++ b/azalea-world/src/entity.rs @@ -4,6 +4,7 @@ use azalea_core::ChunkPos; use azalea_entity::Entity; use nohash_hasher::IntMap; +#[derive(Debug)] pub struct EntityStorage { by_id: IntMap, // TODO: this doesn't work yet (should be updated in the set_pos method in azalea-entity) diff --git a/bot/src/main.rs b/bot/src/main.rs index 0b9da787..bfcba7f5 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -1,5 +1,4 @@ use azalea_client::{Account, Event}; -use azalea_core::BlockPos; #[tokio::main] async fn main() { @@ -20,10 +19,10 @@ async fn main() { match e { // TODO: have a "loaded" or "ready" event that fires when all chunks are loaded Event::Login => {} - Event::Chat(p) => { + Event::Chat(_p) => { let state = client.state.lock().await; let world = state.world.as_ref().unwrap(); - println!("{:?}", state.world.entities.get_player(player)); + println!("{:?}", world.entities); // world.get_block_state(state.player.entity.pos); // println!("{}", p.message.to_ansi(None)); // if p.message.to_ansi(None) == " ok" { -- cgit v1.2.3 From bb6b116cb81a64deeb5ee8c1d021f27dba1cbc58 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 19 Jun 2022 00:30:24 -0500 Subject: Improvements to azalea-world for entities --- Cargo.lock | 1 + azalea-client/src/client.rs | 197 +++++++++++++++++++++++++------------------- azalea-core/src/position.rs | 19 ++++- azalea-entity/src/lib.rs | 17 ++-- azalea-world/Cargo.toml | 1 + azalea-world/src/chunk.rs | 33 +++++++- azalea-world/src/entity.rs | 47 +++++++++-- azalea-world/src/lib.rs | 80 +++++++++++------- bot/src/main.rs | 12 +-- examples/pvp.rs | 2 +- 10 files changed, 267 insertions(+), 142 deletions(-) (limited to 'azalea-client/src') diff --git a/Cargo.lock b/Cargo.lock index 74375949..a3ceba57 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -198,6 +198,7 @@ dependencies = [ "azalea-entity", "azalea-nbt", "azalea-protocol", + "log", "nohash-hasher", ] diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index ff8729cb..dc2fe70f 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -21,9 +21,11 @@ use azalea_protocol::{ resolver, ServerAddress, }; use azalea_world::{ChunkStorage, EntityStorage, World}; -use std::{fmt::Debug, sync::Arc}; +use std::{ + fmt::Debug, + sync::{Arc, Mutex}, +}; use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; -use tokio::sync::Mutex; #[derive(Default)] pub struct ClientState { @@ -55,7 +57,7 @@ pub enum ChatPacket { /// A player that you can control that is currently in a Minecraft server. pub struct Client { event_receiver: UnboundedReceiver, - pub conn: Arc>, + pub conn: Arc>, pub state: Arc>, // game_loop } @@ -63,6 +65,8 @@ pub struct Client { /// Whether we should ignore errors when decoding packets. const IGNORE_ERRORS: bool = !cfg!(debug_assertions); +struct HandleError(String); + impl Client { /// Connect to a Minecraft server with an account. pub async fn join(account: &Account, address: &ServerAddress) -> Result { @@ -137,7 +141,7 @@ impl Client { } }; - let conn = Arc::new(Mutex::new(conn)); + let conn = Arc::new(tokio::sync::Mutex::new(conn)); let (tx, rx) = mpsc::unbounded_channel(); @@ -161,14 +165,16 @@ impl Client { } async fn game_loop( - conn: Arc>, + conn: Arc>, tx: UnboundedSender, state: Arc>, ) { loop { let r = conn.lock().await.read().await; match r { - Ok(packet) => Self::handle(&packet, &tx, &state, &conn).await, + Ok(packet) => { + Self::handle(&packet, &tx, &state, &conn).await; + } Err(e) => { if IGNORE_ERRORS { println!("Error: {:?}", e); @@ -187,82 +193,79 @@ impl Client { packet: &GamePacket, tx: &UnboundedSender, state: &Arc>, - conn: &Arc>, - ) { + conn: &Arc>, + ) -> Result<(), HandleError> { match packet { GamePacket::ClientboundLoginPacket(p) => { println!("Got login packet {:?}", p); - let mut state = state.lock().await; - - // // write p into login.txt - // std::io::Write::write_all( - // &mut std::fs::File::create("login.txt").unwrap(), - // format!("{:#?}", p).as_bytes(), - // ) - // .unwrap(); - - state.player.entity.id = p.player_id; - - // TODO: have registry_holder be a struct because this sucks rn - // best way would be to add serde support to azalea-nbt - - let registry_holder = p - .registry_holder - .as_compound() - .expect("Registry holder is not a compound") - .get("") - .expect("No \"\" tag") - .as_compound() - .expect("\"\" tag is not a compound"); - let dimension_types = registry_holder - .get("minecraft:dimension_type") - .expect("No dimension_type tag") - .as_compound() - .expect("dimension_type is not a compound") - .get("value") - .expect("No dimension_type value") - .as_list() - .expect("dimension_type value is not a list"); - let dimension_type = dimension_types - .iter() - .find(|t| { - t.as_compound() - .expect("dimension_type value is not a compound") - .get("name") - .expect("No name tag") - .as_string() - .expect("name is not a string") - == p.dimension_type.to_string() - }) - .expect(&format!("No dimension_type with name {}", p.dimension_type)) - .as_compound() - .unwrap() - .get("element") - .expect("No element tag") - .as_compound() - .expect("element is not a compound"); - let height = (*dimension_type - .get("height") - .expect("No height tag") - .as_int() - .expect("height tag is not an 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 an int")) - .try_into() - .expect("min_y is not an i32"); - - state.world = Some(World { - height, - min_y, - storage: ChunkStorage::new(16), - entities: EntityStorage::new(), - }); + { + let mut state = state.lock()?; + + // // write p into login.txt + // std::io::Write::write_all( + // &mut std::fs::File::create("login.txt").unwrap(), + // format!("{:#?}", p).as_bytes(), + // ) + // .unwrap(); + + state.player.entity.id = p.player_id; + + // TODO: have registry_holder be a struct because this sucks rn + // best way would be to add serde support to azalea-nbt + + let registry_holder = p + .registry_holder + .as_compound() + .expect("Registry holder is not a compound") + .get("") + .expect("No \"\" tag") + .as_compound() + .expect("\"\" tag is not a compound"); + let dimension_types = registry_holder + .get("minecraft:dimension_type") + .expect("No dimension_type tag") + .as_compound() + .expect("dimension_type is not a compound") + .get("value") + .expect("No dimension_type value") + .as_list() + .expect("dimension_type value is not a list"); + let dimension_type = dimension_types + .iter() + .find(|t| { + t.as_compound() + .expect("dimension_type value is not a compound") + .get("name") + .expect("No name tag") + .as_string() + .expect("name is not a string") + == p.dimension_type.to_string() + }) + .expect(&format!("No dimension_type with name {}", p.dimension_type)) + .as_compound() + .unwrap() + .get("element") + .expect("No element tag") + .as_compound() + .expect("element is not a compound"); + let height = (*dimension_type + .get("height") + .expect("No height tag") + .as_int() + .expect("height tag is not an 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 an int")) + .try_into() + .expect("min_y is not an i32"); + + state.world = Some(World::new(16, height, min_y)); + } conn.lock() .await @@ -321,8 +324,7 @@ impl Client { GamePacket::ClientboundSetChunkCacheCenterPacket(p) => { println!("Got chunk cache center packet {:?}", p); state - .lock() - .await + .lock()? .world .as_mut() .unwrap() @@ -334,8 +336,7 @@ impl Client { // let chunk = Chunk::read_with_world_height(&mut p.chunk_data); // println("chunk {:?}") state - .lock() - .await + .lock()? .world .as_mut() .expect("World doesn't exist! We should've gotten a login packet by now.") @@ -349,13 +350,11 @@ impl Client { println!("Got add entity packet {:?}", p); let entity = Entity::from(p); state - .lock() - .await + .lock()? .world .as_mut() .expect("World doesn't exist! We should've gotten a login packet by now.") - .entities - .insert(entity); + .add_entity(entity); } GamePacket::ClientboundSetEntityDataPacket(p) => { // println!("Got set entity data packet {:?}", p); @@ -392,6 +391,18 @@ impl Client { } GamePacket::ClientboundTeleportEntityPacket(p) => { // println!("Got teleport entity packet {:?}", p); + let state_lock = state.lock()?; + + // let entity = state_lock + // .world + // .unwrap() + // .entity_by_id(p.id) + // .ok_or("Teleporting entity that doesn't exist.".to_string())?; + // state_lock + // .world + // .as_mut() + // .expect("World doesn't exist! We should've gotten a login packet by now.") + // .move_entity(&mut entity, new_pos) } GamePacket::ClientboundUpdateAdvancementsPacket(p) => { println!("Got update advancements packet {:?}", p); @@ -457,9 +468,23 @@ impl Client { } _ => panic!("Unexpected packet {:?}", packet), } + + Ok(()) } pub async fn next(&mut self) -> Option { self.event_receiver.recv().await } } + +impl From> for HandleError { + fn from(e: std::sync::PoisonError) -> Self { + HandleError(e.to_string()) + } +} + +impl From for HandleError { + fn from(e: String) -> Self { + HandleError(e) + } +} diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index 24be5f6a..43881f1c 100644 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -27,7 +27,7 @@ impl Rem for BlockPos { } } -#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] pub struct ChunkPos { pub x: i32, pub z: i32, @@ -164,6 +164,12 @@ impl From<&EntityPos> for BlockPos { } } +impl From<&EntityPos> for ChunkPos { + fn from(pos: &EntityPos) -> Self { + ChunkPos::from(&BlockPos::from(pos)) + } +} + #[cfg(test)] mod tests { use super::*; @@ -181,4 +187,15 @@ mod tests { let chunk_block_pos = ChunkBlockPos::from(&block_pos); assert_eq!(chunk_block_pos, ChunkBlockPos::new(5, 78, 14)); } + + #[test] + fn test_from_entity_pos_to_chunk_pos() { + let entity_pos = EntityPos { + x: 33.5, + y: 77.0, + z: -19.6, + }; + let chunk_pos = ChunkPos::from(&entity_pos); + assert_eq!(chunk_pos, ChunkPos::new(2, -2)); + } } diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index f9d808c2..f776f16f 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -1,4 +1,4 @@ -use azalea_core::EntityPos; +use azalea_core::{ChunkPos, EntityPos}; #[cfg(feature = "protocol")] use azalea_protocol::packets::game::clientbound_add_entity_packet::ClientboundAddEntityPacket; use uuid::Uuid; @@ -16,19 +16,16 @@ impl Entity { &self.pos } - pub fn set_pos(&mut self, pos: EntityPos) { - // TODO: check if it moved to another chunk - self.pos = pos; + /// Sets the position of the entity. This doesn't update the cache in + /// azalea-world, and should only be used within azalea-world! + pub fn unsafe_move(&mut self, new_pos: EntityPos) { + self.pos = new_pos; } } #[cfg(feature = "protocol")] -impl From<&azalea_protocol::packets::game::clientbound_add_entity_packet::ClientboundAddEntityPacket> - for Entity -{ - fn from( - p: &azalea_protocol::packets::game::clientbound_add_entity_packet::ClientboundAddEntityPacket, - ) -> Self { +impl From<&ClientboundAddEntityPacket> for Entity { + fn from(p: &ClientboundAddEntityPacket) -> Self { Self { id: p.id, uuid: p.uuid, diff --git a/azalea-world/Cargo.toml b/azalea-world/Cargo.toml index e5e9da1d..96942138 100644 --- a/azalea-world/Cargo.toml +++ b/azalea-world/Cargo.toml @@ -11,6 +11,7 @@ azalea-core = {path = "../azalea-core"} azalea-entity = {path = "../azalea-entity"} azalea-nbt = {path = "../azalea-nbt"} azalea-protocol = {path = "../azalea-protocol"} +log = "0.4.17" nohash-hasher = "0.2.0" [profile.release] diff --git a/azalea-world/src/chunk.rs b/azalea-world/src/chunk.rs index 96bbd922..cbf77b20 100644 --- a/azalea-world/src/chunk.rs +++ b/azalea-world/src/chunk.rs @@ -1,4 +1,3 @@ -use crate::bit_storage::BitStorage; use crate::palette::PalettedContainer; use crate::palette::PalettedContainerType; use crate::World; @@ -13,10 +12,13 @@ use std::{ const SECTION_HEIGHT: u32 = 16; +#[derive(Debug)] pub struct ChunkStorage { pub view_center: ChunkPos, chunk_radius: u32, view_range: u32, + pub height: u32, + pub min_y: i32, // chunks is a list of size chunk_radius * chunk_radius chunks: Vec>>>, } @@ -32,12 +34,14 @@ fn floor_mod(x: i32, y: u32) -> u32 { } impl ChunkStorage { - pub fn new(chunk_radius: u32) -> Self { + pub fn new(chunk_radius: u32, height: u32, min_y: i32) -> Self { let view_range = chunk_radius * 2 + 1; ChunkStorage { view_center: ChunkPos::new(0, 0), chunk_radius, view_range, + height, + min_y, chunks: vec![None; (view_range * view_range) as usize], } } @@ -61,6 +65,29 @@ impl ChunkStorage { None => None, } } + + pub fn replace_with_packet_data( + &mut self, + pos: &ChunkPos, + data: &mut impl Read, + ) -> Result<(), String> { + if !self.in_range(pos) { + println!( + "Ignoring chunk since it's not in the view range: {}, {}", + pos.x, pos.z + ); + return Ok(()); + } + + let chunk = Arc::new(Mutex::new(Chunk::read_with_world_height( + data, + self.height, + )?)); + println!("Loaded chunk {:?}", pos); + self[pos] = Some(chunk); + + Ok(()) + } } impl Index<&ChunkPos> for ChunkStorage { @@ -84,7 +111,7 @@ pub struct Chunk { impl Chunk { pub fn read_with_world(buf: &mut impl Read, data: &World) -> Result { - Self::read_with_world_height(buf, data.height) + Self::read_with_world_height(buf, data.height()) } pub fn read_with_world_height(buf: &mut impl Read, world_height: u32) -> Result { diff --git a/azalea-world/src/entity.rs b/azalea-world/src/entity.rs index 49e1ae73..7077d0c4 100644 --- a/azalea-world/src/entity.rs +++ b/azalea-world/src/entity.rs @@ -1,14 +1,14 @@ -use std::collections::HashMap; - use azalea_core::ChunkPos; use azalea_entity::Entity; -use nohash_hasher::IntMap; +use log::warn; +use nohash_hasher::{IntMap, IntSet}; +use std::collections::HashMap; #[derive(Debug)] pub struct EntityStorage { by_id: IntMap, // TODO: this doesn't work yet (should be updated in the set_pos method in azalea-entity) - by_chunk: HashMap, + by_chunk: HashMap>, } impl EntityStorage { @@ -22,13 +22,24 @@ impl EntityStorage { /// Add an entity to the storage. #[inline] pub fn insert(&mut self, entity: Entity) { + self.by_chunk + .entry(ChunkPos::from(entity.pos())) + .or_default() + .insert(entity.id); self.by_id.insert(entity.id, entity); } /// Remove an entity from the storage by its id. #[inline] pub fn remove_by_id(&mut self, id: u32) { - self.by_id.remove(&id); + if let Some(entity) = self.by_id.remove(&id) { + let entity_chunk = ChunkPos::from(entity.pos()); + if let None = self.by_chunk.remove(&entity_chunk) { + warn!("Tried to remove entity with id {id} from chunk {entity_chunk:?} but it was not found."); + } + } else { + warn!("Tried to remove entity with id {id} but it was not found.") + } } /// Get a reference to an entity by its id. @@ -42,4 +53,30 @@ impl EntityStorage { pub fn get_mut_by_id(&mut self, id: u32) -> Option<&mut Entity> { self.by_id.get_mut(&id) } + + /// Clear all entities in a chunk. + pub fn clear_chunk(&mut self, chunk: &ChunkPos) { + if let Some(entities) = self.by_chunk.remove(chunk) { + for entity_id in entities { + self.by_id.remove(&entity_id); + } + } + } + + /// Updates an entity from its old chunk. + #[inline] + pub fn update_entity_chunk( + &mut self, + entity_id: u32, + old_chunk: &ChunkPos, + new_chunk: &ChunkPos, + ) { + if let Some(entities) = self.by_chunk.get_mut(old_chunk) { + entities.remove(&entity_id); + } + self.by_chunk + .entry(*new_chunk) + .or_default() + .insert(entity_id); + } } diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index b47126d4..746143c7 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -6,7 +6,8 @@ mod entity; mod palette; use azalea_block::BlockState; -use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos}; +use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos, EntityPos}; +use azalea_entity::Entity; use azalea_protocol::mc_buf::{McBufReadable, McBufWritable}; pub use bit_storage::BitStorage; pub use chunk::{Chunk, ChunkStorage}; @@ -26,61 +27,78 @@ mod tests { } } +#[derive(Debug)] pub struct World { - pub storage: ChunkStorage, - pub entities: EntityStorage, - pub height: u32, - pub min_y: i32, + chunk_storage: ChunkStorage, + entity_storage: EntityStorage, } impl World { + pub fn new(chunk_radius: u32, height: u32, min_y: i32) -> Self { + World { + chunk_storage: ChunkStorage::new(chunk_radius, height, min_y), + entity_storage: EntityStorage::new(), + } + } + 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]; + self.chunk_storage.replace_with_packet_data(pos, data) + } - let chunk = Arc::new(Mutex::new(Chunk::read_with_world(data, self)?)); - println!("Loaded chunk {:?}", pos); - self.storage[pos] = Some(chunk); + pub fn update_view_center(&mut self, pos: &ChunkPos) { + self.chunk_storage.view_center = *pos; + } + + pub fn get_block_state(&self, pos: &BlockPos) -> Option { + self.chunk_storage.get_block_state(pos, self.min_y()) + } + pub fn move_entity(&mut self, entity_id: u32, new_pos: EntityPos) -> Result<(), String> { + let entity = self + .entity_storage + .get_mut_by_id(entity_id) + .ok_or("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 update_view_center(&mut self, pos: &ChunkPos) { - self.storage.view_center = *pos; + pub fn add_entity(&mut self, entity: Entity) { + self.entity_storage.insert(entity); } - pub fn get_block_state(&self, pos: &BlockPos) -> Option { - self.storage.get_block_state(pos, self.min_y) + pub fn height(&self) -> u32 { + self.chunk_storage.height + } + + pub fn min_y(&self) -> i32 { + self.chunk_storage.min_y + } + + pub fn entity_by_id(&self, id: u32) -> Option<&Entity> { + self.entity_storage.get_by_id(id) } } + impl Index<&ChunkPos> for World { type Output = Option>>; fn index(&self, pos: &ChunkPos) -> &Self::Output { - &self.storage[pos] + &self.chunk_storage[pos] } } impl IndexMut<&ChunkPos> for World { fn index_mut<'a>(&'a mut self, pos: &ChunkPos) -> &'a mut Self::Output { - &mut self.storage[pos] + &mut self.chunk_storage[pos] } } -// impl Index<&BlockPos> for World { -// type Output = Option>>; - -// fn index(&self, pos: &BlockPos) -> &Self::Output { -// let chunk = &self[ChunkPos::from(pos)]; -// // chunk. - -// } -// } diff --git a/bot/src/main.rs b/bot/src/main.rs index bfcba7f5..6b318157 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -1,7 +1,7 @@ use azalea_client::{Account, Event}; #[tokio::main] -async fn main() { +async fn main() -> Result<(), Box> { println!("Hello, world!"); // let address = "95.111.249.143:10000"; @@ -15,18 +15,18 @@ async fn main() { let mut client = account.join(&address.try_into().unwrap()).await.unwrap(); println!("connected"); - while let Some(e) = client.next().await { + while let Some(e) = &client.next().await { match e { // TODO: have a "loaded" or "ready" event that fires when all chunks are loaded Event::Login => {} Event::Chat(_p) => { - let state = client.state.lock().await; + let state = &client.state.lock()?; let world = state.world.as_ref().unwrap(); - println!("{:?}", world.entities); + println!("{:?}", world); // world.get_block_state(state.player.entity.pos); // println!("{}", p.message.to_ansi(None)); // if p.message.to_ansi(None) == " ok" { - // let state = client.state.lock().await; + // let state = client.state.lock(); // let world = state.world.as_ref().unwrap(); // let c = world.get_block_state(&BlockPos::new(5, 78, -2)).unwrap(); // println!("block state: {:?}", c); @@ -36,4 +36,6 @@ async fn main() { } println!("done"); + + Ok(()) } diff --git a/examples/pvp.rs b/examples/pvp.rs index 61382ecd..5febdd45 100644 --- a/examples/pvp.rs +++ b/examples/pvp.rs @@ -21,7 +21,7 @@ async fn main() { if bot.entity.can_reach(target.bounding_box) { bot.swing(); } - if !h.using_held_item() && bot.state.lock().await.hunger <= 17 { + if !h.using_held_item() && bot.state.lock().hunger <= 17 { bot.hold(azalea::ItemGroup::Food); tokio::task::spawn(bot.use_held_item()); } -- cgit v1.2.3 From c9a070f711a6fdaf505f522cb8809749c9190e38 Mon Sep 17 00:00:00 2001 From: mat Date: Sun, 19 Jun 2022 22:01:54 -0500 Subject: Fix some clippy warnings --- Cargo.lock | 16 +++++++++++ azalea-client/Cargo.toml | 1 + azalea-client/src/account.rs | 9 +----- azalea-client/src/client.rs | 61 +++++++++++++++++++++++++++-------------- azalea-entity/src/lib.rs | 2 +- azalea-world/src/bit_storage.rs | 6 ++-- azalea-world/src/chunk.rs | 10 +++---- azalea-world/src/entity.rs | 8 +++++- azalea-world/src/lib.rs | 7 ++--- bot/src/main.rs | 9 ++++-- 10 files changed, 83 insertions(+), 46 deletions(-) (limited to 'azalea-client/src') diff --git a/Cargo.lock b/Cargo.lock index a3ceba57..59edd75e 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,6 +109,7 @@ dependencies = [ "azalea-entity", "azalea-protocol", "azalea-world", + "owning_ref", "tokio", ] @@ -891,6 +892,15 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "owning_ref" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "packet-macros" version = "0.1.0" @@ -1239,6 +1249,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "syn" version = "1.0.96" diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml index cc34f03a..9c9ae746 100755 --- a/azalea-client/Cargo.toml +++ b/azalea-client/Cargo.toml @@ -12,4 +12,5 @@ azalea-crypto = {path = "../azalea-crypto"} azalea-entity = {path = "../azalea-entity"} azalea-protocol = {path = "../azalea-protocol"} azalea-world = {path = "../azalea-world"} +owning_ref = "0.4.1" tokio = {version = "1.18.0", features = ["sync"]} diff --git a/azalea-client/src/account.rs b/azalea-client/src/account.rs index 00a5d0f6..0bcad630 100644 --- a/azalea-client/src/account.rs +++ b/azalea-client/src/account.rs @@ -1,12 +1,5 @@ use crate::Client; -use azalea_protocol::{ - packets::game::{ - clientbound_player_chat_packet::ClientboundPlayerChatPacket, - clientbound_system_chat_packet::ClientboundSystemChatPacket, - }, - ServerAddress, -}; -use std::fmt::Debug; +use azalea_protocol::ServerAddress; ///! Connect to Minecraft servers. diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index dc2fe70f..3dd206b5 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -20,7 +20,8 @@ use azalea_protocol::{ }, resolver, ServerAddress, }; -use azalea_world::{ChunkStorage, EntityStorage, World}; +use azalea_world::World; +use owning_ref::OwningRef; use std::{ fmt::Debug, sync::{Arc, Mutex}, @@ -42,7 +43,7 @@ pub enum Event { #[derive(Debug, Clone)] pub enum ChatPacket { System(ClientboundSystemChatPacket), - Player(ClientboundPlayerChatPacket), + Player(Box), } // impl ChatPacket { @@ -65,6 +66,7 @@ pub struct Client { /// Whether we should ignore errors when decoding packets. const IGNORE_ERRORS: bool = !cfg!(debug_assertions); +#[derive(Debug)] struct HandleError(String); impl Client { @@ -172,9 +174,17 @@ impl Client { loop { let r = conn.lock().await.read().await; match r { - Ok(packet) => { - Self::handle(&packet, &tx, &state, &conn).await; - } + Ok(packet) => match Self::handle(&packet, &tx, &state, &conn).await { + Ok(_) => {} + Err(e) => { + println!("Error handling packet: {:?}", e); + if IGNORE_ERRORS { + continue; + } else { + panic!("Error handling packet: {:?}", e); + } + } + }, Err(e) => { if IGNORE_ERRORS { println!("Error: {:?}", e); @@ -242,7 +252,9 @@ impl Client { .expect("name is not a string") == p.dimension_type.to_string() }) - .expect(&format!("No dimension_type with name {}", p.dimension_type)) + .unwrap_or_else(|| { + panic!("No dimension_type with name {}", p.dimension_type) + }) .as_compound() .unwrap() .get("element") @@ -256,13 +268,11 @@ impl Client { .expect("height tag is not an int")) .try_into() .expect("height is not a u32"); - let min_y = (*dimension_type + let min_y = *dimension_type .get("min_y") .expect("No min_y tag") .as_int() - .expect("min_y tag is not an int")) - .try_into() - .expect("min_y is not an i32"); + .expect("min_y tag is not an int"); state.world = Some(World::new(16, height, min_y)); } @@ -308,7 +318,7 @@ impl Client { GamePacket::ClientboundUpdateRecipesPacket(_p) => { println!("Got update recipes packet"); } - GamePacket::ClientboundEntityEventPacket(p) => { + GamePacket::ClientboundEntityEventPacket(_p) => { // println!("Got entity event packet {:?}", p); } GamePacket::ClientboundRecipePacket(_p) => { @@ -356,13 +366,13 @@ impl Client { .expect("World doesn't exist! We should've gotten a login packet by now.") .add_entity(entity); } - GamePacket::ClientboundSetEntityDataPacket(p) => { + GamePacket::ClientboundSetEntityDataPacket(_p) => { // println!("Got set entity data packet {:?}", p); } - GamePacket::ClientboundUpdateAttributesPacket(p) => { + GamePacket::ClientboundUpdateAttributesPacket(_p) => { // println!("Got update attributes packet {:?}", p); } - GamePacket::ClientboundEntityVelocityPacket(p) => { + GamePacket::ClientboundEntityVelocityPacket(_p) => { // println!("Got entity velocity packet {:?}", p); } GamePacket::ClientboundSetEntityLinkPacket(p) => { @@ -389,9 +399,9 @@ impl Client { GamePacket::ClientboundSetExperiencePacket(p) => { println!("Got set experience packet {:?}", p); } - GamePacket::ClientboundTeleportEntityPacket(p) => { + GamePacket::ClientboundTeleportEntityPacket(_p) => { // println!("Got teleport entity packet {:?}", p); - let state_lock = state.lock()?; + // let state_lock = state.lock()?; // let entity = state_lock // .world @@ -407,13 +417,13 @@ impl Client { GamePacket::ClientboundUpdateAdvancementsPacket(p) => { println!("Got update advancements packet {:?}", p); } - GamePacket::ClientboundRotateHeadPacket(p) => { + GamePacket::ClientboundRotateHeadPacket(_p) => { // println!("Got rotate head packet {:?}", p); } - GamePacket::ClientboundMoveEntityPosPacket(p) => { + GamePacket::ClientboundMoveEntityPosPacket(_p) => { // println!("Got move entity pos packet {:?}", p); } - GamePacket::ClientboundMoveEntityPosRotPacket(p) => { + GamePacket::ClientboundMoveEntityPosRotPacket(_p) => { // println!("Got move entity pos rot packet {:?}", p); } GamePacket::ClientboundMoveEntityRotPacket(p) => { @@ -431,7 +441,8 @@ impl Client { } GamePacket::ClientboundPlayerChatPacket(p) => { println!("Got player chat packet {:?}", p); - tx.send(Event::Chat(ChatPacket::Player(p.clone()))).unwrap(); + tx.send(Event::Chat(ChatPacket::Player(Box::new(p.clone())))) + .unwrap(); } GamePacket::ClientboundSystemChatPacket(p) => { println!("Got system chat packet {:?}", p); @@ -475,6 +486,16 @@ impl Client { pub async fn next(&mut self) -> Option { self.event_receiver.recv().await } + + /// Gets the `World` the client is in. + /// + /// This is basically a shortcut for `let world = client.state.lock().unwrap().world.as_ref().unwrap()`. + /// If the client hasn't received a login packet yet, this will panic. + pub fn world(&self) -> OwningRef, World> { + let state_lock: std::sync::MutexGuard = self.state.lock().unwrap(); + let state_lock_ref = OwningRef::new(state_lock); + state_lock_ref.map(|state| state.world.as_ref().expect("World doesn't exist!")) + } } impl From> for HandleError { diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index f776f16f..d32a1a96 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -1,4 +1,4 @@ -use azalea_core::{ChunkPos, EntityPos}; +use azalea_core::EntityPos; #[cfg(feature = "protocol")] use azalea_protocol::packets::game::clientbound_add_entity_packet::ClientboundAddEntityPacket; use uuid::Uuid; diff --git a/azalea-world/src/bit_storage.rs b/azalea-world/src/bit_storage.rs index 0dc81f9a..fcb3f8f9 100644 --- a/azalea-world/src/bit_storage.rs +++ b/azalea-world/src/bit_storage.rs @@ -107,7 +107,7 @@ impl BitStorage { // assert!(bits >= 1 && bits <= 32); if let Some(data) = &data { - if data.len() == 0 { + if data.is_empty() { // TODO: make 0 bit storage actually work return Ok(BitStorage::default()); } @@ -162,7 +162,7 @@ impl BitStorage { // return (int)(var3 >> var5 & this.mask); assert!( - index <= self.size - 1, + index < self.size, "Index {} out of bounds (max is {})", index, self.size - 1 @@ -181,7 +181,7 @@ impl BitStorage { // 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!(index < self.size); assert!(value <= self.mask); let cell_index = self.cell_index(index as u64); let cell = &mut self.data[cell_index as usize]; diff --git a/azalea-world/src/chunk.rs b/azalea-world/src/chunk.rs index 5de39e52..77fa8786 100644 --- a/azalea-world/src/chunk.rs +++ b/azalea-world/src/chunk.rs @@ -60,10 +60,9 @@ impl ChunkStorage { 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, - } + chunk + .as_ref() + .map(|chunk| chunk.lock().unwrap().get(&ChunkBlockPos::from(pos), min_y)) } pub fn replace_with_packet_data( @@ -137,8 +136,7 @@ impl Chunk { // 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 + section.get(chunk_section_pos) } } diff --git a/azalea-world/src/entity.rs b/azalea-world/src/entity.rs index 7077d0c4..2409995c 100644 --- a/azalea-world/src/entity.rs +++ b/azalea-world/src/entity.rs @@ -34,7 +34,7 @@ impl EntityStorage { pub fn remove_by_id(&mut self, id: u32) { if let Some(entity) = self.by_id.remove(&id) { let entity_chunk = ChunkPos::from(entity.pos()); - if let None = self.by_chunk.remove(&entity_chunk) { + if self.by_chunk.remove(&entity_chunk).is_none() { warn!("Tried to remove entity with id {id} from chunk {entity_chunk:?} but it was not found."); } } else { @@ -80,3 +80,9 @@ impl EntityStorage { .insert(entity_id); } } + +impl Default for EntityStorage { + fn default() -> Self { + Self::new() + } +} diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index 746143c7..dc538618 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -6,14 +6,13 @@ mod entity; mod palette; use azalea_block::BlockState; -use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos, EntityPos}; +use azalea_core::{BlockPos, ChunkPos, EntityPos}; use azalea_entity::Entity; -use azalea_protocol::mc_buf::{McBufReadable, McBufWritable}; pub use bit_storage::BitStorage; pub use chunk::{Chunk, ChunkStorage}; pub use entity::EntityStorage; use std::{ - io::{Read, Write}, + io::Read, ops::{Index, IndexMut}, sync::{Arc, Mutex}, }; @@ -61,7 +60,7 @@ impl World { let entity = self .entity_storage .get_mut_by_id(entity_id) - .ok_or("Moving entity that doesn't exist".to_string())?; + .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 diff --git a/bot/src/main.rs b/bot/src/main.rs index e8ff5f95..f0f915f9 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -1,4 +1,5 @@ use azalea_client::{Account, Event}; +use azalea_core::BlockPos; #[tokio::main] async fn main() -> Result<(), Box> { @@ -20,9 +21,11 @@ async fn main() -> Result<(), Box> { // TODO: have a "loaded" or "ready" event that fires when all chunks are loaded Event::Login => {} Event::Chat(_p) => { - let state = client.state.lock().unwrap(); - let world = state.world.as_ref().unwrap(); - println!("{:#?}", world); + let world = client.world(); + world.get_block_state(&BlockPos::new(0, 0, 0)).unwrap(); + // let world = state.world.as_ref().unwrap(); + // world. + // println!("{:#?}", world); // world.get_block_state(state.player.entity.pos); // println!("{}", p.message.to_ansi(None)); // if p.message.to_ansi(None) == " ok" { -- cgit v1.2.3 From efd874957331d8bff1cefa0f43d81685690391bf Mon Sep 17 00:00:00 2001 From: mat Date: Mon, 20 Jun 2022 01:19:59 -0500 Subject: add gametick event and find_one_entity --- Cargo.lock | 1 + azalea-client/src/client.rs | 46 +++++++++++++++++++++++++++++++++++++++------ azalea-world/src/entity.rs | 34 +++++++++++++++++++++++++++++++++ azalea-world/src/lib.rs | 13 +++++++++++++ bot/Cargo.toml | 1 + bot/src/main.rs | 14 +++++++++----- 6 files changed, 98 insertions(+), 11 deletions(-) (limited to 'azalea-client/src') diff --git a/Cargo.lock b/Cargo.lock index 59edd75e..c3e5a0ce 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -235,6 +235,7 @@ dependencies = [ "azalea-core", "azalea-protocol", "tokio", + "uuid", ] [[package]] diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 3dd206b5..828578de 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -26,7 +26,10 @@ use std::{ fmt::Debug, sync::{Arc, Mutex}, }; -use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender}; +use tokio::{ + sync::mpsc::{self, UnboundedReceiver, UnboundedSender}, + time::{self, MissedTickBehavior}, +}; #[derive(Default)] pub struct ClientState { @@ -38,6 +41,8 @@ pub struct ClientState { pub enum Event { Login, Chat(ChatPacket), + /// A game tick, happens 20 times per second. + GameTick, } #[derive(Debug, Clone)] @@ -153,20 +158,22 @@ impl Client { conn: conn.clone(), state: Arc::new(Mutex::new(ClientState::default())), }; - // let client = Arc::new(Mutex::new(client)); - // let weak_client = Arc::<_>::downgrade(&client); // just start up the game loop and we're ready! - // tokio::spawn(Self::game_loop(conn, tx, handler, state)) let game_loop_state = client.state.clone(); - tokio::spawn(Self::game_loop(conn, tx, game_loop_state)); + tokio::spawn(Self::protocol_loop( + conn.clone(), + tx.clone(), + game_loop_state.clone(), + )); + tokio::spawn(Self::game_tick_loop(conn, tx, game_loop_state)); Ok(client) } - async fn game_loop( + async fn protocol_loop( conn: Arc>, tx: UnboundedSender, state: Arc>, @@ -487,6 +494,33 @@ impl Client { self.event_receiver.recv().await } + /// Runs game_tick every 50 milliseconds. + async fn game_tick_loop( + conn: Arc>, + tx: UnboundedSender, + state: Arc>, + ) { + let mut game_tick_interval = time::interval(time::Duration::from_millis(50)); + // TODO: Minecraft bursts up to 10 ticks and then skips, we should too + game_tick_interval.set_missed_tick_behavior(time::MissedTickBehavior::Burst); + loop { + game_tick_interval.tick().await; + Self::game_tick(&conn, &tx, &state).await; + } + } + + /// Runs every 50 milliseconds. + async fn game_tick( + conn: &Arc>, + tx: &UnboundedSender, + state: &Arc>, + ) { + if state.lock().unwrap().world.is_none() { + return; + } + tx.send(Event::GameTick).unwrap(); + } + /// Gets the `World` the client is in. /// /// This is basically a shortcut for `let world = client.state.lock().unwrap().world.as_ref().unwrap()`. diff --git a/azalea-world/src/entity.rs b/azalea-world/src/entity.rs index 2409995c..e4e9864f 100644 --- a/azalea-world/src/entity.rs +++ b/azalea-world/src/entity.rs @@ -79,6 +79,40 @@ impl EntityStorage { .or_default() .insert(entity_id); } + + /// Get an iterator over all entities. + #[inline] + pub fn entities(&self) -> std::collections::hash_map::Values<'_, u32, Entity> { + self.by_id.values() + } + + pub fn find_one_entity(&self, mut f: F) -> Option<&Entity> + where + F: FnMut(&Entity) -> bool, + { + for entity in self.entities() { + if f(entity) { + return Some(entity); + } + } + None + } + + pub fn find_one_entity_in_chunk(&self, chunk: &ChunkPos, mut f: F) -> Option<&Entity> + where + F: FnMut(&Entity) -> bool, + { + if let Some(entities) = self.by_chunk.get(chunk) { + for entity_id in entities { + if let Some(entity) = self.by_id.get(entity_id) { + if f(entity) { + return Some(entity); + } + } + } + } + None + } } impl Default for EntityStorage { diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index dc538618..10beb309 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -87,6 +87,19 @@ impl World { pub fn entity_by_id(&self, id: u32) -> Option<&Entity> { self.entity_storage.get_by_id(id) } + + /// Get an iterator over all entities. + #[inline] + pub fn entities(&self) -> std::collections::hash_map::Values<'_, u32, Entity> { + self.entity_storage.entities() + } + + pub fn find_one_entity(&self, mut f: F) -> Option<&Entity> + where + F: FnMut(&Entity) -> bool, + { + self.entity_storage.find_one_entity(|entity| f(entity)) + } } impl Index<&ChunkPos> for World { diff --git a/bot/Cargo.toml b/bot/Cargo.toml index b66e3b75..f61bf2fa 100755 --- a/bot/Cargo.toml +++ b/bot/Cargo.toml @@ -10,3 +10,4 @@ azalea-client = {path = "../azalea-client"} azalea-core = {path = "../azalea-core"} azalea-protocol = {path = "../azalea-protocol"} tokio = "^1.19.2" +uuid = "^1.1.2" diff --git a/bot/src/main.rs b/bot/src/main.rs index 2c2bee32..6ff4cc0b 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -20,12 +20,15 @@ async fn main() -> Result<(), Box> { match e { // TODO: have a "loaded" or "ready" event that fires when all chunks are loaded Event::Login => {} - Event::Chat(_p) => { + Event::GameTick => { let world = client.world(); - let b = world.get_block_state(&BlockPos::new(0, 0, 0)).unwrap(); - // let world = state.world.as_ref().unwrap(); - // world. - println!("{:?}", b); + if let Some(b) = world.find_one_entity(|e| { + e.uuid == uuid::uuid!("6536bfed-8695-48fd-83a1-ecd24cf2a0fd") + }) { + // let world = state.world.as_ref().unwrap(); + // world. + println!("{:?}", b); + } // world.get_block_state(state.player.entity.pos); // println!("{}", p.message.to_ansi(None)); // if p.message.to_ansi(None) == " ok" { @@ -35,6 +38,7 @@ async fn main() -> Result<(), Box> { // println!("block state: {:?}", c); // } } + _ => {} } } -- cgit v1.2.3 From c7b0c51274b5d8548c8a2f829b75dfbec4038be2 Mon Sep 17 00:00:00 2001 From: mat Date: Tue, 21 Jun 2022 23:19:31 -0500 Subject: Add move_to --- azalea-client/src/lib.rs | 1 + azalea-client/src/movement.rs | 24 ++++++++++++++++++++++++ bot/src/main.rs | 2 +- 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 azalea-client/src/movement.rs (limited to 'azalea-client/src') diff --git a/azalea-client/src/lib.rs b/azalea-client/src/lib.rs index 867f05a1..c3c37460 100755 --- a/azalea-client/src/lib.rs +++ b/azalea-client/src/lib.rs @@ -2,6 +2,7 @@ mod account; mod client; +mod movement; pub mod ping; mod player; diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs new file mode 100644 index 00000000..9f5cd27c --- /dev/null +++ b/azalea-client/src/movement.rs @@ -0,0 +1,24 @@ +use azalea_core::EntityPos; +use crate::Client; +use azalea_protocol::packets::game::serverbound_move_player_packet_pos_rot::ServerboundMovePlayerPacketPosRot; + +impl Client { + /// Set the client's position to the given coordinates. + pub async fn move_to(&mut self, pos: &EntityPos) { + self.conn + .lock() + .await + .write( + ServerboundMovePlayerPacketPosRot { + x: pos.x, + y: pos.y, + z: pos.z, + x_rot: 0.0, + y_rot: 0.0, + on_ground: false, + } + .get(), + ) + .await; + } +} diff --git a/bot/src/main.rs b/bot/src/main.rs index 6ff4cc0b..546a9244 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -6,7 +6,7 @@ async fn main() -> Result<(), Box> { println!("Hello, world!"); // let address = "95.111.249.143:10000"; - let address = "localhost:59021"; + let address = "localhost:57172"; // let response = azalea_client::ping::ping_server(&address.try_into().unwrap()) // .await // .unwrap(); -- cgit v1.2.3 From 5ca49e680ed8519456dc9a9af84321d4b69dcbb3 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 23 Jun 2022 15:12:17 -0500 Subject: azalea-buf --- Cargo.lock | 23 + Cargo.toml | 1 + azalea-buf/Cargo.toml | 11 + azalea-buf/README.md | 3 + azalea-buf/buf-macros/Cargo.toml | 13 + azalea-buf/buf-macros/src/lib.rs | 177 +++++++ azalea-buf/src/definitions.rs | 39 ++ azalea-buf/src/lib.rs | 190 ++++++++ azalea-buf/src/read.rs | 380 +++++++++++++++ azalea-buf/src/write.rs | 284 +++++++++++ azalea-chat/Cargo.toml | 1 + azalea-chat/src/component.rs | 27 ++ azalea-client/src/client.rs | 51 +- azalea-core/Cargo.toml | 1 + azalea-core/src/delta.rs | 26 + azalea-core/src/difficulty.rs | 12 + azalea-core/src/direction.rs | 2 +- azalea-core/src/game_type.rs | 24 + azalea-core/src/lib.rs | 6 + azalea-core/src/particle/mod.rs | 259 ++++++++++ azalea-core/src/position.rs | 59 +++ azalea-core/src/resource_location.rs | 26 + azalea-core/src/serializable_uuid.rs | 22 + azalea-core/src/slot.rs | 28 ++ azalea-crypto/Cargo.toml | 1 + azalea-crypto/src/lib.rs | 2 + azalea-crypto/src/signing.rs | 16 + azalea-entity/src/data.rs | 136 ++++++ azalea-entity/src/lib.rs | 22 +- azalea-nbt/Cargo.toml | 1 + azalea-nbt/src/decode.rs | 10 + azalea-nbt/src/encode.rs | 7 + azalea-nbt/src/lib.rs | 33 ++ azalea-protocol/Cargo.toml | 10 +- azalea-protocol/packet-macros/src/lib.rs | 177 ------- azalea-protocol/src/lib.rs | 6 +- azalea-protocol/src/mc_buf/definitions.rs | 442 ----------------- azalea-protocol/src/mc_buf/mod.rs | 233 --------- azalea-protocol/src/mc_buf/read.rs | 525 --------------------- azalea-protocol/src/mc_buf/write.rs | 430 ----------------- .../packets/game/clientbound_add_player_packet.rs | 3 +- .../game/clientbound_move_entity_pos_packet.rs | 5 +- .../game/clientbound_move_entity_posrot_packet.rs | 8 +- azalea-world/src/lib.rs | 16 + bot/src/main.rs | 2 +- 45 files changed, 1909 insertions(+), 1841 deletions(-) create mode 100644 azalea-buf/Cargo.toml create mode 100644 azalea-buf/README.md create mode 100644 azalea-buf/buf-macros/Cargo.toml create mode 100644 azalea-buf/buf-macros/src/lib.rs create mode 100644 azalea-buf/src/definitions.rs create mode 100644 azalea-buf/src/lib.rs create mode 100644 azalea-buf/src/read.rs create mode 100644 azalea-buf/src/write.rs create mode 100644 azalea-core/src/delta.rs create mode 100644 azalea-core/src/particle/mod.rs create mode 100644 azalea-entity/src/data.rs delete mode 100644 azalea-protocol/src/mc_buf/definitions.rs delete mode 100644 azalea-protocol/src/mc_buf/mod.rs delete mode 100644 azalea-protocol/src/mc_buf/read.rs delete mode 100644 azalea-protocol/src/mc_buf/write.rs (limited to 'azalea-client/src') diff --git a/Cargo.lock b/Cargo.lock index c3e5a0ce..855b65de 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,10 +89,20 @@ dependencies = [ name = "azalea-brigadier" version = "0.1.0" +[[package]] +name = "azalea-buf" +version = "0.1.0" +dependencies = [ + "buf-macros", + "byteorder", + "tokio", +] + [[package]] name = "azalea-chat" version = "0.1.0" dependencies = [ + "azalea-buf", "azalea-language", "lazy_static", "serde", @@ -117,6 +127,7 @@ dependencies = [ name = "azalea-core" version = "0.1.0" dependencies = [ + "azalea-buf", "azalea-chat", "azalea-nbt", "uuid", @@ -127,6 +138,7 @@ name = "azalea-crypto" version = "0.1.0" dependencies = [ "aes", + "azalea-buf", "cfb8", "num-bigint", "rand", @@ -156,6 +168,7 @@ dependencies = [ name = "azalea-nbt" version = "0.1.0" dependencies = [ + "azalea-buf", "byteorder", "criterion", "flate2", @@ -171,6 +184,7 @@ dependencies = [ "async-recursion", "azalea-auth", "azalea-brigadier", + "azalea-buf", "azalea-chat", "azalea-core", "azalea-crypto", @@ -250,6 +264,15 @@ dependencies = [ "serde", ] +[[package]] +name = "buf-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "bumpalo" version = "3.10.0" diff --git a/Cargo.toml b/Cargo.toml index fc32b2d9..72019b5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "azalea-language", "azalea-block", "azalea-entity", + "azalea-buf", ] [profile.release] diff --git a/azalea-buf/Cargo.toml b/azalea-buf/Cargo.toml new file mode 100644 index 00000000..79f9d64d --- /dev/null +++ b/azalea-buf/Cargo.toml @@ -0,0 +1,11 @@ +[package] +edition = "2021" +name = "azalea-buf" +version = "0.1.0" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +buf-macros = {path = "./buf-macros"} +byteorder = "1.4.3" +tokio = {version = "^1.19.2", features = ["io-util", "net", "macros"]} diff --git a/azalea-buf/README.md b/azalea-buf/README.md new file mode 100644 index 00000000..c988bcdb --- /dev/null +++ b/azalea-buf/README.md @@ -0,0 +1,3 @@ +# Azalea Buf + +An implementation of Minecraft's FriendlyByteBuf. This is used frequently in the game for serialization and deserialization of data. diff --git a/azalea-buf/buf-macros/Cargo.toml b/azalea-buf/buf-macros/Cargo.toml new file mode 100644 index 00000000..fecf64ed --- /dev/null +++ b/azalea-buf/buf-macros/Cargo.toml @@ -0,0 +1,13 @@ +[package] +edition = "2021" +name = "buf-macros" +version = "0.1.0" + +[lib] +proc-macro = true +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +proc-macro2 = "^1.0.36" +quote = "^1.0.10" +syn = "^1.0.82" diff --git a/azalea-buf/buf-macros/src/lib.rs b/azalea-buf/buf-macros/src/lib.rs new file mode 100644 index 00000000..3afeaeed --- /dev/null +++ b/azalea-buf/buf-macros/src/lib.rs @@ -0,0 +1,177 @@ +use proc_macro::TokenStream; +use quote::{quote, ToTokens}; +use syn::{self, parse_macro_input, Data, DeriveInput, FieldsNamed, Ident}; + +fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream { + match data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => { + let FieldsNamed { named, .. } = match fields { + syn::Fields::Named(f) => f, + _ => panic!("#[derive(McBuf)] can only be used on structs with named fields"), + }; + + let read_fields = named + .iter() + .map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + // do a different buf.write_* for each field depending on the type + // if it's a string, use buf.write_string + match field_type { + syn::Type::Path(_) => { + if f.attrs.iter().any(|a| a.path.is_ident("var")) { + quote! { + let #field_name = crate::McBufVarReadable::var_read_into(buf)?; + } + } else { + quote! { + let #field_name = crate::McBufReadable::read_into(buf)?; + } + } + } + _ => panic!( + "Error reading field {}: {}", + field_name.clone().unwrap(), + field_type.to_token_stream() + ), + } + }) + .collect::>(); + let read_field_names = named.iter().map(|f| &f.ident).collect::>(); + + quote! { + impl crate::McBufReadable for #ident { + fn read_into(buf: &mut impl std::io::Read) -> Result { + #(#read_fields)* + Ok(#ident { + #(#read_field_names: #read_field_names),* + }) + } + } + } + } + syn::Data::Enum(syn::DataEnum { variants, .. }) => { + let mut match_contents = quote!(); + let mut variant_discrim: u32 = 0; + for variant in variants { + let variant_name = &variant.ident; + match &variant.discriminant.as_ref() { + Some(d) => { + variant_discrim = match &d.1 { + syn::Expr::Lit(e) => match &e.lit { + syn::Lit::Int(i) => i.base10_parse().unwrap(), + _ => panic!("Error parsing enum discriminant"), + }, + _ => panic!("Error parsing enum discriminant"), + } + } + None => { + variant_discrim += 1; + } + } + match_contents.extend(quote! { + #variant_discrim => Ok(Self::#variant_name), + }); + } + + quote! { + impl crate::McBufReadable for #ident { + fn read_into(buf: &mut impl std::io::Read) -> Result + { + let id = crate::McBufVarReadable::var_read_into(buf)?; + match id { + #match_contents + _ => Err(format!("Unknown enum variant {}", id)), + } + } + } + } + } + _ => panic!("#[derive(McBuf)] can only be used on structs"), + } +} + +fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream { + match data { + syn::Data::Struct(syn::DataStruct { fields, .. }) => { + let FieldsNamed { named, .. } = match fields { + syn::Fields::Named(f) => f, + _ => panic!("#[derive(McBuf)] can only be used on structs with named fields"), + }; + + let write_fields = named + .iter() + .map(|f| { + let field_name = &f.ident; + let field_type = &f.ty; + // do a different buf.write_* for each field depending on the type + // if it's a string, use buf.write_string + match field_type { + syn::Type::Path(_) => { + if f.attrs.iter().any(|attr| attr.path.is_ident("var")) { + quote! { + crate::McBufVarWritable::var_write_into(&self.#field_name, buf)?; + } + } else { + quote! { + crate::McBufWritable::write_into(&self.#field_name, buf)?; + } + } + } + _ => panic!( + "Error writing field {}: {}", + field_name.clone().unwrap(), + field_type.to_token_stream() + ), + } + }) + .collect::>(); + + quote! { + impl crate::McBufWritable for #ident { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + #(#write_fields)* + Ok(()) + } + } + } + } + syn::Data::Enum(syn::DataEnum { .. }) => { + quote! { + impl crate::McBufWritable for #ident { + fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { + crate::Writable::write_varint(buf, *self as i32) + } + } + } + } + _ => panic!("#[derive(McBuf)] can only be used on structs"), + } +} + +#[proc_macro_derive(McBufReadable, attributes(var))] +pub fn derive_mcbufreadable(input: TokenStream) -> TokenStream { + let DeriveInput { ident, data, .. } = parse_macro_input!(input); + + create_impl_mcbufreadable(&ident, &data).into() +} + +#[proc_macro_derive(McBufWritable, attributes(var))] +pub fn derive_mcbufwritable(input: TokenStream) -> TokenStream { + let DeriveInput { ident, data, .. } = parse_macro_input!(input); + + create_impl_mcbufwritable(&ident, &data).into() +} + +#[proc_macro_derive(McBuf, attributes(var))] +pub fn derive_mcbuf(input: TokenStream) -> TokenStream { + let DeriveInput { ident, data, .. } = parse_macro_input!(input); + + let writable = create_impl_mcbufwritable(&ident, &data); + let readable = create_impl_mcbufreadable(&ident, &data); + quote! { + #writable + #readable + } + .into() +} diff --git a/azalea-buf/src/definitions.rs b/azalea-buf/src/definitions.rs new file mode 100644 index 00000000..e5d8e0c0 --- /dev/null +++ b/azalea-buf/src/definitions.rs @@ -0,0 +1,39 @@ +use buf_macros::McBuf; +use std::ops::Deref; + +/// A Vec that isn't prefixed by a VarInt with the size. +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct UnsizedByteArray(Vec); + +impl Deref for UnsizedByteArray { + type Target = Vec; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From> for UnsizedByteArray { + fn from(vec: Vec) -> Self { + Self(vec) + } +} + +impl From<&str> for UnsizedByteArray { + fn from(s: &str) -> Self { + Self(s.as_bytes().to_vec()) + } +} + +/// Represents Java's BitSet, a list of bits. +#[derive(Debug, Clone, PartialEq, Eq, Hash, McBuf)] +pub struct BitSet { + data: Vec, +} + +// the Index trait requires us to return a reference, but we can't do that +impl BitSet { + pub fn index(&self, index: usize) -> bool { + (self.data[index / 64] & (1u64 << (index % 64))) != 0 + } +} diff --git a/azalea-buf/src/lib.rs b/azalea-buf/src/lib.rs new file mode 100644 index 00000000..2ba17ac2 --- /dev/null +++ b/azalea-buf/src/lib.rs @@ -0,0 +1,190 @@ +//! Utilities for reading and writing for the Minecraft protocol + +#![feature(min_specialization)] +#![feature(arbitrary_enum_discriminant)] + +mod definitions; +mod read; +mod write; + +pub use definitions::*; +pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable}; +pub use write::{McBufVarWritable, McBufWritable, Writable}; + +// const DEFAULT_NBT_QUOTA: u32 = 2097152; +const MAX_STRING_LENGTH: u16 = 32767; +// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144; + +#[cfg(test)] +mod tests { + use super::*; + use std::{collections::HashMap, io::Cursor}; + + #[test] + fn test_write_varint() { + let mut buf = Vec::new(); + buf.write_varint(0).unwrap(); + assert_eq!(buf, vec![0]); + + let mut buf = Vec::new(); + buf.write_varint(1).unwrap(); + assert_eq!(buf, vec![1]); + + let mut buf = Vec::new(); + buf.write_varint(2).unwrap(); + assert_eq!(buf, vec![2]); + + let mut buf = Vec::new(); + buf.write_varint(127).unwrap(); + assert_eq!(buf, vec![127]); + + let mut buf = Vec::new(); + buf.write_varint(128).unwrap(); + assert_eq!(buf, vec![128, 1]); + + let mut buf = Vec::new(); + buf.write_varint(255).unwrap(); + assert_eq!(buf, vec![255, 1]); + + let mut buf = Vec::new(); + buf.write_varint(25565).unwrap(); + assert_eq!(buf, vec![221, 199, 1]); + + let mut buf = Vec::new(); + buf.write_varint(2097151).unwrap(); + assert_eq!(buf, vec![255, 255, 127]); + + let mut buf = Vec::new(); + buf.write_varint(2147483647).unwrap(); + assert_eq!(buf, vec![255, 255, 255, 255, 7]); + + let mut buf = Vec::new(); + buf.write_varint(-1).unwrap(); + assert_eq!(buf, vec![255, 255, 255, 255, 15]); + + let mut buf = Vec::new(); + buf.write_varint(-2147483648).unwrap(); + assert_eq!(buf, vec![128, 128, 128, 128, 8]); + } + + #[test] + fn test_read_varint() { + let mut buf = Cursor::new(vec![0]); + assert_eq!(buf.read_varint().unwrap(), 0); + assert_eq!(buf.get_varint_size(0), 1); + + let mut buf = Cursor::new(vec![1]); + assert_eq!(buf.read_varint().unwrap(), 1); + assert_eq!(buf.get_varint_size(1), 1); + + let mut buf = Cursor::new(vec![2]); + assert_eq!(buf.read_varint().unwrap(), 2); + assert_eq!(buf.get_varint_size(2), 1); + + let mut buf = Cursor::new(vec![127]); + assert_eq!(buf.read_varint().unwrap(), 127); + assert_eq!(buf.get_varint_size(127), 1); + + let mut buf = Cursor::new(vec![128, 1]); + assert_eq!(buf.read_varint().unwrap(), 128); + assert_eq!(buf.get_varint_size(128), 2); + + let mut buf = Cursor::new(vec![255, 1]); + assert_eq!(buf.read_varint().unwrap(), 255); + assert_eq!(buf.get_varint_size(255), 2); + + let mut buf = Cursor::new(vec![221, 199, 1]); + assert_eq!(buf.read_varint().unwrap(), 25565); + assert_eq!(buf.get_varint_size(25565), 3); + + let mut buf = Cursor::new(vec![255, 255, 127]); + assert_eq!(buf.read_varint().unwrap(), 2097151); + assert_eq!(buf.get_varint_size(2097151), 3); + + let mut buf = Cursor::new(vec![255, 255, 255, 255, 7]); + assert_eq!(buf.read_varint().unwrap(), 2147483647); + assert_eq!(buf.get_varint_size(2147483647), 5); + + let mut buf = Cursor::new(vec![255, 255, 255, 255, 15]); + assert_eq!(buf.read_varint().unwrap(), -1); + assert_eq!(buf.get_varint_size(-1), 5); + + let mut buf = Cursor::new(vec![128, 128, 128, 128, 8]); + assert_eq!(buf.read_varint().unwrap(), -2147483648); + assert_eq!(buf.get_varint_size(-2147483648), 5); + } + + #[test] + fn test_read_varint_longer() { + let mut buf = Cursor::new(vec![138, 56, 0, 135, 56, 123]); + assert_eq!(buf.read_varint().unwrap(), 7178); + } + + #[test] + fn test_list() { + let mut buf = Vec::new(); + buf.write_list(&vec!["a", "bc", "def"], |buf, s| buf.write_utf(s)) + .unwrap(); + + // there's no read_list because idk how to do it in rust + let mut buf = Cursor::new(buf); + + let mut result = Vec::new(); + let length = buf.read_varint().unwrap(); + for _ in 0..length { + result.push(buf.read_utf().unwrap()); + } + + assert_eq!(result, vec!["a", "bc", "def"]); + } + + #[test] + fn test_int_id_list() { + let mut buf = Vec::new(); + buf.write_list(&vec![1, 2, 3], |buf, i| buf.write_varint(*i)) + .unwrap(); + + let mut buf = Cursor::new(buf); + + let result = buf.read_int_id_list().unwrap(); + assert_eq!(result, vec![1, 2, 3]); + } + + #[test] + fn test_map() { + let mut buf = Vec::new(); + buf.write_map( + vec![("a", 1), ("bc", 23), ("def", 456)], + Vec::write_utf, + Vec::write_varint, + ) + .unwrap(); + + let mut buf = Cursor::new(buf); + + let mut result = Vec::new(); + let length = buf.read_varint().unwrap(); + for _ in 0..length { + result.push((buf.read_utf().unwrap(), buf.read_varint().unwrap())); + } + + assert_eq!( + result, + vec![ + ("a".to_string(), 1), + ("bc".to_string(), 23), + ("def".to_string(), 456) + ] + ); + } + + #[test] + fn test_long() { + let mut buf = Vec::new(); + buf.write_long(123456).unwrap(); + + let mut buf = Cursor::new(buf); + + assert_eq!(buf.read_long().unwrap(), 123456); + } +} diff --git a/azalea-buf/src/read.rs b/azalea-buf/src/read.rs new file mode 100644 index 00000000..569a5b1d --- /dev/null +++ b/azalea-buf/src/read.rs @@ -0,0 +1,380 @@ +use super::{UnsizedByteArray, MAX_STRING_LENGTH}; +use byteorder::{ReadBytesExt, BE}; +use std::{collections::HashMap, hash::Hash, io::Read}; +use tokio::io::{AsyncRead, AsyncReadExt}; + +// TODO: get rid of Readable and use McBufReadable everywhere + +pub trait Readable { + fn read_int_id_list(&mut self) -> Result, String>; + fn read_varint(&mut self) -> Result; + fn get_varint_size(&mut self, value: i32) -> u8; + fn get_varlong_size(&mut self, value: i32) -> u8; + fn read_byte_array(&mut self) -> Result, String>; + fn read_bytes_with_len(&mut self, n: usize) -> Result, String>; + fn read_bytes(&mut self) -> Result, String>; + fn read_utf(&mut self) -> Result; + fn read_utf_with_len(&mut self, max_length: u32) -> Result; + fn read_byte(&mut self) -> Result; + fn read_int(&mut self) -> Result; + fn read_boolean(&mut self) -> Result; + fn read_long(&mut self) -> Result; + fn read_short(&mut self) -> Result; + fn read_float(&mut self) -> Result; + fn read_double(&mut self) -> Result; +} + +impl Readable for R +where + R: Read, +{ + fn read_int_id_list(&mut self) -> Result, String> { + let len = self.read_varint()?; + let mut list = Vec::with_capacity(len as usize); + for _ in 0..len { + list.push(self.read_varint()?); + } + Ok(list) + } + + // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 + /// Read a single varint from the reader and return the value, along with the number of bytes read + fn read_varint(&mut self) -> Result { + let mut buffer = [0]; + let mut ans = 0; + for i in 0..5 { + self.read_exact(&mut buffer) + .map_err(|_| "Invalid VarInt".to_string())?; + ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); + if buffer[0] & 0b1000_0000 == 0 { + return Ok(ans); + } + } + Ok(ans) + } + + fn get_varint_size(&mut self, value: i32) -> u8 { + for i in 1..5 { + if (value & -1 << (i * 7)) != 0 { + continue; + } + return i; + } + 5 + } + + fn get_varlong_size(&mut self, value: i32) -> u8 { + for i in 1..10 { + if (value & -1 << (i * 7)) != 0 { + continue; + } + return i; + } + 10 + } + + fn read_byte_array(&mut self) -> Result, String> { + let length = self.read_varint()? as usize; + self.read_bytes_with_len(length) + } + + fn read_bytes_with_len(&mut self, n: usize) -> Result, String> { + let mut buffer = vec![0; n]; + self.read_exact(&mut buffer) + .map_err(|_| "Error reading bytes".to_string())?; + Ok(buffer) + } + + fn read_bytes(&mut self) -> Result, String> { + // read to end of the buffer + let mut bytes = vec![]; + self.read_to_end(&mut bytes) + .map_err(|_| "Error reading bytes".to_string())?; + Ok(bytes) + } + + fn read_utf(&mut self) -> Result { + self.read_utf_with_len(MAX_STRING_LENGTH.into()) + } + + fn read_utf_with_len(&mut self, max_length: u32) -> Result { + let length = self.read_varint()?; + // i don't know why it's multiplied by 4 but it's like that in mojang's code so + if length < 0 { + return Err( + "The received encoded string buffer length is less than zero! Weird string!" + .to_string(), + ); + } + if length as u32 > max_length * 4 { + return Err(format!( + "The received encoded string buffer length is longer than maximum allowed ({} > {})", + length, + max_length * 4 + )); + } + + // this is probably quite inefficient, idk how to do it better + let mut string = String::new(); + let mut buffer = vec![0; length as usize]; + self.read_exact(&mut buffer) + .map_err(|_| "Invalid UTF-8".to_string())?; + string.push_str(std::str::from_utf8(&buffer).unwrap()); + if string.len() > length as usize { + return Err(format!( + "The received string length is longer than maximum allowed ({} > {})", + length, max_length + )); + } + + Ok(string) + } + + /// Read a single byte from the reader + fn read_byte(&mut self) -> Result { + self.read_u8().map_err(|_| "Error reading byte".to_string()) + } + + fn read_int(&mut self) -> Result { + match self.read_i32::() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading int".to_string()), + } + } + + fn read_boolean(&mut self) -> Result { + match self.read_byte()? { + 0 => Ok(false), + 1 => Ok(true), + _ => Err("Error reading boolean".to_string()), + } + } + + fn read_long(&mut self) -> Result { + match self.read_i64::() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading long".to_string()), + } + } + + fn read_short(&mut self) -> Result { + match self.read_i16::() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading short".to_string()), + } + } + + fn read_float(&mut self) -> Result { + match self.read_f32::() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading float".to_string()), + } + } + + fn read_double(&mut self) -> Result { + match self.read_f64::() { + Ok(r) => Ok(r), + Err(_) => Err("Error reading double".to_string()), + } + } +} + +// fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 +/// Read a single varint from the reader and return the value, along with the number of bytes read +pub async fn read_varint_async(reader: &mut (dyn AsyncRead + Unpin + Send)) -> Result { + let mut buffer = [0]; + let mut ans = 0; + for i in 0..5 { + reader + .read_exact(&mut buffer) + .await + .map_err(|_| "Invalid VarInt".to_string())?; + ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); + if buffer[0] & 0b1000_0000 == 0 { + return Ok(ans); + } + } + Ok(ans) +} + +pub trait McBufReadable +where + Self: Sized, +{ + fn read_into(buf: &mut impl Read) -> Result; +} + +pub trait McBufVarReadable +where + Self: Sized, +{ + fn var_read_into(buf: &mut impl Read) -> Result; +} + +impl McBufReadable for i32 { + fn read_into(buf: &mut impl Read) -> Result { + Readable::read_int(buf) + } +} + +impl McBufVarReadable for i32 { + fn var_read_into(buf: &mut impl Read) -> Result { + buf.read_varint() + } +} + +impl McBufVarReadable for i64 { + // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L54 + fn var_read_into(buf: &mut impl Read) -> Result { + let mut buffer = [0]; + let mut ans = 0; + for i in 0..8 { + buf.read_exact(&mut buffer) + .map_err(|_| "Invalid VarLong".to_string())?; + ans |= ((buffer[0] & 0b0111_1111) as i64) << 7 * i; + if buffer[0] & 0b1000_0000 == 0 { + break; + } + } + Ok(ans) + } +} +impl McBufVarReadable for u64 { + fn var_read_into(buf: &mut impl Read) -> Result { + i64::var_read_into(buf).map(|i| i as u64) + } +} + +impl McBufReadable for UnsizedByteArray { + fn read_into(buf: &mut impl Read) -> Result { + Ok(buf.read_bytes()?.into()) + } +} + +impl McBufReadable for Vec { + default fn read_into(buf: &mut impl Read) -> Result { + let length = buf.read_varint()? as usize; + let mut contents = Vec::with_capacity(length); + for _ in 0..length { + contents.push(T::read_into(buf)?); + } + Ok(contents) + } +} + +impl McBufReadable for HashMap { + default fn read_into(buf: &mut impl Read) -> Result { + let length = buf.read_varint()? as usize; + let mut contents = HashMap::with_capacity(length); + for _ in 0..length { + contents.insert(K::read_into(buf)?, V::read_into(buf)?); + } + Ok(contents) + } +} + +impl McBufReadable for Vec { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_byte_array() + } +} + +impl McBufReadable for String { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_utf() + } +} + +impl McBufReadable for u32 { + fn read_into(buf: &mut impl Read) -> Result { + Readable::read_int(buf).map(|i| i as u32) + } +} + +impl McBufVarReadable for u32 { + fn var_read_into(buf: &mut impl Read) -> Result { + buf.read_varint().map(|i| i as u32) + } +} + +impl McBufReadable for u16 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_short().map(|i| i as u16) + } +} + +impl McBufReadable for i16 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_short() + } +} + +impl McBufVarReadable for u16 { + fn var_read_into(buf: &mut impl Read) -> Result { + buf.read_varint().map(|i| i as u16) + } +} + +impl McBufVarReadable for Vec { + fn var_read_into(buf: &mut impl Read) -> Result { + let length = buf.read_varint()? as usize; + let mut contents = Vec::with_capacity(length); + for _ in 0..length { + contents.push(T::var_read_into(buf)?); + } + Ok(contents) + } +} + +impl McBufReadable for i64 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_long() + } +} + +impl McBufReadable for u64 { + fn read_into(buf: &mut impl Read) -> Result { + i64::read_into(buf).map(|i| i as u64) + } +} + +impl McBufReadable for bool { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_boolean() + } +} + +impl McBufReadable for u8 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_byte() + } +} + +impl McBufReadable for i8 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_byte().map(|i| i as i8) + } +} + +impl McBufReadable for f32 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_float() + } +} + +impl McBufReadable for f64 { + fn read_into(buf: &mut impl Read) -> Result { + buf.read_double() + } +} + +impl McBufReadable for Option { + default fn read_into(buf: &mut impl Read) -> Result { + let present = buf.read_boolean()?; + Ok(if present { + Some(T::read_into(buf)?) + } else { + None + }) + } +} diff --git a/azalea-buf/src/write.rs b/azalea-buf/src/write.rs new file mode 100644 index 00000000..fdf58203 --- /dev/null +++ b/azalea-buf/src/write.rs @@ -0,0 +1,284 @@ +use super::{UnsizedByteArray, MAX_STRING_LENGTH}; +use byteorder::{BigEndian, WriteBytesExt}; +use std::{collections::HashMap, io::Write}; + +// TODO: get rid of Writable and use McBufWritable everywhere + +pub trait Writable: Write { + fn write_list(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error> + where + F: FnOnce(&mut Self, &T) -> Result<(), std::io::Error> + Copy, + { + self.write_varint(list.len() as i32)?; + for item in list { + writer(self, item)?; + } + Ok(()) + } + + fn write_int_id_list(&mut self, list: &[i32]) -> Result<(), std::io::Error> { + self.write_list(list, |buf, n| buf.write_varint(*n)) + } + + fn write_map( + &mut self, + map: Vec<(KT, VT)>, + key_writer: KF, + value_writer: VF, + ) -> Result<(), std::io::Error> + where + KF: Fn(&mut Self, KT) -> Result<(), std::io::Error> + Copy, + VF: Fn(&mut Self, VT) -> Result<(), std::io::Error> + Copy, + { + self.write_varint(map.len() as i32)?; + for (key, value) in map { + key_writer(self, key)?; + value_writer(self, value)?; + } + Ok(()) + } + + fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error> { + WriteBytesExt::write_u8(self, n) + } + + fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { + self.write_all(bytes)?; + Ok(()) + } + + fn write_varint(&mut self, mut value: i32) -> Result<(), std::io::Error> { + let mut buffer = [0]; + if value == 0 { + self.write_all(&buffer).unwrap(); + } + while value != 0 { + buffer[0] = (value & 0b0111_1111) as u8; + value = (value >> 7) & (i32::max_value() >> 6); + if value != 0 { + buffer[0] |= 0b1000_0000; + } + self.write_all(&buffer)?; + } + Ok(()) + } + + fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error> { + if string.len() > len { + panic!( + "String too big (was {} bytes encoded, max {})", + string.len(), + len + ); + } + self.write_varint(string.len() as i32)?; + self.write_bytes(string.as_bytes()) + } + + fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error> { + self.write_utf_with_len(string, MAX_STRING_LENGTH.into()) + } + + fn write_short(&mut self, n: i16) -> Result<(), std::io::Error> { + WriteBytesExt::write_i16::(self, n) + } + + fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { + self.write_varint(bytes.len() as i32)?; + self.write_bytes(bytes) + } + + fn write_int(&mut self, n: i32) -> Result<(), std::io::Error> { + WriteBytesExt::write_i32::(self, n) + } + + fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error> { + self.write_byte(if b { 1 } else { 0 }) + } + + fn write_long(&mut self, n: i64) -> Result<(), std::io::Error> { + WriteBytesExt::write_i64::(self, n) + } + + fn write_float(&mut self, n: f32) -> Result<(), std::io::Error> { + WriteBytesExt::write_f32::(self, n) + } + + fn write_double(&mut self, n: f64) -> Result<(), std::io::Error> { + WriteBytesExt::write_f64::(self, n) + } +} + +impl Writable for W {} + +pub trait McBufWritable { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>; +} + +pub trait McBufVarWritable { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>; +} + +impl McBufWritable for i32 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + Writable::write_int(buf, *self) + } +} + +impl McBufVarWritable for i32 { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_varint(*self) + } +} + +impl McBufWritable for UnsizedByteArray { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_bytes(self) + } +} + +impl McBufWritable for Vec { + default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_list(self, |buf, i| T::write_into(i, buf)) + } +} + +impl McBufWritable for HashMap { + default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + u32::var_write_into(&(self.len() as u32), buf)?; + for (key, value) in self { + key.write_into(buf)?; + value.write_into(buf)?; + } + + Ok(()) + } +} + +impl McBufWritable for Vec { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_byte_array(self) + } +} + +impl McBufWritable for String { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_utf(self) + } +} + +impl McBufWritable for u32 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i16::write_into(&(*self as i16), buf) + } +} + +impl McBufVarWritable for u32 { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i32::var_write_into(&(*self as i32), buf) + } +} + +impl McBufVarWritable for i64 { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + let mut buffer = [0]; + let mut value = *self; + while value != 0 { + buffer[0] = (value & 0b0111_1111) as u8; + value = (value >> 7) & (i64::max_value() >> 6); + if value != 0 { + buffer[0] |= 0b1000_0000; + } + buf.write(&mut buffer)?; + } + Ok(()) + } +} + +impl McBufVarWritable for u64 { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i64::var_write_into(&(*self as i64), buf) + } +} + +impl McBufWritable for u16 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i16::write_into(&(*self as i16), buf) + } +} + +impl McBufVarWritable for u16 { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i32::var_write_into(&(*self as i32), buf) + } +} + +impl McBufVarWritable for Vec { + fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + u32::var_write_into(&(self.len() as u32), buf)?; + for i in self { + i.var_write_into(buf)?; + } + Ok(()) + } +} + +impl McBufWritable for u8 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_byte(*self) + } +} + +impl McBufWritable for i16 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + Writable::write_short(buf, *self) + } +} + +impl McBufWritable for i64 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + Writable::write_long(buf, *self) + } +} + +impl McBufWritable for u64 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + i64::write_into(&(*self as i64), buf) + } +} + +impl McBufWritable for bool { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_boolean(*self) + } +} + +impl McBufWritable for i8 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_byte(*self as u8) + } +} + +impl McBufWritable for f32 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_float(*self) + } +} + +impl McBufWritable for f64 { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_double(*self) + } +} + +impl McBufWritable for Option { + default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + if let Some(s) = self { + buf.write_boolean(true)?; + s.write_into(buf)?; + } else { + buf.write_boolean(false)?; + }; + Ok(()) + } +} diff --git a/azalea-chat/Cargo.toml b/azalea-chat/Cargo.toml index 192d3405..5b85f6c2 100755 --- a/azalea-chat/Cargo.toml +++ b/azalea-chat/Cargo.toml @@ -6,6 +6,7 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +azalea-buf = {path = "../azalea-buf"} azalea-language = {path = "../azalea-language"} lazy_static = "1.4.0" serde = "^1.0.130" diff --git a/azalea-chat/src/component.rs b/azalea-chat/src/component.rs index 36709cc0..872a0a5c 100755 --- a/azalea-chat/src/component.rs +++ b/azalea-chat/src/component.rs @@ -264,3 +264,30 @@ impl<'de> Deserialize<'de> for Component { Ok(component) } } + +impl McBufReadable for Component { + fn read_into(buf: &mut impl Read) -> Result { + let string = buf.read_utf()?; + let json: serde_json::Value = serde_json::from_str(string.as_str()) + .map_err(|_| "Component isn't valid JSON".to_string())?; + let component = Component::deserialize(json).map_err(|e| e.to_string())?; + Ok(component) + } +} + +impl McBufWritable for Component { + // async fn read_into(buf: &mut impl Read) -> Result + // where + // R: AsyncRead + std::marker::Unpin + std::marker::Send, + // { + // let string = buf.read_utf().await?; + // let json: serde_json::Value = serde_json::from_str(string.as_str()) + // .map_err(|e| "Component isn't valid JSON".to_string())?; + // let component = Component::deserialize(json).map_err(|e| e.to_string())?; + // Ok(component) + // } + fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> { + // component doesn't have serialize implemented yet + todo!() + } +} diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 828578de..a5259db9 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -1,5 +1,5 @@ use crate::{Account, Player}; -use azalea_core::{resource_location::ResourceLocation, ChunkPos}; +use azalea_core::{resource_location::ResourceLocation, ChunkPos, EntityPos}; use azalea_entity::Entity; use azalea_protocol::{ connect::{GameConnection, HandshakeConnection}, @@ -387,6 +387,13 @@ impl Client { } GamePacket::ClientboundAddPlayerPacket(p) => { println!("Got add player packet {:?}", p); + let entity = Entity::from(p); + state + .lock()? + .world + .as_mut() + .expect("World doesn't exist! We should've gotten a login packet by now.") + .add_entity(entity); } GamePacket::ClientboundInitializeBorderPacket(p) => { println!("Got initialize border packet {:?}", p); @@ -406,20 +413,18 @@ impl Client { GamePacket::ClientboundSetExperiencePacket(p) => { println!("Got set experience packet {:?}", p); } - GamePacket::ClientboundTeleportEntityPacket(_p) => { - // println!("Got teleport entity packet {:?}", p); - // let state_lock = state.lock()?; - - // let entity = state_lock - // .world - // .unwrap() - // .entity_by_id(p.id) - // .ok_or("Teleporting entity that doesn't exist.".to_string())?; - // state_lock - // .world - // .as_mut() - // .expect("World doesn't exist! We should've gotten a login packet by now.") - // .move_entity(&mut entity, new_pos) + GamePacket::ClientboundTeleportEntityPacket(p) => { + let mut state_lock = state.lock()?; + let world = state_lock.world.as_mut().unwrap(); + + world.move_entity( + p.id, + EntityPos { + x: p.x, + y: p.y, + z: p.z, + }, + )?; } GamePacket::ClientboundUpdateAdvancementsPacket(p) => { println!("Got update advancements packet {:?}", p); @@ -427,11 +432,21 @@ impl Client { GamePacket::ClientboundRotateHeadPacket(_p) => { // println!("Got rotate head packet {:?}", p); } - GamePacket::ClientboundMoveEntityPosPacket(_p) => { + GamePacket::ClientboundMoveEntityPosPacket(p) => { // println!("Got move entity pos packet {:?}", p); } - GamePacket::ClientboundMoveEntityPosRotPacket(_p) => { - // println!("Got move entity pos rot packet {:?}", p); + GamePacket::ClientboundMoveEntityPosRotPacket(p) => { + let mut state_lock = state.lock()?; + let world = state_lock.world.as_mut().unwrap(); + + world.move_entity( + p.entity_id, + EntityPos { + x: p.x, + y: p.y, + z: p.z, + }, + )?; } GamePacket::ClientboundMoveEntityRotPacket(p) => { println!("Got move entity rot packet {:?}", p); diff --git a/azalea-core/Cargo.toml b/azalea-core/Cargo.toml index 27112b21..470bc998 100755 --- a/azalea-core/Cargo.toml +++ b/azalea-core/Cargo.toml @@ -6,6 +6,7 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +azalea-buf = {path = "../azalea-buf"} azalea-chat = {path = "../azalea-chat"} azalea-nbt = {path = "../azalea-nbt"} uuid = "^1.1.2" diff --git a/azalea-core/src/delta.rs b/azalea-core/src/delta.rs new file mode 100644 index 00000000..32517e0d --- /dev/null +++ b/azalea-core/src/delta.rs @@ -0,0 +1,26 @@ +/// Only works for up to 8 blocks +#[derive(Clone, Debug, McBuf)] +pub struct PositionDelta { + xa: i16, + ya: i16, + za: i16, +} + +impl PositionDelta { + pub fn float(&self) -> (f64, f64, f64) { + ( + (self.xa as f64) / 4096.0, + (self.ya as f64) / 4096.0, + (self.za as f64) / 4096.0, + ) + } +} + +impl EntityPos { + pub fn apply_delta(&mut self, delta: &PositionDelta) { + let (x, y, z) = delta.float(); + self.x += x; + self.y += y; + self.z += z; + } +} diff --git a/azalea-core/src/difficulty.rs b/azalea-core/src/difficulty.rs index 5d869325..7568f8e2 100755 --- a/azalea-core/src/difficulty.rs +++ b/azalea-core/src/difficulty.rs @@ -61,6 +61,18 @@ impl Difficulty { } } +impl McBufReadable for Difficulty { + fn read_into(buf: &mut impl Read) -> Result { + Ok(Difficulty::by_id(u8::read_into(buf)?)) + } +} + +impl McBufWritable for Difficulty { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + u8::write_into(&self.id(), buf) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-core/src/direction.rs b/azalea-core/src/direction.rs index 721f21a0..bb655bdb 100644 --- a/azalea-core/src/direction.rs +++ b/azalea-core/src/direction.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, McBuf)] pub enum Direction { Down = 0, Up = 1, diff --git a/azalea-core/src/game_type.rs b/azalea-core/src/game_type.rs index f5b9fb38..963048dd 100755 --- a/azalea-core/src/game_type.rs +++ b/azalea-core/src/game_type.rs @@ -71,3 +71,27 @@ impl GameType { } } } + +impl McBufReadable for GameType { + fn read_into(buf: &mut impl Read) -> Result { + GameType::from_id(buf.read_byte()?) + } +} + +impl McBufReadable for Option { + fn read_into(buf: &mut impl Read) -> Result { + GameType::from_optional_id(buf.read_byte()? as i8) + } +} + +impl McBufWritable for GameType { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + u8::write_into(&self.to_id(), buf) + } +} + +impl McBufWritable for Option { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_byte(GameType::to_optional_id(self) as u8) + } +} diff --git a/azalea-core/src/lib.rs b/azalea-core/src/lib.rs index a2632871..41c901c8 100755 --- a/azalea-core/src/lib.rs +++ b/azalea-core/src/lib.rs @@ -15,3 +15,9 @@ pub use position::*; mod direction; pub use direction::Direction; + +mod delta; +pub use delta::*; + +mod particle; +pub use particle::*; diff --git a/azalea-core/src/particle/mod.rs b/azalea-core/src/particle/mod.rs new file mode 100644 index 00000000..fc815a0b --- /dev/null +++ b/azalea-core/src/particle/mod.rs @@ -0,0 +1,259 @@ + +#[derive(Debug, Clone, McBuf)] +pub struct Particle { + #[var] + pub id: i32, + pub data: ParticleData, +} + +#[derive(Clone, Debug)] +pub enum ParticleData { + AmbientEntityEffect, + AngryVillager, + Block(BlockParticle), + BlockMarker(BlockParticle), + Bubble, + Cloud, + Crit, + DamageIndicator, + DragonBreath, + DrippingLava, + FallingLava, + LandingLava, + DrippingWater, + FallingWater, + Dust(DustParticle), + DustColorTransition(DustColorTransitionParticle), + Effect, + ElderGuardian, + EnchantedHit, + Enchant, + EndRod, + EntityEffect, + ExplosionEmitter, + Explosion, + FallingDust(BlockParticle), + Firework, + Fishing, + Flame, + SoulFireFlame, + Soul, + Flash, + HappyVillager, + Composter, + Heart, + InstantEffect, + Item(ItemParticle), + Vibration(VibrationParticle), + ItemSlime, + ItemSnowball, + LargeSmoke, + Lava, + Mycelium, + Note, + Poof, + Portal, + Rain, + Smoke, + Sneeze, + Spit, + SquidInk, + SweepAttack, + TotemOfUndying, + Underwater, + Splash, + Witch, + BubblePop, + CurrentDown, + BubbleColumnUp, + Nautilus, + Dolphin, + CampfireCozySmoke, + CampfireSignalSmoke, + DrippingHoney, + FallingHoney, + LandingHoney, + FallingNectar, + FallingSporeBlossom, + Ash, + CrimsonSpore, + WarpedSpore, + SporeBlossomAir, + DrippingObsidianTear, + FallingObsidianTear, + LandingObsidianTear, + ReversePortal, + WhiteAsh, + SmallFlame, + Snowflake, + DrippingDripstoneLava, + FallingDripstoneLava, + DrippingDripstoneWater, + FallingDripstoneWater, + GlowSquidInk, + Glow, + WaxOn, + WaxOff, + ElectricSpark, + Scrape, +} + +#[derive(Debug, Clone, McBuf)] +pub struct BlockParticle { + #[var] + pub block_state: i32, +} +#[derive(Debug, Clone, McBuf)] +pub struct DustParticle { + /// Red value, 0-1 + pub red: f32, + /// Green value, 0-1 + pub green: f32, + /// Blue value, 0-1 + pub blue: f32, + /// The scale, will be clamped between 0.01 and 4. + pub scale: f32, +} + +#[derive(Debug, Clone, McBuf)] +pub struct DustColorTransitionParticle { + /// Red value, 0-1 + pub from_red: f32, + /// Green value, 0-1 + pub from_green: f32, + /// Blue value, 0-1 + pub from_blue: f32, + /// The scale, will be clamped between 0.01 and 4. + pub scale: f32, + /// Red value, 0-1 + pub to_red: f32, + /// Green value, 0-1 + pub to_green: f32, + /// Blue value, 0-1 + pub to_blue: f32, +} + +#[derive(Debug, Clone, McBuf)] +pub struct ItemParticle { + pub item: Slot, +} + +#[derive(Debug, Clone, McBuf)] +pub struct VibrationParticle { + pub origin: BlockPos, + pub position_type: String, + pub block_position: BlockPos, + #[var] + pub entity_id: u32, + #[var] + pub ticks: u32, +} + +impl ParticleData { + pub fn read_from_particle_id(buf: &mut impl Read, id: u32) -> Result { + Ok(match id { + 0 => ParticleData::AmbientEntityEffect, + 1 => ParticleData::AngryVillager, + 2 => ParticleData::Block(BlockParticle::read_into(buf)?), + 3 => ParticleData::BlockMarker(BlockParticle::read_into(buf)?), + 4 => ParticleData::Bubble, + 5 => ParticleData::Cloud, + 6 => ParticleData::Crit, + 7 => ParticleData::DamageIndicator, + 8 => ParticleData::DragonBreath, + 9 => ParticleData::DrippingLava, + 10 => ParticleData::FallingLava, + 11 => ParticleData::LandingLava, + 12 => ParticleData::DrippingWater, + 13 => ParticleData::FallingWater, + 14 => ParticleData::Dust(DustParticle::read_into(buf)?), + 15 => ParticleData::DustColorTransition(DustColorTransitionParticle::read_into(buf)?), + 16 => ParticleData::Effect, + 17 => ParticleData::ElderGuardian, + 18 => ParticleData::EnchantedHit, + 19 => ParticleData::Enchant, + 20 => ParticleData::EndRod, + 21 => ParticleData::EntityEffect, + 22 => ParticleData::ExplosionEmitter, + 23 => ParticleData::Explosion, + 24 => ParticleData::FallingDust(BlockParticle::read_into(buf)?), + 25 => ParticleData::Firework, + 26 => ParticleData::Fishing, + 27 => ParticleData::Flame, + 28 => ParticleData::SoulFireFlame, + 29 => ParticleData::Soul, + 30 => ParticleData::Flash, + 31 => ParticleData::HappyVillager, + 32 => ParticleData::Composter, + 33 => ParticleData::Heart, + 34 => ParticleData::InstantEffect, + 35 => ParticleData::Item(ItemParticle::read_into(buf)?), + 36 => ParticleData::Vibration(VibrationParticle::read_into(buf)?), + 37 => ParticleData::ItemSlime, + 38 => ParticleData::ItemSnowball, + 39 => ParticleData::LargeSmoke, + 40 => ParticleData::Lava, + 41 => ParticleData::Mycelium, + 42 => ParticleData::Note, + 43 => ParticleData::Poof, + 44 => ParticleData::Portal, + 45 => ParticleData::Rain, + 46 => ParticleData::Smoke, + 47 => ParticleData::Sneeze, + 48 => ParticleData::Spit, + 49 => ParticleData::SquidInk, + 50 => ParticleData::SweepAttack, + 51 => ParticleData::TotemOfUndying, + 52 => ParticleData::Underwater, + 53 => ParticleData::Splash, + 54 => ParticleData::Witch, + 55 => ParticleData::BubblePop, + 56 => ParticleData::CurrentDown, + 57 => ParticleData::BubbleColumnUp, + 58 => ParticleData::Nautilus, + 59 => ParticleData::Dolphin, + 60 => ParticleData::CampfireCozySmoke, + 61 => ParticleData::CampfireSignalSmoke, + 62 => ParticleData::DrippingHoney, + 63 => ParticleData::FallingHoney, + 64 => ParticleData::LandingHoney, + 65 => ParticleData::FallingNectar, + 66 => ParticleData::FallingSporeBlossom, + 67 => ParticleData::Ash, + 68 => ParticleData::CrimsonSpore, + 69 => ParticleData::WarpedSpore, + 70 => ParticleData::SporeBlossomAir, + 71 => ParticleData::DrippingObsidianTear, + 72 => ParticleData::FallingObsidianTear, + 73 => ParticleData::LandingObsidianTear, + 74 => ParticleData::ReversePortal, + 75 => ParticleData::WhiteAsh, + 76 => ParticleData::SmallFlame, + 77 => ParticleData::Snowflake, + 78 => ParticleData::DrippingDripstoneLava, + 79 => ParticleData::FallingDripstoneLava, + 80 => ParticleData::DrippingDripstoneWater, + 81 => ParticleData::FallingDripstoneWater, + 82 => ParticleData::GlowSquidInk, + 83 => ParticleData::Glow, + 84 => ParticleData::WaxOn, + 85 => ParticleData::WaxOff, + 86 => ParticleData::ElectricSpark, + 87 => ParticleData::Scrape, + _ => return Err(format!("Unknown particle id: {}", id)), + }) + } +} + +impl McBufReadable for ParticleData { + fn read_into(buf: &mut impl Read) -> Result { + let id = u32::var_read_into(buf)?; + ParticleData::read_from_particle_id(buf, id) + } +} + +impl McBufWritable for ParticleData { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + todo!() + } +} diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index 7b2c01da..9e7aca20 100644 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -170,6 +170,65 @@ impl From<&EntityPos> for ChunkPos { } } +impl McBufReadable for BlockPos { + fn read_into(buf: &mut impl Read) -> Result { + let val = u64::read_into(buf)?; + let x = (val >> 38) as i32; + let y = (val & 0xFFF) as i32; + let z = ((val >> 12) & 0x3FFFFFF) as i32; + Ok(BlockPos { x, y, z }) + } +} + +impl McBufReadable for GlobalPos { + fn read_into(buf: &mut impl Read) -> Result { + Ok(GlobalPos { + dimension: ResourceLocation::read_into(buf)?, + pos: BlockPos::read_into(buf)?, + }) + } +} + +impl McBufReadable for ChunkSectionPos { + fn read_into(buf: &mut impl Read) -> Result { + let long = i64::read_into(buf)?; + Ok(ChunkSectionPos { + x: (long >> 42) as i32, + y: (long << 44 >> 44) as i32, + z: (long << 22 >> 42) as i32, + }) + } +} + +impl McBufWritable for BlockPos { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_long( + (((self.x & 0x3FFFFFF) as i64) << 38) + | (((self.z & 0x3FFFFFF) as i64) << 12) + | ((self.y & 0xFFF) as i64), + ) + } +} + +impl McBufWritable for GlobalPos { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + ResourceLocation::write_into(&self.dimension, buf)?; + BlockPos::write_into(&self.pos, buf)?; + + Ok(()) + } +} + +impl McBufWritable for ChunkSectionPos { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + let long = (((self.x & 0x3FFFFF) as i64) << 42) + | (self.y & 0xFFFFF) as i64 + | (((self.z & 0x3FFFFF) as i64) << 20); + long.write_into(buf)?; + Ok(()) + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs index cdf8f381..6807714b 100755 --- a/azalea-core/src/resource_location.rs +++ b/azalea-core/src/resource_location.rs @@ -42,6 +42,18 @@ impl std::fmt::Debug for ResourceLocation { } } +impl McBufReadable for ResourceLocation { + fn read_into(buf: &mut impl Read) -> Result { + let location_string = self.read_utf()?; + ResourceLocation::new(&location_string) + } +} +impl McBufWritable for ResourceLocation { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + buf.write_utf(&self.to_string()) + } +} + #[cfg(test)] mod tests { use super::*; @@ -70,4 +82,18 @@ mod tests { assert_eq!(r.namespace, "azalea"); assert_eq!(r.path, ""); } + + #[test] + fn mcbuf_resource_location() { + let mut buf = Vec::new(); + buf.write_resource_location(&ResourceLocation::new("minecraft:dirt").unwrap()) + .unwrap(); + + let mut buf = Cursor::new(buf); + + assert_eq!( + buf.read_resource_location().unwrap(), + ResourceLocation::new("minecraft:dirt").unwrap() + ); + } } diff --git a/azalea-core/src/serializable_uuid.rs b/azalea-core/src/serializable_uuid.rs index f8c03b60..2c7128ff 100755 --- a/azalea-core/src/serializable_uuid.rs +++ b/azalea-core/src/serializable_uuid.rs @@ -30,6 +30,28 @@ impl SerializableUuid for Uuid { } } +impl McBufReadable for Uuid { + fn read_into(buf: &mut impl Read) -> Result { + Ok(Uuid::from_int_array([ + Readable::read_int(self)? as u32, + Readable::read_int(self)? as u32, + Readable::read_int(self)? as u32, + Readable::read_int(self)? as u32, + ])) + } +} + +impl McBufWritable for Uuid { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + let [a, b, c, d] = self.to_int_array(); + a.write_into(buf)?; + b.write_into(buf)?; + c.write_into(buf)?; + d.write_into(buf)?; + Ok(()) + } +} + #[cfg(tests)] mod tests { use super::*; diff --git a/azalea-core/src/slot.rs b/azalea-core/src/slot.rs index 5e42f558..e3b78289 100644 --- a/azalea-core/src/slot.rs +++ b/azalea-core/src/slot.rs @@ -12,3 +12,31 @@ pub struct SlotData { pub count: u8, pub nbt: azalea_nbt::Tag, } + +impl McBufReadable for Slot { + fn read_into(buf: &mut impl Read) -> Result { + let present = buf.read_boolean()?; + if !present { + return Ok(Slot::Empty); + } + let id = buf.read_varint()?; + let count = buf.read_byte()?; + let nbt = buf.read_nbt()?; + Ok(Slot::Present(SlotData { id, count, nbt })) + } +} + +impl McBufWritable for Slot { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + match self { + Slot::Empty => buf.write_byte(0)?, + Slot::Present(i) => { + buf.write_varint(i.id)?; + buf.write_byte(i.count)?; + buf.write_nbt(&i.nbt)?; + } + } + + Ok(()) + } +} diff --git a/azalea-crypto/Cargo.toml b/azalea-crypto/Cargo.toml index 2532bff9..ee652565 100644 --- a/azalea-crypto/Cargo.toml +++ b/azalea-crypto/Cargo.toml @@ -7,6 +7,7 @@ version = "0.1.0" [dependencies] aes = "0.8.1" +azalea-buf = {path = "../azalea-buf"} cfb8 = "0.8.1" num-bigint = "^0.4.3" rand = {version = "^0.8.4", features = ["getrandom"]} diff --git a/azalea-crypto/src/lib.rs b/azalea-crypto/src/lib.rs index a5e797e8..85705883 100644 --- a/azalea-crypto/src/lib.rs +++ b/azalea-crypto/src/lib.rs @@ -79,6 +79,8 @@ pub fn decrypt_packet(cipher: &mut Aes128CfbDec, packet: &mut [u8]) { cipher.decrypt_blocks_inout_mut(chunks); } + + #[cfg(test)] mod tests { use super::*; diff --git a/azalea-crypto/src/signing.rs b/azalea-crypto/src/signing.rs index 21cd813a..a5280a18 100644 --- a/azalea-crypto/src/signing.rs +++ b/azalea-crypto/src/signing.rs @@ -3,3 +3,19 @@ pub struct SaltSignaturePair { pub salt: u64, pub signature: Vec, } + +impl McBufReadable for SaltSignaturePair { + fn read_into(buf: &mut impl Read) -> Result { + let salt = u64::read_into(buf)?; + let signature = Vec::::read_into(buf)?; + Ok(SaltSignaturePair { salt, signature }) + } +} + +impl McBufWritable for SaltSignaturePair { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + self.salt.write_into(buf)?; + self.signature.write_into(buf)?; + Ok(()) + } +} diff --git a/azalea-entity/src/data.rs b/azalea-entity/src/data.rs new file mode 100644 index 00000000..ddc6f57b --- /dev/null +++ b/azalea-entity/src/data.rs @@ -0,0 +1,136 @@ +pub type EntityMetadata = Vec; + +#[derive(Clone, Debug)] +pub struct EntityDataItem { + // we can't identify what the index is for here because we don't know the + // entity type + pub index: u8, + pub value: EntityDataValue, +} + +impl McBufReadable for Vec { + fn read_into(buf: &mut impl Read) -> Result { + let mut metadata = Vec::new(); + loop { + let index = u8::read_into(buf)?; + if index == 0xff { + break; + } + let value = EntityDataValue::read_into(buf)?; + metadata.push(EntityDataItem { index, value }); + } + Ok(metadata) + } +} + +impl McBufWritable for Vec { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + for item in self { + buf.write_byte(item.index)?; + item.value.write_into(buf)?; + } + buf.write_byte(0xff)?; + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub enum EntityDataValue { + Byte(u8), + // varint + Int(i32), + Float(f32), + String(String), + Component(Component), + OptionalComponent(Option), + ItemStack(Slot), + Boolean(bool), + Rotations { x: f32, y: f32, z: f32 }, + BlockPos(BlockPos), + OptionalBlockPos(Option), + Direction(Direction), + OptionalUuid(Option), + // 0 for absent (implies air); otherwise, a block state ID as per the global palette + // this is a varint + OptionalBlockState(Option), + CompoundTag(azalea_nbt::Tag), + Particle(Particle), + VillagerData(VillagerData), + // 0 for absent; 1 + actual value otherwise. Used for entity IDs. + OptionalUnsignedInt(Option), + Pose(Pose), +} + +impl McBufReadable for EntityDataValue { + fn read_into(buf: &mut impl Read) -> Result { + let type_ = buf.read_varint()?; + Ok(match type_ { + 0 => EntityDataValue::Byte(buf.read_byte()?), + 1 => EntityDataValue::Int(buf.read_varint()?), + 2 => EntityDataValue::Float(buf.read_float()?), + 3 => EntityDataValue::String(buf.read_utf()?), + 4 => EntityDataValue::Component(Component::read_into(buf)?), + 5 => EntityDataValue::OptionalComponent(Option::::read_into(buf)?), + 6 => EntityDataValue::ItemStack(Slot::read_into(buf)?), + 7 => EntityDataValue::Boolean(buf.read_boolean()?), + 8 => EntityDataValue::Rotations { + x: buf.read_float()?, + y: buf.read_float()?, + z: buf.read_float()?, + }, + 9 => EntityDataValue::BlockPos(BlockPos::read_into(buf)?), + 10 => EntityDataValue::OptionalBlockPos(Option::::read_into(buf)?), + 11 => EntityDataValue::Direction(Direction::read_into(buf)?), + 12 => EntityDataValue::OptionalUuid(Option::::read_into(buf)?), + 13 => EntityDataValue::OptionalBlockState({ + let val = i32::read_into(buf)?; + if val == 0 { + None + } else { + Some(val) + } + }), + 14 => EntityDataValue::CompoundTag(azalea_nbt::Tag::read_into(buf)?), + 15 => EntityDataValue::Particle(Particle::read_into(buf)?), + 16 => EntityDataValue::VillagerData(VillagerData::read_into(buf)?), + 17 => EntityDataValue::OptionalUnsignedInt({ + let val = buf.read_varint()?; + if val == 0 { + None + } else { + Some((val - 1) as u32) + } + }), + 18 => EntityDataValue::Pose(Pose::read_into(buf)?), + _ => return Err(format!("Unknown entity data type: {}", type_)), + }) + } +} + +impl McBufWritable for EntityDataValue { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + todo!(); + } +} + +#[derive(Clone, Debug, Copy, McBuf)] +pub enum Pose { + Standing = 0, + FallFlying = 1, + Sleeping = 2, + Swimming = 3, + SpinAttack = 4, + Sneaking = 5, + LongJumping = 6, + Dying = 7, +} + +#[derive(Debug, Clone, McBuf)] +pub struct VillagerData { + #[var] + type_: u32, + #[var] + profession: u32, + #[var] + level: u32, +} diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index d32a1a96..2283142f 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -1,6 +1,11 @@ +mod data; + use azalea_core::EntityPos; #[cfg(feature = "protocol")] -use azalea_protocol::packets::game::clientbound_add_entity_packet::ClientboundAddEntityPacket; +use azalea_protocol::packets::game::{ + clientbound_add_entity_packet::ClientboundAddEntityPacket, + clientbound_add_player_packet::ClientboundAddPlayerPacket, +}; use uuid::Uuid; #[derive(Default, Debug)] @@ -38,6 +43,21 @@ impl From<&ClientboundAddEntityPacket> for Entity { } } +#[cfg(feature = "protocol")] +impl From<&ClientboundAddPlayerPacket> for Entity { + fn from(p: &ClientboundAddPlayerPacket) -> Self { + Self { + id: p.id, + uuid: p.uuid, + pos: EntityPos { + x: p.x, + y: p.y, + z: p.z, + }, + } + } +} + // #[cfg(test)] // mod tests { // #[test] diff --git a/azalea-nbt/Cargo.toml b/azalea-nbt/Cargo.toml index ad466e1f..992d242a 100755 --- a/azalea-nbt/Cargo.toml +++ b/azalea-nbt/Cargo.toml @@ -6,6 +6,7 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +azalea-buf = {path = "../azalea-buf"} byteorder = "1.4.3" flate2 = "1.0.23" num-derive = "^0.3.3" diff --git a/azalea-nbt/src/decode.rs b/azalea-nbt/src/decode.rs index 7f2ca754..73cd613e 100755 --- a/azalea-nbt/src/decode.rs +++ b/azalea-nbt/src/decode.rs @@ -136,3 +136,13 @@ impl Tag { Tag::read(&mut gz) } } + +impl McBufReadable for Tag { + fn read_into(buf: &mut impl Read) -> Result { + match Tag::read(self) { + Ok(r) => Ok(r), + // Err(e) => Err(e.to_string()), + Err(e) => Err(e.to_string()).unwrap(), + } + } +} diff --git a/azalea-nbt/src/encode.rs b/azalea-nbt/src/encode.rs index fb5585b3..17d20270 100755 --- a/azalea-nbt/src/encode.rs +++ b/azalea-nbt/src/encode.rs @@ -217,3 +217,10 @@ impl Tag { self.write(&mut encoder) } } + +impl McBufWritable for Tag { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + self.write(buf) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) + } +} diff --git a/azalea-nbt/src/lib.rs b/azalea-nbt/src/lib.rs index d14fd929..8cca1f2b 100755 --- a/azalea-nbt/src/lib.rs +++ b/azalea-nbt/src/lib.rs @@ -5,3 +5,36 @@ mod tag; pub use error::Error; pub use tag::Tag; + +#[cfg(test)] +mod tests { + use super::*; + use std::{collections::HashMap, io::Cursor}; + + #[test] + fn mcbuf_nbt() { + let mut buf = Vec::new(); + buf.write_nbt(&Tag::Compound(HashMap::from_iter(vec![( + "hello world".to_string(), + Tag::Compound(HashMap::from_iter(vec![( + "name".to_string(), + Tag::String("Bananrama".to_string()), + )])), + )]))) + .unwrap(); + + let mut buf = Cursor::new(buf); + + let result = buf.read_nbt().unwrap(); + assert_eq!( + result, + Tag::Compound(HashMap::from_iter(vec![( + "hello world".to_string(), + Tag::Compound(HashMap::from_iter(vec![( + "name".to_string(), + Tag::String("Bananrama".to_string()), + )])), + )])) + ); + } +} diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml index f1640a01..dadb212c 100755 --- a/azalea-protocol/Cargo.toml +++ b/azalea-protocol/Cargo.toml @@ -6,12 +6,13 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -async-compression = {version = "^0.3.8", features = ["tokio", "zlib"]} +async-compression = {version = "^0.3.8", features = ["tokio", "zlib"], optional = true} async-recursion = "^0.3.2" azalea-auth = {path = "../azalea-auth"} azalea-brigadier = {path = "../azalea-brigadier"} +azalea-buf = {path = "../azalea-buf"} azalea-chat = {path = "../azalea-chat"} -azalea-core = {path = "../azalea-core"} +azalea-core = {path = "../azalea-core", optional = true} azalea-crypto = {path = "../azalea-crypto"} azalea-nbt = {path = "../azalea-nbt"} byteorder = "^1.4.3" @@ -27,3 +28,8 @@ tokio = {version = "^1.19.2", features = ["io-util", "net", "macros"]} tokio-util = "^0.6.9" trust-dns-resolver = "^0.20.3" uuid = "^1.1.2" + +[features] +connecting = [] +default = ["packets"] +packets = ["connecting", "dep:async-compression", "dep:azalea-core"] diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 5ea69a62..56b5dc52 100755 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -6,180 +6,6 @@ use syn::{ parse_macro_input, Data, DeriveInput, FieldsNamed, Ident, LitInt, Token, }; -fn create_impl_mcbufreadable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream { - match data { - syn::Data::Struct(syn::DataStruct { fields, .. }) => { - let FieldsNamed { named, .. } = match fields { - syn::Fields::Named(f) => f, - _ => panic!("#[derive(*Packet)] can only be used on structs with named fields"), - }; - - let read_fields = named - .iter() - .map(|f| { - let field_name = &f.ident; - let field_type = &f.ty; - // do a different buf.write_* for each field depending on the type - // if it's a string, use buf.write_string - match field_type { - syn::Type::Path(_) => { - if f.attrs.iter().any(|a| a.path.is_ident("var")) { - quote! { - let #field_name = crate::mc_buf::McBufVarReadable::var_read_into(buf)?; - } - } else { - quote! { - let #field_name = crate::mc_buf::McBufReadable::read_into(buf)?; - } - } - } - _ => panic!( - "Error reading field {}: {}", - field_name.clone().unwrap(), - field_type.to_token_stream() - ), - } - }) - .collect::>(); - let read_field_names = named.iter().map(|f| &f.ident).collect::>(); - - quote! { - impl crate::mc_buf::McBufReadable for #ident { - fn read_into(buf: &mut impl std::io::Read) -> Result { - #(#read_fields)* - Ok(#ident { - #(#read_field_names: #read_field_names),* - }) - } - } - } - } - syn::Data::Enum(syn::DataEnum { variants, .. }) => { - let mut match_contents = quote!(); - let mut variant_discrim: u32 = 0; - for variant in variants { - let variant_name = &variant.ident; - match &variant.discriminant.as_ref() { - Some(d) => { - variant_discrim = match &d.1 { - syn::Expr::Lit(e) => match &e.lit { - syn::Lit::Int(i) => i.base10_parse().unwrap(), - _ => panic!("Error parsing enum discriminant"), - }, - _ => panic!("Error parsing enum discriminant"), - } - } - None => { - variant_discrim += 1; - } - } - match_contents.extend(quote! { - #variant_discrim => Ok(Self::#variant_name), - }); - } - - quote! { - impl crate::mc_buf::McBufReadable for #ident { - fn read_into(buf: &mut impl std::io::Read) -> Result - { - let id = crate::mc_buf::McBufVarReadable::var_read_into(buf)?; - match id { - #match_contents - _ => Err(format!("Unknown enum variant {}", id)), - } - } - } - } - } - _ => panic!("#[derive(*Packet)] can only be used on structs"), - } -} - -fn create_impl_mcbufwritable(ident: &Ident, data: &Data) -> proc_macro2::TokenStream { - match data { - syn::Data::Struct(syn::DataStruct { fields, .. }) => { - let FieldsNamed { named, .. } = match fields { - syn::Fields::Named(f) => f, - _ => panic!("#[derive(*Packet)] can only be used on structs with named fields"), - }; - - let write_fields = named - .iter() - .map(|f| { - let field_name = &f.ident; - let field_type = &f.ty; - // do a different buf.write_* for each field depending on the type - // if it's a string, use buf.write_string - match field_type { - syn::Type::Path(_) => { - if f.attrs.iter().any(|attr| attr.path.is_ident("var")) { - quote! { - crate::mc_buf::McBufVarWritable::var_write_into(&self.#field_name, buf)?; - } - } else { - quote! { - crate::mc_buf::McBufWritable::write_into(&self.#field_name, buf)?; - } - } - } - _ => panic!( - "Error writing field {}: {}", - field_name.clone().unwrap(), - field_type.to_token_stream() - ), - } - }) - .collect::>(); - - quote! { - impl crate::mc_buf::McBufWritable for #ident { - fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - #(#write_fields)* - Ok(()) - } - } - } - } - syn::Data::Enum(syn::DataEnum { .. }) => { - quote! { - impl crate::mc_buf::McBufWritable for #ident { - fn write_into(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - crate::mc_buf::Writable::write_varint(buf, *self as i32) - } - } - } - } - _ => panic!("#[derive(*Packet)] can only be used on structs"), - } -} - -#[proc_macro_derive(McBufReadable, attributes(var))] -pub fn derive_mcbufreadable(input: TokenStream) -> TokenStream { - let DeriveInput { ident, data, .. } = parse_macro_input!(input); - - create_impl_mcbufreadable(&ident, &data).into() -} - -#[proc_macro_derive(McBufWritable, attributes(var))] -pub fn derive_mcbufwritable(input: TokenStream) -> TokenStream { - let DeriveInput { ident, data, .. } = parse_macro_input!(input); - - create_impl_mcbufwritable(&ident, &data).into() -} - -#[proc_macro_derive(McBuf, attributes(var))] -pub fn derive_mcbuf(input: TokenStream) -> TokenStream { - let DeriveInput { ident, data, .. } = parse_macro_input!(input); - - let writable = create_impl_mcbufwritable(&ident, &data); - let readable = create_impl_mcbufreadable(&ident, &data); - quote! { - #writable - #readable - } - .into() -} - fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> TokenStream { let DeriveInput { ident, data, .. } = parse_macro_input!(input); @@ -192,9 +18,6 @@ fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> Toke _ => panic!("#[derive(*Packet)] can only be used on structs with named fields"), }; - let _mcbufreadable_impl = create_impl_mcbufreadable(&ident, &data); - let _mcbufwritable_impl = create_impl_mcbufwritable(&ident, &data); - let contents = quote! { impl #ident { pub fn get(self) -> #state { diff --git a/azalea-protocol/src/lib.rs b/azalea-protocol/src/lib.rs index d7f75f00..cadbb437 100755 --- a/azalea-protocol/src/lib.rs +++ b/azalea-protocol/src/lib.rs @@ -1,13 +1,12 @@ //! This lib is responsible for parsing Minecraft packets. -#![feature(min_specialization)] -#![feature(arbitrary_enum_discriminant)] - use std::net::IpAddr; use std::str::FromStr; +#[cfg(feature = "connecting")] pub mod connect; pub mod mc_buf; +#[cfg(feature = "packets")] pub mod packets; pub mod read; pub mod resolver; @@ -43,6 +42,7 @@ impl<'a> TryFrom<&'a str> for ServerAddress { } } +#[cfg(feature = "connecting")] pub async fn connect(address: ServerAddress) -> Result<(), Box> { let resolved_address = resolver::resolve_address(&address).await; println!("Resolved address: {:?}", resolved_address); diff --git a/azalea-protocol/src/mc_buf/definitions.rs b/azalea-protocol/src/mc_buf/definitions.rs deleted file mode 100644 index 3867b3fc..00000000 --- a/azalea-protocol/src/mc_buf/definitions.rs +++ /dev/null @@ -1,442 +0,0 @@ -use crate::mc_buf::read::{McBufReadable, Readable}; -use crate::mc_buf::write::{McBufWritable, Writable}; -use crate::mc_buf::McBufVarReadable; -use azalea_chat::component::Component; -use azalea_core::{BlockPos, Direction, Slot}; -use packet_macros::McBuf; -use std::io::{Read, Write}; -use std::ops::Deref; -use uuid::Uuid; - -/// A Vec that isn't prefixed by a VarInt with the size. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct UnsizedByteArray(Vec); - -impl Deref for UnsizedByteArray { - type Target = Vec; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl From> for UnsizedByteArray { - fn from(vec: Vec) -> Self { - Self(vec) - } -} - -impl From<&str> for UnsizedByteArray { - fn from(s: &str) -> Self { - Self(s.as_bytes().to_vec()) - } -} - -/// Represents Java's BitSet, a list of bits. -#[derive(Debug, Clone, PartialEq, Eq, Hash, McBuf)] -pub struct BitSet { - data: Vec, -} - -// the Index trait requires us to return a reference, but we can't do that -impl BitSet { - pub fn index(&self, index: usize) -> bool { - (self.data[index / 64] & (1u64 << (index % 64))) != 0 - } -} - -pub type EntityMetadata = Vec; - -#[derive(Clone, Debug)] -pub struct EntityDataItem { - // we can't identify what the index is for here because we don't know the - // entity type - pub index: u8, - pub value: EntityDataValue, -} - -impl McBufReadable for Vec { - fn read_into(buf: &mut impl Read) -> Result { - let mut metadata = Vec::new(); - loop { - let index = buf.read_byte()?; - if index == 0xff { - break; - } - let value = EntityDataValue::read_into(buf)?; - metadata.push(EntityDataItem { index, value }); - } - Ok(metadata) - } -} - -impl McBufWritable for Vec { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - for item in self { - buf.write_byte(item.index)?; - item.value.write_into(buf)?; - } - buf.write_byte(0xff)?; - Ok(()) - } -} - -#[derive(Clone, Debug)] -pub enum EntityDataValue { - Byte(u8), - // varint - Int(i32), - Float(f32), - String(String), - Component(Component), - OptionalComponent(Option), - ItemStack(Slot), - Boolean(bool), - Rotations { x: f32, y: f32, z: f32 }, - BlockPos(BlockPos), - OptionalBlockPos(Option), - Direction(Direction), - OptionalUuid(Option), - // 0 for absent (implies air); otherwise, a block state ID as per the global palette - // this is a varint - OptionalBlockState(Option), - CompoundTag(azalea_nbt::Tag), - Particle(Particle), - VillagerData(VillagerData), - // 0 for absent; 1 + actual value otherwise. Used for entity IDs. - OptionalUnsignedInt(Option), - Pose(Pose), -} - -impl McBufReadable for EntityDataValue { - fn read_into(buf: &mut impl Read) -> Result { - let type_ = buf.read_varint()?; - Ok(match type_ { - 0 => EntityDataValue::Byte(buf.read_byte()?), - 1 => EntityDataValue::Int(buf.read_varint()?), - 2 => EntityDataValue::Float(buf.read_float()?), - 3 => EntityDataValue::String(buf.read_utf()?), - 4 => EntityDataValue::Component(Component::read_into(buf)?), - 5 => EntityDataValue::OptionalComponent(Option::::read_into(buf)?), - 6 => EntityDataValue::ItemStack(Slot::read_into(buf)?), - 7 => EntityDataValue::Boolean(buf.read_boolean()?), - 8 => EntityDataValue::Rotations { - x: buf.read_float()?, - y: buf.read_float()?, - z: buf.read_float()?, - }, - 9 => EntityDataValue::BlockPos(BlockPos::read_into(buf)?), - 10 => EntityDataValue::OptionalBlockPos(Option::::read_into(buf)?), - 11 => EntityDataValue::Direction(Direction::read_into(buf)?), - 12 => EntityDataValue::OptionalUuid(Option::::read_into(buf)?), - 13 => EntityDataValue::OptionalBlockState({ - let val = i32::read_into(buf)?; - if val == 0 { - None - } else { - Some(val) - } - }), - 14 => EntityDataValue::CompoundTag(azalea_nbt::Tag::read_into(buf)?), - 15 => EntityDataValue::Particle(Particle::read_into(buf)?), - 16 => EntityDataValue::VillagerData(VillagerData::read_into(buf)?), - 17 => EntityDataValue::OptionalUnsignedInt({ - let val = buf.read_varint()?; - if val == 0 { - None - } else { - Some((val - 1) as u32) - } - }), - 18 => EntityDataValue::Pose(Pose::read_into(buf)?), - _ => return Err(format!("Unknown entity data type: {}", type_)), - }) - } -} - -impl McBufWritable for EntityDataValue { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - todo!(); - } -} - -#[derive(Clone, Debug, Copy, McBuf)] -pub enum Pose { - Standing = 0, - FallFlying = 1, - Sleeping = 2, - Swimming = 3, - SpinAttack = 4, - Sneaking = 5, - LongJumping = 6, - Dying = 7, -} - -#[derive(Debug, Clone, McBuf)] -pub struct VillagerData { - #[var] - type_: u32, - #[var] - profession: u32, - #[var] - level: u32, -} - -#[derive(Debug, Clone, McBuf)] -pub struct Particle { - #[var] - pub id: i32, - pub data: ParticleData, -} - -#[derive(Clone, Debug)] -pub enum ParticleData { - AmbientEntityEffect, - AngryVillager, - Block(BlockParticle), - BlockMarker(BlockParticle), - Bubble, - Cloud, - Crit, - DamageIndicator, - DragonBreath, - DrippingLava, - FallingLava, - LandingLava, - DrippingWater, - FallingWater, - Dust(DustParticle), - DustColorTransition(DustColorTransitionParticle), - Effect, - ElderGuardian, - EnchantedHit, - Enchant, - EndRod, - EntityEffect, - ExplosionEmitter, - Explosion, - FallingDust(BlockParticle), - Firework, - Fishing, - Flame, - SoulFireFlame, - Soul, - Flash, - HappyVillager, - Composter, - Heart, - InstantEffect, - Item(ItemParticle), - Vibration(VibrationParticle), - ItemSlime, - ItemSnowball, - LargeSmoke, - Lava, - Mycelium, - Note, - Poof, - Portal, - Rain, - Smoke, - Sneeze, - Spit, - SquidInk, - SweepAttack, - TotemOfUndying, - Underwater, - Splash, - Witch, - BubblePop, - CurrentDown, - BubbleColumnUp, - Nautilus, - Dolphin, - CampfireCozySmoke, - CampfireSignalSmoke, - DrippingHoney, - FallingHoney, - LandingHoney, - FallingNectar, - FallingSporeBlossom, - Ash, - CrimsonSpore, - WarpedSpore, - SporeBlossomAir, - DrippingObsidianTear, - FallingObsidianTear, - LandingObsidianTear, - ReversePortal, - WhiteAsh, - SmallFlame, - Snowflake, - DrippingDripstoneLava, - FallingDripstoneLava, - DrippingDripstoneWater, - FallingDripstoneWater, - GlowSquidInk, - Glow, - WaxOn, - WaxOff, - ElectricSpark, - Scrape, -} - -#[derive(Debug, Clone, McBuf)] -pub struct BlockParticle { - #[var] - pub block_state: i32, -} -#[derive(Debug, Clone, McBuf)] -pub struct DustParticle { - /// Red value, 0-1 - pub red: f32, - /// Green value, 0-1 - pub green: f32, - /// Blue value, 0-1 - pub blue: f32, - /// The scale, will be clamped between 0.01 and 4. - pub scale: f32, -} - -#[derive(Debug, Clone, McBuf)] -pub struct DustColorTransitionParticle { - /// Red value, 0-1 - pub from_red: f32, - /// Green value, 0-1 - pub from_green: f32, - /// Blue value, 0-1 - pub from_blue: f32, - /// The scale, will be clamped between 0.01 and 4. - pub scale: f32, - /// Red value, 0-1 - pub to_red: f32, - /// Green value, 0-1 - pub to_green: f32, - /// Blue value, 0-1 - pub to_blue: f32, -} - -#[derive(Debug, Clone, McBuf)] -pub struct ItemParticle { - pub item: Slot, -} - -#[derive(Debug, Clone, McBuf)] -pub struct VibrationParticle { - pub origin: BlockPos, - pub position_type: String, - pub block_position: BlockPos, - #[var] - pub entity_id: u32, - #[var] - pub ticks: u32, -} - -impl ParticleData { - pub fn read_from_particle_id(buf: &mut impl Read, id: u32) -> Result { - Ok(match id { - 0 => ParticleData::AmbientEntityEffect, - 1 => ParticleData::AngryVillager, - 2 => ParticleData::Block(BlockParticle::read_into(buf)?), - 3 => ParticleData::BlockMarker(BlockParticle::read_into(buf)?), - 4 => ParticleData::Bubble, - 5 => ParticleData::Cloud, - 6 => ParticleData::Crit, - 7 => ParticleData::DamageIndicator, - 8 => ParticleData::DragonBreath, - 9 => ParticleData::DrippingLava, - 10 => ParticleData::FallingLava, - 11 => ParticleData::LandingLava, - 12 => ParticleData::DrippingWater, - 13 => ParticleData::FallingWater, - 14 => ParticleData::Dust(DustParticle::read_into(buf)?), - 15 => ParticleData::DustColorTransition(DustColorTransitionParticle::read_into(buf)?), - 16 => ParticleData::Effect, - 17 => ParticleData::ElderGuardian, - 18 => ParticleData::EnchantedHit, - 19 => ParticleData::Enchant, - 20 => ParticleData::EndRod, - 21 => ParticleData::EntityEffect, - 22 => ParticleData::ExplosionEmitter, - 23 => ParticleData::Explosion, - 24 => ParticleData::FallingDust(BlockParticle::read_into(buf)?), - 25 => ParticleData::Firework, - 26 => ParticleData::Fishing, - 27 => ParticleData::Flame, - 28 => ParticleData::SoulFireFlame, - 29 => ParticleData::Soul, - 30 => ParticleData::Flash, - 31 => ParticleData::HappyVillager, - 32 => ParticleData::Composter, - 33 => ParticleData::Heart, - 34 => ParticleData::InstantEffect, - 35 => ParticleData::Item(ItemParticle::read_into(buf)?), - 36 => ParticleData::Vibration(VibrationParticle::read_into(buf)?), - 37 => ParticleData::ItemSlime, - 38 => ParticleData::ItemSnowball, - 39 => ParticleData::LargeSmoke, - 40 => ParticleData::Lava, - 41 => ParticleData::Mycelium, - 42 => ParticleData::Note, - 43 => ParticleData::Poof, - 44 => ParticleData::Portal, - 45 => ParticleData::Rain, - 46 => ParticleData::Smoke, - 47 => ParticleData::Sneeze, - 48 => ParticleData::Spit, - 49 => ParticleData::SquidInk, - 50 => ParticleData::SweepAttack, - 51 => ParticleData::TotemOfUndying, - 52 => ParticleData::Underwater, - 53 => ParticleData::Splash, - 54 => ParticleData::Witch, - 55 => ParticleData::BubblePop, - 56 => ParticleData::CurrentDown, - 57 => ParticleData::BubbleColumnUp, - 58 => ParticleData::Nautilus, - 59 => ParticleData::Dolphin, - 60 => ParticleData::CampfireCozySmoke, - 61 => ParticleData::CampfireSignalSmoke, - 62 => ParticleData::DrippingHoney, - 63 => ParticleData::FallingHoney, - 64 => ParticleData::LandingHoney, - 65 => ParticleData::FallingNectar, - 66 => ParticleData::FallingSporeBlossom, - 67 => ParticleData::Ash, - 68 => ParticleData::CrimsonSpore, - 69 => ParticleData::WarpedSpore, - 70 => ParticleData::SporeBlossomAir, - 71 => ParticleData::DrippingObsidianTear, - 72 => ParticleData::FallingObsidianTear, - 73 => ParticleData::LandingObsidianTear, - 74 => ParticleData::ReversePortal, - 75 => ParticleData::WhiteAsh, - 76 => ParticleData::SmallFlame, - 77 => ParticleData::Snowflake, - 78 => ParticleData::DrippingDripstoneLava, - 79 => ParticleData::FallingDripstoneLava, - 80 => ParticleData::DrippingDripstoneWater, - 81 => ParticleData::FallingDripstoneWater, - 82 => ParticleData::GlowSquidInk, - 83 => ParticleData::Glow, - 84 => ParticleData::WaxOn, - 85 => ParticleData::WaxOff, - 86 => ParticleData::ElectricSpark, - 87 => ParticleData::Scrape, - _ => return Err(format!("Unknown particle id: {}", id)), - }) - } -} - -impl McBufReadable for ParticleData { - fn read_into(buf: &mut impl Read) -> Result { - let id = u32::var_read_into(buf)?; - ParticleData::read_from_particle_id(buf, id) - } -} - -impl McBufWritable for ParticleData { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - todo!() - } -} diff --git a/azalea-protocol/src/mc_buf/mod.rs b/azalea-protocol/src/mc_buf/mod.rs deleted file mode 100644 index 548ba7c2..00000000 --- a/azalea-protocol/src/mc_buf/mod.rs +++ /dev/null @@ -1,233 +0,0 @@ -//! Utilities for reading and writing for the Minecraft protocol - -mod definitions; -mod read; -mod write; - -pub use definitions::{BitSet, EntityMetadata, ParticleData, UnsizedByteArray}; -pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable}; -pub use write::{McBufVarWritable, McBufWritable, Writable}; - -// const DEFAULT_NBT_QUOTA: u32 = 2097152; -const MAX_STRING_LENGTH: u16 = 32767; -// const MAX_COMPONENT_STRING_LENGTH: u32 = 262144; - -// TODO: maybe get rid of the readable/writable traits so there's not two ways to do the same thing and improve McBufReadable/McBufWritable - -// TODO: have a definitions.rs in mc_buf that contains UnsizedByteArray and BitSet - -#[cfg(test)] -mod tests { - use super::*; - use azalea_core::resource_location::ResourceLocation; - use std::{collections::HashMap, io::Cursor}; - - #[test] - fn test_write_varint() { - let mut buf = Vec::new(); - buf.write_varint(0).unwrap(); - assert_eq!(buf, vec![0]); - - let mut buf = Vec::new(); - buf.write_varint(1).unwrap(); - assert_eq!(buf, vec![1]); - - let mut buf = Vec::new(); - buf.write_varint(2).unwrap(); - assert_eq!(buf, vec![2]); - - let mut buf = Vec::new(); - buf.write_varint(127).unwrap(); - assert_eq!(buf, vec![127]); - - let mut buf = Vec::new(); - buf.write_varint(128).unwrap(); - assert_eq!(buf, vec![128, 1]); - - let mut buf = Vec::new(); - buf.write_varint(255).unwrap(); - assert_eq!(buf, vec![255, 1]); - - let mut buf = Vec::new(); - buf.write_varint(25565).unwrap(); - assert_eq!(buf, vec![221, 199, 1]); - - let mut buf = Vec::new(); - buf.write_varint(2097151).unwrap(); - assert_eq!(buf, vec![255, 255, 127]); - - let mut buf = Vec::new(); - buf.write_varint(2147483647).unwrap(); - assert_eq!(buf, vec![255, 255, 255, 255, 7]); - - let mut buf = Vec::new(); - buf.write_varint(-1).unwrap(); - assert_eq!(buf, vec![255, 255, 255, 255, 15]); - - let mut buf = Vec::new(); - buf.write_varint(-2147483648).unwrap(); - assert_eq!(buf, vec![128, 128, 128, 128, 8]); - } - - #[test] - fn test_read_varint() { - let mut buf = Cursor::new(vec![0]); - assert_eq!(buf.read_varint().unwrap(), 0); - assert_eq!(buf.get_varint_size(0), 1); - - let mut buf = Cursor::new(vec![1]); - assert_eq!(buf.read_varint().unwrap(), 1); - assert_eq!(buf.get_varint_size(1), 1); - - let mut buf = Cursor::new(vec![2]); - assert_eq!(buf.read_varint().unwrap(), 2); - assert_eq!(buf.get_varint_size(2), 1); - - let mut buf = Cursor::new(vec![127]); - assert_eq!(buf.read_varint().unwrap(), 127); - assert_eq!(buf.get_varint_size(127), 1); - - let mut buf = Cursor::new(vec![128, 1]); - assert_eq!(buf.read_varint().unwrap(), 128); - assert_eq!(buf.get_varint_size(128), 2); - - let mut buf = Cursor::new(vec![255, 1]); - assert_eq!(buf.read_varint().unwrap(), 255); - assert_eq!(buf.get_varint_size(255), 2); - - let mut buf = Cursor::new(vec![221, 199, 1]); - assert_eq!(buf.read_varint().unwrap(), 25565); - assert_eq!(buf.get_varint_size(25565), 3); - - let mut buf = Cursor::new(vec![255, 255, 127]); - assert_eq!(buf.read_varint().unwrap(), 2097151); - assert_eq!(buf.get_varint_size(2097151), 3); - - let mut buf = Cursor::new(vec![255, 255, 255, 255, 7]); - assert_eq!(buf.read_varint().unwrap(), 2147483647); - assert_eq!(buf.get_varint_size(2147483647), 5); - - let mut buf = Cursor::new(vec![255, 255, 255, 255, 15]); - assert_eq!(buf.read_varint().unwrap(), -1); - assert_eq!(buf.get_varint_size(-1), 5); - - let mut buf = Cursor::new(vec![128, 128, 128, 128, 8]); - assert_eq!(buf.read_varint().unwrap(), -2147483648); - assert_eq!(buf.get_varint_size(-2147483648), 5); - } - - #[test] - fn test_read_varint_longer() { - let mut buf = Cursor::new(vec![138, 56, 0, 135, 56, 123]); - assert_eq!(buf.read_varint().unwrap(), 7178); - } - - #[test] - fn test_list() { - let mut buf = Vec::new(); - buf.write_list(&vec!["a", "bc", "def"], |buf, s| buf.write_utf(s)) - .unwrap(); - - // there's no read_list because idk how to do it in rust - let mut buf = Cursor::new(buf); - - let mut result = Vec::new(); - let length = buf.read_varint().unwrap(); - for _ in 0..length { - result.push(buf.read_utf().unwrap()); - } - - assert_eq!(result, vec!["a", "bc", "def"]); - } - - #[test] - fn test_int_id_list() { - let mut buf = Vec::new(); - buf.write_list(&vec![1, 2, 3], |buf, i| buf.write_varint(*i)) - .unwrap(); - - let mut buf = Cursor::new(buf); - - let result = buf.read_int_id_list().unwrap(); - assert_eq!(result, vec![1, 2, 3]); - } - - #[test] - fn test_map() { - let mut buf = Vec::new(); - buf.write_map( - vec![("a", 1), ("bc", 23), ("def", 456)], - Vec::write_utf, - Vec::write_varint, - ) - .unwrap(); - - let mut buf = Cursor::new(buf); - - let mut result = Vec::new(); - let length = buf.read_varint().unwrap(); - for _ in 0..length { - result.push((buf.read_utf().unwrap(), buf.read_varint().unwrap())); - } - - assert_eq!( - result, - vec![ - ("a".to_string(), 1), - ("bc".to_string(), 23), - ("def".to_string(), 456) - ] - ); - } - - #[test] - fn test_nbt() { - let mut buf = Vec::new(); - buf.write_nbt(&azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "hello world".to_string(), - azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "name".to_string(), - azalea_nbt::Tag::String("Bananrama".to_string()), - )])), - )]))) - .unwrap(); - - let mut buf = Cursor::new(buf); - - let result = buf.read_nbt().unwrap(); - assert_eq!( - result, - azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "hello world".to_string(), - azalea_nbt::Tag::Compound(HashMap::from_iter(vec![( - "name".to_string(), - azalea_nbt::Tag::String("Bananrama".to_string()), - )])), - )])) - ); - } - - #[test] - fn test_long() { - let mut buf = Vec::new(); - buf.write_long(123456).unwrap(); - - let mut buf = Cursor::new(buf); - - assert_eq!(buf.read_long().unwrap(), 123456); - } - - #[test] - fn test_resource_location() { - let mut buf = Vec::new(); - buf.write_resource_location(&ResourceLocation::new("minecraft:dirt").unwrap()) - .unwrap(); - - let mut buf = Cursor::new(buf); - - assert_eq!( - buf.read_resource_location().unwrap(), - ResourceLocation::new("minecraft:dirt").unwrap() - ); - } -} diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs deleted file mode 100644 index ac8d5ccb..00000000 --- a/azalea-protocol/src/mc_buf/read.rs +++ /dev/null @@ -1,525 +0,0 @@ -use super::{UnsizedByteArray, MAX_STRING_LENGTH}; -use azalea_chat::component::Component; -use azalea_core::{ - difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, - serializable_uuid::SerializableUuid, BlockPos, ChunkSectionPos, Direction, GlobalPos, Slot, - SlotData, -}; -use azalea_crypto::SaltSignaturePair; -use byteorder::{ReadBytesExt, BE}; -use serde::Deserialize; -use std::{collections::HashMap, hash::Hash, io::Read}; -use tokio::io::{AsyncRead, AsyncReadExt}; -use uuid::Uuid; - -pub trait Readable { - fn read_int_id_list(&mut self) -> Result, String>; - fn read_varint(&mut self) -> Result; - fn get_varint_size(&mut self, value: i32) -> u8; - fn get_varlong_size(&mut self, value: i32) -> u8; - fn read_byte_array(&mut self) -> Result, String>; - fn read_bytes_with_len(&mut self, n: usize) -> Result, String>; - fn read_bytes(&mut self) -> Result, String>; - fn read_utf(&mut self) -> Result; - fn read_utf_with_len(&mut self, max_length: u32) -> Result; - fn read_byte(&mut self) -> Result; - fn read_int(&mut self) -> Result; - fn read_boolean(&mut self) -> Result; - fn read_nbt(&mut self) -> Result; - fn read_long(&mut self) -> Result; - fn read_resource_location(&mut self) -> Result; - fn read_short(&mut self) -> Result; - fn read_float(&mut self) -> Result; - fn read_double(&mut self) -> Result; - fn read_uuid(&mut self) -> Result; -} - -impl Readable for R -where - R: Read, -{ - fn read_int_id_list(&mut self) -> Result, String> { - let len = self.read_varint()?; - let mut list = Vec::with_capacity(len as usize); - for _ in 0..len { - list.push(self.read_varint()?); - } - Ok(list) - } - - // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 - /// Read a single varint from the reader and return the value, along with the number of bytes read - fn read_varint(&mut self) -> Result { - let mut buffer = [0]; - let mut ans = 0; - for i in 0..5 { - self.read_exact(&mut buffer) - .map_err(|_| "Invalid VarInt".to_string())?; - ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); - if buffer[0] & 0b1000_0000 == 0 { - return Ok(ans); - } - } - Ok(ans) - } - - fn get_varint_size(&mut self, value: i32) -> u8 { - for i in 1..5 { - if (value & -1 << (i * 7)) != 0 { - continue; - } - return i; - } - 5 - } - - fn get_varlong_size(&mut self, value: i32) -> u8 { - for i in 1..10 { - if (value & -1 << (i * 7)) != 0 { - continue; - } - return i; - } - 10 - } - - fn read_byte_array(&mut self) -> Result, String> { - let length = self.read_varint()? as usize; - self.read_bytes_with_len(length) - } - - fn read_bytes_with_len(&mut self, n: usize) -> Result, String> { - let mut buffer = vec![0; n]; - self.read_exact(&mut buffer) - .map_err(|_| "Error reading bytes".to_string())?; - Ok(buffer) - } - - fn read_bytes(&mut self) -> Result, String> { - // read to end of the buffer - let mut bytes = vec![]; - self.read_to_end(&mut bytes) - .map_err(|_| "Error reading bytes".to_string())?; - Ok(bytes) - } - - fn read_utf(&mut self) -> Result { - self.read_utf_with_len(MAX_STRING_LENGTH.into()) - } - - fn read_utf_with_len(&mut self, max_length: u32) -> Result { - let length = self.read_varint()?; - // i don't know why it's multiplied by 4 but it's like that in mojang's code so - if length < 0 { - return Err( - "The received encoded string buffer length is less than zero! Weird string!" - .to_string(), - ); - } - if length as u32 > max_length * 4 { - return Err(format!( - "The received encoded string buffer length is longer than maximum allowed ({} > {})", - length, - max_length * 4 - )); - } - - // this is probably quite inefficient, idk how to do it better - let mut string = String::new(); - let mut buffer = vec![0; length as usize]; - self.read_exact(&mut buffer) - .map_err(|_| "Invalid UTF-8".to_string())?; - string.push_str(std::str::from_utf8(&buffer).unwrap()); - if string.len() > length as usize { - return Err(format!( - "The received string length is longer than maximum allowed ({} > {})", - length, max_length - )); - } - - Ok(string) - } - - /// Read a single byte from the reader - fn read_byte(&mut self) -> Result { - self.read_u8().map_err(|_| "Error reading byte".to_string()) - } - - fn read_int(&mut self) -> Result { - match self.read_i32::() { - Ok(r) => Ok(r), - Err(_) => Err("Error reading int".to_string()), - } - } - - fn read_boolean(&mut self) -> Result { - match self.read_byte()? { - 0 => Ok(false), - 1 => Ok(true), - _ => Err("Error reading boolean".to_string()), - } - } - - fn read_nbt(&mut self) -> Result { - match azalea_nbt::Tag::read(self) { - Ok(r) => Ok(r), - // Err(e) => Err(e.to_string()), - Err(e) => Err(e.to_string()).unwrap(), - } - } - - fn read_long(&mut self) -> Result { - match self.read_i64::() { - Ok(r) => Ok(r), - Err(_) => Err("Error reading long".to_string()), - } - } - - fn read_resource_location(&mut self) -> Result { - // get the resource location from the string - let location_string = self.read_utf()?; - let location = ResourceLocation::new(&location_string)?; - Ok(location) - } - - fn read_short(&mut self) -> Result { - match self.read_i16::() { - Ok(r) => Ok(r), - Err(_) => Err("Error reading short".to_string()), - } - } - - fn read_float(&mut self) -> Result { - match self.read_f32::() { - Ok(r) => Ok(r), - Err(_) => Err("Error reading float".to_string()), - } - } - - fn read_double(&mut self) -> Result { - match self.read_f64::() { - Ok(r) => Ok(r), - Err(_) => Err("Error reading double".to_string()), - } - } - - fn read_uuid(&mut self) -> Result { - Ok(Uuid::from_int_array([ - Readable::read_int(self)? as u32, - Readable::read_int(self)? as u32, - Readable::read_int(self)? as u32, - Readable::read_int(self)? as u32, - ])) - } -} - -// fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L67 -/// Read a single varint from the reader and return the value, along with the number of bytes read -pub async fn read_varint_async(reader: &mut (dyn AsyncRead + Unpin + Send)) -> Result { - let mut buffer = [0]; - let mut ans = 0; - for i in 0..5 { - reader - .read_exact(&mut buffer) - .await - .map_err(|_| "Invalid VarInt".to_string())?; - ans |= ((buffer[0] & 0b0111_1111) as i32) << (7 * i); - if buffer[0] & 0b1000_0000 == 0 { - return Ok(ans); - } - } - Ok(ans) -} - -pub trait McBufReadable -where - Self: Sized, -{ - fn read_into(buf: &mut impl Read) -> Result; -} - -pub trait McBufVarReadable -where - Self: Sized, -{ - fn var_read_into(buf: &mut impl Read) -> Result; -} - -impl McBufReadable for i32 { - fn read_into(buf: &mut impl Read) -> Result { - Readable::read_int(buf) - } -} - -impl McBufVarReadable for i32 { - fn var_read_into(buf: &mut impl Read) -> Result { - buf.read_varint() - } -} - -impl McBufVarReadable for i64 { - // fast varints modified from https://github.com/luojia65/mc-varint/blob/master/src/lib.rs#L54 - fn var_read_into(buf: &mut impl Read) -> Result { - let mut buffer = [0]; - let mut ans = 0; - for i in 0..8 { - buf.read_exact(&mut buffer) - .map_err(|_| "Invalid VarLong".to_string())?; - ans |= ((buffer[0] & 0b0111_1111) as i64) << 7 * i; - if buffer[0] & 0b1000_0000 == 0 { - break; - } - } - Ok(ans) - } -} -impl McBufVarReadable for u64 { - fn var_read_into(buf: &mut impl Read) -> Result { - i64::var_read_into(buf).map(|i| i as u64) - } -} - -impl McBufReadable for UnsizedByteArray { - fn read_into(buf: &mut impl Read) -> Result { - Ok(buf.read_bytes()?.into()) - } -} - -impl McBufReadable for Vec { - default fn read_into(buf: &mut impl Read) -> Result { - let length = buf.read_varint()? as usize; - let mut contents = Vec::with_capacity(length); - for _ in 0..length { - contents.push(T::read_into(buf)?); - } - Ok(contents) - } -} - -impl McBufReadable for HashMap { - default fn read_into(buf: &mut impl Read) -> Result { - let length = buf.read_varint()? as usize; - let mut contents = HashMap::with_capacity(length); - for _ in 0..length { - contents.insert(K::read_into(buf)?, V::read_into(buf)?); - } - Ok(contents) - } -} - -impl McBufReadable for Vec { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_byte_array() - } -} - -impl McBufReadable for String { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_utf() - } -} - -impl McBufReadable for ResourceLocation { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_resource_location() - } -} - -impl McBufReadable for u32 { - fn read_into(buf: &mut impl Read) -> Result { - Readable::read_int(buf).map(|i| i as u32) - } -} - -impl McBufVarReadable for u32 { - fn var_read_into(buf: &mut impl Read) -> Result { - buf.read_varint().map(|i| i as u32) - } -} - -impl McBufReadable for u16 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_short().map(|i| i as u16) - } -} - -impl McBufReadable for i16 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_short() - } -} - -impl McBufVarReadable for u16 { - fn var_read_into(buf: &mut impl Read) -> Result { - buf.read_varint().map(|i| i as u16) - } -} - -impl McBufVarReadable for Vec { - fn var_read_into(buf: &mut impl Read) -> Result { - let length = buf.read_varint()? as usize; - let mut contents = Vec::with_capacity(length); - for _ in 0..length { - contents.push(T::var_read_into(buf)?); - } - Ok(contents) - } -} - -impl McBufReadable for i64 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_long() - } -} - -impl McBufReadable for u64 { - fn read_into(buf: &mut impl Read) -> Result { - i64::read_into(buf).map(|i| i as u64) - } -} - -impl McBufReadable for bool { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_boolean() - } -} - -impl McBufReadable for u8 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_byte() - } -} - -impl McBufReadable for i8 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_byte().map(|i| i as i8) - } -} - -impl McBufReadable for f32 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_float() - } -} - -impl McBufReadable for f64 { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_double() - } -} - -impl McBufReadable for GameType { - fn read_into(buf: &mut impl Read) -> Result { - GameType::from_id(buf.read_byte()?) - } -} - -impl McBufReadable for Option { - fn read_into(buf: &mut impl Read) -> Result { - GameType::from_optional_id(buf.read_byte()? as i8) - } -} - -impl McBufReadable for Option { - default fn read_into(buf: &mut impl Read) -> Result { - let present = buf.read_boolean()?; - Ok(if present { - Some(T::read_into(buf)?) - } else { - None - }) - } -} - -impl McBufReadable for azalea_nbt::Tag { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_nbt() - } -} - -impl McBufReadable for Difficulty { - fn read_into(buf: &mut impl Read) -> Result { - Ok(Difficulty::by_id(u8::read_into(buf)?)) - } -} - -impl McBufReadable for Component { - fn read_into(buf: &mut impl Read) -> Result { - let string = buf.read_utf()?; - let json: serde_json::Value = serde_json::from_str(string.as_str()) - .map_err(|_| "Component isn't valid JSON".to_string())?; - let component = Component::deserialize(json).map_err(|e| e.to_string())?; - Ok(component) - } -} - -impl McBufReadable for Slot { - fn read_into(buf: &mut impl Read) -> Result { - let present = buf.read_boolean()?; - if !present { - return Ok(Slot::Empty); - } - let id = buf.read_varint()?; - let count = buf.read_byte()?; - let nbt = buf.read_nbt()?; - Ok(Slot::Present(SlotData { id, count, nbt })) - } -} - -impl McBufReadable for Uuid { - fn read_into(buf: &mut impl Read) -> Result { - buf.read_uuid() - } -} - -impl McBufReadable for BlockPos { - fn read_into(buf: &mut impl Read) -> Result { - let val = u64::read_into(buf)?; - let x = (val >> 38) as i32; - let y = (val & 0xFFF) as i32; - let z = ((val >> 12) & 0x3FFFFFF) as i32; - Ok(BlockPos { x, y, z }) - } -} - -impl McBufReadable for GlobalPos { - fn read_into(buf: &mut impl Read) -> Result { - Ok(GlobalPos { - dimension: ResourceLocation::read_into(buf)?, - pos: BlockPos::read_into(buf)?, - }) - } -} - -impl McBufReadable for Direction { - fn read_into(buf: &mut impl Read) -> Result { - match buf.read_varint()? { - 0 => Ok(Self::Down), - 1 => Ok(Self::Up), - 2 => Ok(Self::North), - 3 => Ok(Self::South), - 4 => Ok(Self::West), - 5 => Ok(Self::East), - _ => Err("Invalid direction".to_string()), - } - } -} - -impl McBufReadable for ChunkSectionPos { - fn read_into(buf: &mut impl Read) -> Result { - let long = i64::read_into(buf)?; - Ok(ChunkSectionPos { - x: (long >> 42) as i32, - y: (long << 44 >> 44) as i32, - z: (long << 22 >> 42) as i32, - }) - } -} - -impl McBufReadable for SaltSignaturePair { - fn read_into(buf: &mut impl Read) -> Result { - let salt = u64::read_into(buf)?; - let signature = Vec::::read_into(buf)?; - Ok(SaltSignaturePair { salt, signature }) - } -} diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs deleted file mode 100644 index 945477d0..00000000 --- a/azalea-protocol/src/mc_buf/write.rs +++ /dev/null @@ -1,430 +0,0 @@ -use super::{UnsizedByteArray, MAX_STRING_LENGTH}; -use azalea_chat::component::Component; -use azalea_core::{ - difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, - serializable_uuid::SerializableUuid, BlockPos, ChunkSectionPos, Direction, GlobalPos, Slot, -}; -use azalea_crypto::SaltSignaturePair; -use byteorder::{BigEndian, WriteBytesExt}; -use std::{collections::HashMap, io::Write}; -use uuid::Uuid; - -pub trait Writable: Write { - fn write_list(&mut self, list: &[T], writer: F) -> Result<(), std::io::Error> - where - F: FnOnce(&mut Self, &T) -> Result<(), std::io::Error> + Copy, - { - self.write_varint(list.len() as i32)?; - for item in list { - writer(self, item)?; - } - Ok(()) - } - - fn write_int_id_list(&mut self, list: &[i32]) -> Result<(), std::io::Error> { - self.write_list(list, |buf, n| buf.write_varint(*n)) - } - - fn write_map( - &mut self, - map: Vec<(KT, VT)>, - key_writer: KF, - value_writer: VF, - ) -> Result<(), std::io::Error> - where - KF: Fn(&mut Self, KT) -> Result<(), std::io::Error> + Copy, - VF: Fn(&mut Self, VT) -> Result<(), std::io::Error> + Copy, - { - self.write_varint(map.len() as i32)?; - for (key, value) in map { - key_writer(self, key)?; - value_writer(self, value)?; - } - Ok(()) - } - - fn write_byte(&mut self, n: u8) -> Result<(), std::io::Error> { - WriteBytesExt::write_u8(self, n) - } - - fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { - self.write_all(bytes)?; - Ok(()) - } - - fn write_varint(&mut self, mut value: i32) -> Result<(), std::io::Error> { - let mut buffer = [0]; - if value == 0 { - self.write_all(&buffer).unwrap(); - } - while value != 0 { - buffer[0] = (value & 0b0111_1111) as u8; - value = (value >> 7) & (i32::max_value() >> 6); - if value != 0 { - buffer[0] |= 0b1000_0000; - } - self.write_all(&buffer)?; - } - Ok(()) - } - - fn write_utf_with_len(&mut self, string: &str, len: usize) -> Result<(), std::io::Error> { - if string.len() > len { - panic!( - "String too big (was {} bytes encoded, max {})", - string.len(), - len - ); - } - self.write_varint(string.len() as i32)?; - self.write_bytes(string.as_bytes()) - } - - fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error> { - self.write_utf_with_len(string, MAX_STRING_LENGTH.into()) - } - - fn write_short(&mut self, n: i16) -> Result<(), std::io::Error> { - WriteBytesExt::write_i16::(self, n) - } - - fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { - self.write_varint(bytes.len() as i32)?; - self.write_bytes(bytes) - } - - fn write_int(&mut self, n: i32) -> Result<(), std::io::Error> { - WriteBytesExt::write_i32::(self, n) - } - - fn write_boolean(&mut self, b: bool) -> Result<(), std::io::Error> { - self.write_byte(if b { 1 } else { 0 }) - } - - fn write_nbt(&mut self, nbt: &azalea_nbt::Tag) -> Result<(), std::io::Error> - where - Self: Sized, - { - nbt.write(self) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) - } - - fn write_long(&mut self, n: i64) -> Result<(), std::io::Error> { - WriteBytesExt::write_i64::(self, n) - } - - fn write_float(&mut self, n: f32) -> Result<(), std::io::Error> { - WriteBytesExt::write_f32::(self, n) - } - - fn write_double(&mut self, n: f64) -> Result<(), std::io::Error> { - WriteBytesExt::write_f64::(self, n) - } - - fn write_resource_location( - &mut self, - location: &ResourceLocation, - ) -> Result<(), std::io::Error> { - self.write_utf(&location.to_string()) - } - - fn write_uuid(&mut self, uuid: &Uuid) -> Result<(), std::io::Error> - where - Self: Sized, - { - let [a, b, c, d] = uuid.to_int_array(); - a.write_into(self)?; - b.write_into(self)?; - c.write_into(self)?; - d.write_into(self)?; - Ok(()) - } -} - -impl Writable for W {} - -pub trait McBufWritable { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>; -} - -pub trait McBufVarWritable { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error>; -} - -impl McBufWritable for i32 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - Writable::write_int(buf, *self) - } -} - -impl McBufVarWritable for i32 { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_varint(*self) - } -} - -impl McBufWritable for UnsizedByteArray { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_bytes(self) - } -} - -impl McBufWritable for Vec { - default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_list(self, |buf, i| T::write_into(i, buf)) - } -} - -impl McBufWritable for HashMap { - default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - u32::var_write_into(&(self.len() as u32), buf)?; - for (key, value) in self { - key.write_into(buf)?; - value.write_into(buf)?; - } - - Ok(()) - } -} - -impl McBufWritable for Vec { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_byte_array(self) - } -} - -impl McBufWritable for String { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_utf(self) - } -} - -impl McBufWritable for ResourceLocation { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_resource_location(self) - } -} - -impl McBufWritable for u32 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i16::write_into(&(*self as i16), buf) - } -} - -impl McBufVarWritable for u32 { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i32::var_write_into(&(*self as i32), buf) - } -} - -impl McBufVarWritable for i64 { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let mut buffer = [0]; - let mut cnt = 0; - let mut value = *self; - while value != 0 { - buffer[0] = (value & 0b0111_1111) as u8; - value = (value >> 7) & (i64::max_value() >> 6); - if value != 0 { - buffer[0] |= 0b1000_0000; - } - cnt += buf.write(&mut buffer)?; - } - Ok(()) - } -} - -impl McBufVarWritable for u64 { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i64::var_write_into(&(*self as i64), buf) - } -} - -impl McBufWritable for u16 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i16::write_into(&(*self as i16), buf) - } -} - -impl McBufVarWritable for u16 { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i32::var_write_into(&(*self as i32), buf) - } -} - -impl McBufVarWritable for Vec { - fn var_write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - u32::var_write_into(&(self.len() as u32), buf)?; - for i in self { - i.var_write_into(buf)?; - } - Ok(()) - } -} - -impl McBufWritable for u8 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_byte(*self) - } -} - -impl McBufWritable for i16 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - Writable::write_short(buf, *self) - } -} - -impl McBufWritable for i64 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - Writable::write_long(buf, *self) - } -} - -impl McBufWritable for u64 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - i64::write_into(&(*self as i64), buf) - } -} - -impl McBufWritable for bool { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_boolean(*self) - } -} - -impl McBufWritable for i8 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_byte(*self as u8) - } -} - -impl McBufWritable for f32 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_float(*self) - } -} - -impl McBufWritable for f64 { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_double(*self) - } -} - -impl McBufWritable for GameType { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - u8::write_into(&self.to_id(), buf) - } -} - -impl McBufWritable for Option { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_byte(GameType::to_optional_id(self) as u8) - } -} - -impl McBufWritable for Option { - default fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - if let Some(s) = self { - buf.write_boolean(true)?; - s.write_into(buf)?; - } else { - buf.write_boolean(false)?; - }; - Ok(()) - } -} - -impl McBufWritable for azalea_nbt::Tag { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_nbt(self) - } -} - -impl McBufWritable for Difficulty { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - u8::write_into(&self.id(), buf) - } -} - -impl McBufWritable for Component { - // async fn read_into(buf: &mut impl Read) -> Result - // where - // R: AsyncRead + std::marker::Unpin + std::marker::Send, - // { - // let string = buf.read_utf().await?; - // let json: serde_json::Value = serde_json::from_str(string.as_str()) - // .map_err(|e| "Component isn't valid JSON".to_string())?; - // let component = Component::deserialize(json).map_err(|e| e.to_string())?; - // Ok(component) - // } - fn write_into(&self, _buf: &mut impl Write) -> Result<(), std::io::Error> { - // component doesn't have serialize implemented yet - todo!() - } -} - -impl McBufWritable for Slot { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - match self { - Slot::Empty => buf.write_byte(0)?, - Slot::Present(i) => { - buf.write_varint(i.id)?; - buf.write_byte(i.count)?; - buf.write_nbt(&i.nbt)?; - } - } - - Ok(()) - } -} - -impl McBufWritable for Uuid { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_uuid(self)?; - - Ok(()) - } -} - -impl McBufWritable for BlockPos { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_long( - (((self.x & 0x3FFFFFF) as i64) << 38) - | (((self.z & 0x3FFFFFF) as i64) << 12) - | ((self.y & 0xFFF) as i64), - ) - } -} - -impl McBufWritable for GlobalPos { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - ResourceLocation::write_into(&self.dimension, buf)?; - BlockPos::write_into(&self.pos, buf)?; - - Ok(()) - } -} - -impl McBufWritable for Direction { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_varint(*self as i32) - } -} - -impl McBufWritable for ChunkSectionPos { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let long = (((self.x & 0x3FFFFF) as i64) << 42) - | (self.y & 0xFFFFF) as i64 - | (((self.z & 0x3FFFFF) as i64) << 20); - long.write_into(buf)?; - Ok(()) - } -} - -impl McBufWritable for SaltSignaturePair { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - self.salt.write_into(buf)?; - self.signature.write_into(buf)?; - Ok(()) - } -} diff --git a/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs b/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs index f1947d09..4dae88a5 100644 --- a/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs @@ -1,10 +1,11 @@ use packet_macros::{GamePacket, McBuf}; use uuid::Uuid; +/// This packet is sent by the server when a player comes into visible range, not when a player joins. #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundAddPlayerPacket { #[var] - pub id: i32, + pub id: u32, pub uuid: Uuid, pub x: f64, pub y: f64, diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs index 0fc0104a..714917b7 100644 --- a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs @@ -1,11 +1,10 @@ +use azalea_core::EntityPos; use packet_macros::{GamePacket, McBuf}; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundMoveEntityPosPacket { #[var] pub entity_id: i32, - pub xa: i16, - pub ya: i16, - pub za: i16, + pub delta: PositionDelta, pub on_ground: bool, } diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs index 5fde1b93..b02a2981 100644 --- a/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs @@ -1,12 +1,12 @@ +use super::clientbound_move_entity_pos_packet::PositionDelta; use packet_macros::{GamePacket, McBuf}; +/// This packet is sent by the server when an entity moves less then 8 blocks. #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundMoveEntityPosRotPacket { #[var] - pub entity_id: i32, - pub xa: i16, - pub ya: i16, - pub za: i16, + pub entity_id: u32, + pub delta: PositionDelta, pub y_rot: i8, pub x_rot: i8, pub on_ground: bool, diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index 10beb309..6e922bb9 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -72,6 +72,22 @@ impl World { Ok(()) } + pub fn move_entity_with_delta(&mut self, entity_id: u32, delta: PositionDelta) -> Result<(), String> { + let entity = self + .entity_storage + .get_mut_by_id(entity_id) + .ok_or_else(|| "Moving entity that doesn't exist".to_string())?; + let old_chunk = ChunkPos::from(entity.pos()); + let new_chunk = ChunkPos::from(&new_pos); + // this is fine because we update the chunk below + entity.unsafe_move(new_pos); + if old_chunk != new_chunk { + self.entity_storage + .update_entity_chunk(entity_id, &old_chunk, &new_chunk); + } + Ok(()) + } + pub fn add_entity(&mut self, entity: Entity) { self.entity_storage.insert(entity); } diff --git a/bot/src/main.rs b/bot/src/main.rs index 546a9244..1cb209e5 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -6,7 +6,7 @@ async fn main() -> Result<(), Box> { println!("Hello, world!"); // let address = "95.111.249.143:10000"; - let address = "localhost:57172"; + let address = "localhost:52722"; // let response = azalea_client::ping::ping_server(&address.try_into().unwrap()) // .await // .unwrap(); -- cgit v1.2.3 From 37c6618c16319a7f40fd2e165190407472598e84 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 23 Jun 2022 19:17:04 -0500 Subject: Fix everything so azalea-buf works --- Cargo.lock | 8 ++- azalea-buf/Cargo.toml | 1 + azalea-buf/src/definitions.rs | 23 ++++++- azalea-buf/src/lib.rs | 2 + azalea-buf/src/serializable_uuid.rs | 76 ++++++++++++++++++++++ azalea-buf/src/write.rs | 4 +- azalea-client/src/client.rs | 18 ++--- azalea-core/src/delta.rs | 1 + azalea-core/src/difficulty.rs | 15 +++-- azalea-core/src/direction.rs | 2 + azalea-core/src/game_type.rs | 48 ++++++++++---- azalea-core/src/lib.rs | 12 ++-- azalea-core/src/particle/mod.rs | 3 +- azalea-core/src/position.rs | 18 ++--- azalea-core/src/resource_location.rs | 16 +++-- azalea-core/src/serializable_uuid.rs | 73 --------------------- azalea-core/src/slot.rs | 24 ++++--- azalea-entity/Cargo.toml | 8 +-- azalea-entity/src/data.rs | 46 +++++++------ azalea-entity/src/lib.rs | 37 ++--------- azalea-protocol/Cargo.toml | 1 + azalea-protocol/packet-macros/src/lib.rs | 4 +- azalea-protocol/src/lib.rs | 1 - .../packets/game/clientbound_add_entity_packet.rs | 19 +++++- .../packets/game/clientbound_add_player_packet.rs | 19 +++++- .../src/packets/game/clientbound_animate_packet.rs | 3 +- .../game/clientbound_block_changed_ack_packet.rs | 3 +- .../game/clientbound_block_update_packet.rs | 3 +- .../game/clientbound_change_difficulty_packet.rs | 5 +- .../game/clientbound_chat_preview_packet.rs | 3 +- .../clientbound_container_set_content_packet.rs | 3 +- .../game/clientbound_custom_payload_packet.rs | 7 +- .../game/clientbound_declare_commands_packet.rs | 15 +++-- .../packets/game/clientbound_disconnect_packet.rs | 3 +- .../game/clientbound_entity_event_packet.rs | 3 +- .../game/clientbound_entity_velocity_packet.rs | 3 +- .../packets/game/clientbound_game_event_packet.rs | 3 +- .../game/clientbound_initialize_border_packet.rs | 3 +- .../packets/game/clientbound_keep_alive_packet.rs | 3 +- .../clientbound_level_chunk_with_light_packet.rs | 3 +- .../packets/game/clientbound_level_event_packet.rs | 3 +- .../game/clientbound_level_particles_packet.rs | 4 +- .../game/clientbound_light_update_packet.rs | 5 +- .../src/packets/game/clientbound_login_packet.rs | 7 +- .../game/clientbound_move_entity_pos_packet.rs | 5 +- .../game/clientbound_move_entity_posrot_packet.rs | 5 +- .../game/clientbound_move_entity_rot_packet.rs | 3 +- .../game/clientbound_player_abilities_packet.rs | 5 +- .../packets/game/clientbound_player_chat_packet.rs | 3 +- .../packets/game/clientbound_player_info_packet.rs | 5 +- .../game/clientbound_player_position_packet.rs | 5 +- .../src/packets/game/clientbound_recipe_packet.rs | 7 +- .../game/clientbound_remove_entities_packet.rs | 3 +- .../packets/game/clientbound_rotate_head_packet.rs | 3 +- .../clientbound_section_blocks_update_packet.rs | 5 +- .../packets/game/clientbound_server_data_packet.rs | 3 +- .../game/clientbound_set_carried_item_packet.rs | 3 +- .../clientbound_set_chunk_cache_center_packet.rs | 3 +- ...lientbound_set_default_spawn_position_packet.rs | 3 +- .../clientbound_set_display_chat_preview_packet.rs | 3 +- .../game/clientbound_set_entity_data_packet.rs | 5 +- .../game/clientbound_set_entity_link_packet.rs | 3 +- .../game/clientbound_set_equipment_packet.rs | 5 +- .../game/clientbound_set_experience_packet.rs | 3 +- .../packets/game/clientbound_set_health_packet.rs | 3 +- .../packets/game/clientbound_set_time_packet.rs | 3 +- .../src/packets/game/clientbound_sound_packet.rs | 3 +- .../packets/game/clientbound_system_chat_packet.rs | 3 +- .../game/clientbound_teleport_entity_packet.rs | 3 +- .../game/clientbound_update_advancements_packet.rs | 6 +- .../game/clientbound_update_attributes_packet.rs | 7 +- .../game/clientbound_update_recipes_packet.rs | 11 ++-- .../packets/game/clientbound_update_tags_packet.rs | 33 +++++++--- .../clientbound_update_view_distance_packet.rs | 3 +- .../game/serverbound_chat_command_packet.rs | 3 +- .../game/serverbound_chat_preview_packet.rs | 3 +- .../game/serverbound_custom_payload_packet.rs | 7 +- .../packets/game/serverbound_keep_alive_packet.rs | 3 +- .../game/serverbound_move_player_packet_pos.rs | 3 +- .../game/serverbound_move_player_packet_pos_rot.rs | 3 +- .../game/serverbound_move_player_packet_rot.rs | 3 +- .../serverbound_move_player_packet_status_only.rs | 3 +- .../packets/handshake/client_intention_packet.rs | 3 +- .../login/clientbound_custom_query_packet.rs | 6 +- .../login/clientbound_game_profile_packet.rs | 4 +- .../src/packets/login/clientbound_hello_packet.rs | 2 +- .../login/clientbound_login_compression_packet.rs | 2 +- .../login/clientbound_login_disconnect_packet.rs | 3 +- .../src/packets/login/serverbound_hello_packet.rs | 3 +- .../src/packets/login/serverbound_key_packet.rs | 5 +- azalea-protocol/src/packets/mod.rs | 11 ++-- .../status/clientbound_status_response_packet.rs | 2 +- .../status/serverbound_status_request_packet.rs | 3 +- azalea-protocol/src/read.rs | 7 +- azalea-protocol/src/write.rs | 3 +- azalea-world/Cargo.toml | 2 +- azalea-world/src/chunk.rs | 2 +- azalea-world/src/lib.rs | 30 ++++----- azalea-world/src/palette.rs | 2 +- 99 files changed, 491 insertions(+), 343 deletions(-) create mode 100644 azalea-buf/src/serializable_uuid.rs delete mode 100755 azalea-core/src/serializable_uuid.rs (limited to 'azalea-client/src') diff --git a/Cargo.lock b/Cargo.lock index 855b65de..aeea0fb1 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -96,6 +96,7 @@ dependencies = [ "buf-macros", "byteorder", "tokio", + "uuid", ] [[package]] @@ -150,8 +151,10 @@ dependencies = [ name = "azalea-entity" version = "0.1.0" dependencies = [ + "azalea-buf", + "azalea-chat", "azalea-core", - "azalea-protocol", + "azalea-nbt", "uuid", ] @@ -188,6 +191,7 @@ dependencies = [ "azalea-chat", "azalea-core", "azalea-crypto", + "azalea-entity", "azalea-nbt", "byteorder", "bytes", @@ -209,10 +213,10 @@ name = "azalea-world" version = "0.1.0" dependencies = [ "azalea-block", + "azalea-buf", "azalea-core", "azalea-entity", "azalea-nbt", - "azalea-protocol", "log", "nohash-hasher", ] diff --git a/azalea-buf/Cargo.toml b/azalea-buf/Cargo.toml index 79f9d64d..4321dace 100644 --- a/azalea-buf/Cargo.toml +++ b/azalea-buf/Cargo.toml @@ -9,3 +9,4 @@ version = "0.1.0" buf-macros = {path = "./buf-macros"} byteorder = "1.4.3" tokio = {version = "^1.19.2", features = ["io-util", "net", "macros"]} +uuid = "1.1.2" diff --git a/azalea-buf/src/definitions.rs b/azalea-buf/src/definitions.rs index e5d8e0c0..921fb63d 100644 --- a/azalea-buf/src/definitions.rs +++ b/azalea-buf/src/definitions.rs @@ -1,5 +1,8 @@ -use buf_macros::McBuf; -use std::ops::Deref; +use crate::{McBufReadable, McBufWritable}; +use std::{ + io::{Read, Write}, + ops::Deref, +}; /// A Vec that isn't prefixed by a VarInt with the size. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -26,7 +29,7 @@ impl From<&str> for UnsizedByteArray { } /// Represents Java's BitSet, a list of bits. -#[derive(Debug, Clone, PartialEq, Eq, Hash, McBuf)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct BitSet { data: Vec, } @@ -37,3 +40,17 @@ impl BitSet { (self.data[index / 64] & (1u64 << (index % 64))) != 0 } } + +impl McBufReadable for BitSet { + fn read_into(buf: &mut impl Read) -> Result { + Ok(Self { + data: Vec::::read_into(buf)?, + }) + } +} + +impl McBufWritable for BitSet { + fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { + self.data.write_into(buf) + } +} diff --git a/azalea-buf/src/lib.rs b/azalea-buf/src/lib.rs index bd9f43be..cd72f22e 100644 --- a/azalea-buf/src/lib.rs +++ b/azalea-buf/src/lib.rs @@ -5,11 +5,13 @@ mod definitions; mod read; +mod serializable_uuid; mod write; pub use buf_macros::*; pub use definitions::*; pub use read::{read_varint_async, McBufReadable, McBufVarReadable, Readable}; +pub use serializable_uuid::*; pub use write::{McBufVarWritable, McBufWritable, Writable}; // const DEFAULT_NBT_QUOTA: u32 = 2097152; diff --git a/azalea-buf/src/serializable_uuid.rs b/azalea-buf/src/serializable_uuid.rs new file mode 100644 index 00000000..10921aa6 --- /dev/null +++ b/azalea-buf/src/serializable_uuid.rs @@ -0,0 +1,76 @@ +use crate::{McBufReadable, McBufWritable, Readable}; +use std::io::{Read, Write}; +use uuid::Uuid; + +pub trait SerializableUuid { + fn to_int_array(&self) -> [u32; 4]; + fn from_int_array(array: [u32; 4]) -> Self; +} + +fn least_most_to_int_array(most: u64, least: u64) -> [u32; 4] { + [ + (most >> 32) as u32, + most as u32, + (least >> 32) as u32, + least as u32, + ] +} + +impl SerializableUuid for Uuid { + fn to_int_array(&self) -> [u32; 4] { + let most_significant_bits = (self.as_u128() >> 64) as u64; + let least_significant_bits = (self.as_u128() & 0xffffffffffffffff) as u64; + + least_most_to_int_array(most_significant_bits, least_significant_bits) + } + + fn from_int_array(array: [u32; 4]) -> Self { + let most = ((array[0] as u64) << 32) | ((array[1] as u64) & 0xFFFFFFFF); + let least = ((array[2] as u64) << 32) | ((array[3] as u64) & 0xFFFFFFFF); + + Uuid::from_u128(((most as u128) << 64) | least as u128) + } +} + +impl McBufReadable for Uuid { + fn read_into(buf: &mut impl Read) -> Result { + Ok(Uuid::from_int_array([ + Readable::read_int(buf)? as u32, + Readable::read_int(buf)? as u32, + Readable::read_int(buf)? as u32, + Readable::read_int(buf)? 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(()) + } +} + +// TODO: add a test for Uuid in McBuf +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn to_int_array() { + let u = Uuid::parse_str("6536bfed-8695-48fd-83a1-ecd24cf2a0fd").unwrap(); + assert_eq!( + u.to_int_array(), + [0x6536bfed, 0x869548fd, 0x83a1ecd2, 0x4cf2a0fd] + ); + } + + #[test] + fn from_int_array() { + let u = Uuid::from_int_array([0x6536bfed, 0x869548fd, 0x83a1ecd2, 0x4cf2a0fd]); + assert_eq!(u.to_string(), "6536bfed-8695-48fd-83a1-ecd24cf2a0fd"); + } +} diff --git a/azalea-buf/src/write.rs b/azalea-buf/src/write.rs index fdf58203..38ddcf49 100644 --- a/azalea-buf/src/write.rs +++ b/azalea-buf/src/write.rs @@ -189,7 +189,9 @@ impl McBufVarWritable for i64 { if value != 0 { buffer[0] |= 0b1000_0000; } - buf.write(&mut buffer)?; + // this only writes a single byte, so write_all isn't necessary + // the let _ = is so clippy doesn't complain + let _ = buf.write(&mut buffer)?; } Ok(()) } diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index a5259db9..a7841637 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -1,5 +1,5 @@ use crate::{Account, Player}; -use azalea_core::{resource_location::ResourceLocation, ChunkPos, EntityPos}; +use azalea_core::{ChunkPos, EntityPos, ResourceLocation}; use azalea_entity::Entity; use azalea_protocol::{ connect::{GameConnection, HandshakeConnection}, @@ -439,14 +439,14 @@ impl Client { 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, - }, - )?; + // world.move_entity( + // p.entity_id, + // EntityPos { + // x: p.x, + // y: p.y, + // z: p.z, + // }, + // )?; } GamePacket::ClientboundMoveEntityRotPacket(p) => { println!("Got move entity rot packet {:?}", p); diff --git a/azalea-core/src/delta.rs b/azalea-core/src/delta.rs index a4d02907..339e52cd 100644 --- a/azalea-core/src/delta.rs +++ b/azalea-core/src/delta.rs @@ -1,3 +1,4 @@ +use crate::EntityPos; pub use azalea_buf::McBuf; /// Only works for up to 8 blocks diff --git a/azalea-core/src/difficulty.rs b/azalea-core/src/difficulty.rs index 7568f8e2..ebbb7708 100755 --- a/azalea-core/src/difficulty.rs +++ b/azalea-core/src/difficulty.rs @@ -1,11 +1,16 @@ -use std::fmt::{Debug, Error, Formatter}; +use std::{ + fmt::{Debug, Error, Formatter}, + io::{Read, Write}, +}; + +use azalea_buf::{McBufReadable, McBufWritable}; #[derive(Hash, Clone, Debug, PartialEq)] pub enum Difficulty { - PEACEFUL, - EASY, - NORMAL, - HARD, + PEACEFUL = 0, + EASY = 1, + NORMAL = 2, + HARD = 3, } pub enum Err { diff --git a/azalea-core/src/direction.rs b/azalea-core/src/direction.rs index bb655bdb..d3083922 100644 --- a/azalea-core/src/direction.rs +++ b/azalea-core/src/direction.rs @@ -1,3 +1,5 @@ +use azalea_buf::McBuf; + #[derive(Clone, Copy, Debug, McBuf)] pub enum Direction { Down = 0, diff --git a/azalea-core/src/game_type.rs b/azalea-core/src/game_type.rs index 963048dd..67c392b2 100755 --- a/azalea-core/src/game_type.rs +++ b/azalea-core/src/game_type.rs @@ -1,4 +1,7 @@ -#[derive(Hash, Clone, Debug)] +use azalea_buf::{McBufReadable, McBufWritable}; +use std::io::{Read, Write}; + +#[derive(Hash, Copy, Clone, Debug)] pub enum GameType { SURVIVAL, CREATIVE, @@ -17,8 +20,8 @@ impl GameType { } /// Get the id of the game type, but return -1 if the game type is invalid. - pub fn to_optional_id(game_type: &Option) -> i8 { - match game_type { + pub fn to_optional_id>>(game_type: T) -> i8 { + match game_type.into() { Some(game_type) => game_type.to_id() as i8, None => -1, } @@ -34,11 +37,12 @@ impl GameType { }) } - pub fn from_optional_id(id: i8) -> Result, String> { + pub fn from_optional_id(id: i8) -> Result { Ok(match id { -1 => None, id => Some(GameType::from_id(id as u8)?), - }) + } + .into()) } pub fn short_name(&self) -> &'static str { @@ -74,13 +78,7 @@ impl GameType { impl McBufReadable for GameType { fn read_into(buf: &mut impl Read) -> Result { - GameType::from_id(buf.read_byte()?) - } -} - -impl McBufReadable for Option { - fn read_into(buf: &mut impl Read) -> Result { - GameType::from_optional_id(buf.read_byte()? as i8) + GameType::from_id(u8::read_into(buf)?) } } @@ -90,8 +88,30 @@ impl McBufWritable for GameType { } } -impl McBufWritable for Option { +/// Rust doesn't let us `impl McBufReadable for Option` so we have to make a new type :( +#[derive(Hash, Copy, Clone, Debug)] +pub struct OptionalGameType(Option); + +impl From> for OptionalGameType { + fn from(game_type: Option) -> Self { + OptionalGameType(game_type) + } +} + +impl From for Option { + fn from(optional_game_type: OptionalGameType) -> Self { + optional_game_type.0 + } +} + +impl McBufReadable for OptionalGameType { + fn read_into(buf: &mut impl Read) -> Result { + GameType::from_optional_id(i8::read_into(buf)?) + } +} + +impl McBufWritable for OptionalGameType { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - buf.write_byte(GameType::to_optional_id(self) as u8) + GameType::to_optional_id(*self).write_into(buf) } } diff --git a/azalea-core/src/lib.rs b/azalea-core/src/lib.rs index 41c901c8..a1fa1fca 100755 --- a/azalea-core/src/lib.rs +++ b/azalea-core/src/lib.rs @@ -2,10 +2,14 @@ #![feature(int_roundings)] -pub mod difficulty; -pub mod game_type; -pub mod resource_location; -pub mod serializable_uuid; +mod difficulty; +pub use difficulty::*; + +mod resource_location; +pub use resource_location::*; + +mod game_type; +pub use game_type::*; mod slot; pub use slot::{Slot, SlotData}; diff --git a/azalea-core/src/particle/mod.rs b/azalea-core/src/particle/mod.rs index 5ca8ac8e..6eb53955 100644 --- a/azalea-core/src/particle/mod.rs +++ b/azalea-core/src/particle/mod.rs @@ -1,4 +1,5 @@ -use azalea_buf::{McBufReadable, McBufWritable}; +use crate::{BlockPos, Slot}; +use azalea_buf::{McBuf, McBufReadable, McBufVarReadable, McBufWritable}; use std::io::{Read, Write}; #[derive(Debug, Clone, McBuf)] diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index 9e7aca20..8caa5799 100644 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -1,6 +1,9 @@ -use std::ops::Rem; - -use crate::resource_location::ResourceLocation; +use crate::ResourceLocation; +use azalea_buf::{McBufReadable, McBufWritable}; +use std::{ + io::{Read, Write}, + ops::Rem, +}; #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct BlockPos { @@ -202,11 +205,10 @@ impl McBufReadable for ChunkSectionPos { 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), - ) + let data = (((self.x & 0x3FFFFFF) as i64) << 38) + | (((self.z & 0x3FFFFFF) as i64) << 12) + | ((self.y & 0xFFF) as i64); + data.write_into(buf) } } diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs index 6807714b..61ae8a20 100755 --- a/azalea-core/src/resource_location.rs +++ b/azalea-core/src/resource_location.rs @@ -1,5 +1,8 @@ //! A resource, like minecraft:stone +use azalea_buf::{McBufReadable, McBufWritable}; +use std::io::{Read, Write}; + #[derive(Hash, Clone, PartialEq, Eq)] pub struct ResourceLocation { pub namespace: String, @@ -44,18 +47,20 @@ impl std::fmt::Debug for ResourceLocation { impl McBufReadable for ResourceLocation { fn read_into(buf: &mut impl Read) -> Result { - let location_string = self.read_utf()?; + let location_string = String::read_into(buf)?; 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()) + self.to_string().write_into(buf) } } #[cfg(test)] mod tests { + use std::io::Cursor; + use super::*; #[test] @@ -86,13 +91,14 @@ mod tests { #[test] fn mcbuf_resource_location() { let mut buf = Vec::new(); - buf.write_resource_location(&ResourceLocation::new("minecraft:dirt").unwrap()) - .unwrap(); + ResourceLocation::new("minecraft:dirt") + .unwrap() + .write_into(&mut buf)?; let mut buf = Cursor::new(buf); assert_eq!( - buf.read_resource_location().unwrap(), + ResourceLocation::read_into(&mut buf).unwrap(), ResourceLocation::new("minecraft:dirt").unwrap() ); } diff --git a/azalea-core/src/serializable_uuid.rs b/azalea-core/src/serializable_uuid.rs deleted file mode 100755 index 2c7128ff..00000000 --- a/azalea-core/src/serializable_uuid.rs +++ /dev/null @@ -1,73 +0,0 @@ -use uuid::Uuid; - -pub trait SerializableUuid { - fn to_int_array(&self) -> [u32; 4]; - fn from_int_array(array: [u32; 4]) -> Self; -} - -fn least_most_to_int_array(most: u64, least: u64) -> [u32; 4] { - [ - (most >> 32) as u32, - most as u32, - (least >> 32) as u32, - least as u32, - ] -} - -impl SerializableUuid for Uuid { - fn to_int_array(&self) -> [u32; 4] { - let most_significant_bits = (self.as_u128() >> 64) as u64; - let least_significant_bits = (self.as_u128() & 0xffffffffffffffff) as u64; - - least_most_to_int_array(most_significant_bits, least_significant_bits) - } - - fn from_int_array(array: [u32; 4]) -> Self { - let most = ((array[0] as u64) << 32) | ((array[1] as u64) & 0xFFFFFFFF); - let least = ((array[2] as u64) << 32) | ((array[3] as u64) & 0xFFFFFFFF); - - Uuid::from_u128(((most as u128) << 64) | least as u128) - } -} - -impl McBufReadable for Uuid { - fn read_into(buf: &mut impl Read) -> Result { - Ok(Uuid::from_int_array([ - Readable::read_int(self)? as u32, - Readable::read_int(self)? as u32, - Readable::read_int(self)? as u32, - Readable::read_int(self)? as u32, - ])) - } -} - -impl McBufWritable for Uuid { - fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - let [a, b, c, d] = self.to_int_array(); - a.write_into(buf)?; - b.write_into(buf)?; - c.write_into(buf)?; - d.write_into(buf)?; - Ok(()) - } -} - -#[cfg(tests)] -mod tests { - use super::*; - - #[test] - fn to_int_array() { - let u = Uuid::parse_str("6536bfed-8695-48fd-83a1-ecd24cf2a0fd").unwrap(); - assert_eq!( - u.to_int_array(), - [0x6536bfed, 0x869548fd, 0x83a1ecd2, 0x4cf2a0fd] - ); - } - - #[test] - fn from_int_array() { - let u = Uuid::from_int_array([0x6536bfed, 0x869548fd, 0x83a1ecd2, 0x4cf2a0fd]); - assert_eq!(u.to_string(), "6536bfed-8695-48fd-83a1-ecd24cf2a0fd"); - } -} diff --git a/azalea-core/src/slot.rs b/azalea-core/src/slot.rs index e3b78289..6e622872 100644 --- a/azalea-core/src/slot.rs +++ b/azalea-core/src/slot.rs @@ -1,13 +1,17 @@ // TODO: have an azalea-inventory or azalea-container crate and put this there +use azalea_buf::{McBuf, McBufReadable, McBufWritable}; +use std::io::{Read, Write}; + #[derive(Debug, Clone)] pub enum Slot { - Present(SlotData), Empty, + Present(SlotData), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, McBuf)] pub struct SlotData { + #[var] pub id: i32, pub count: u8, pub nbt: azalea_nbt::Tag, @@ -15,26 +19,20 @@ pub struct SlotData { impl McBufReadable for Slot { fn read_into(buf: &mut impl Read) -> Result { - let present = buf.read_boolean()?; + let present = bool::read_into(buf)?; 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 })) + let slot = SlotData::read_into(buf)?; + Ok(Slot::Present(slot)) } } 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)?; - } + Slot::Empty => 0u8.write_into(buf)?, + Slot::Present(i) => i.write_into(buf)?, } Ok(()) diff --git a/azalea-entity/Cargo.toml b/azalea-entity/Cargo.toml index ae23267e..022a3343 100644 --- a/azalea-entity/Cargo.toml +++ b/azalea-entity/Cargo.toml @@ -6,10 +6,8 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +azalea-buf = {path = "../azalea-buf"} +azalea-chat = {path = "../azalea-chat"} azalea-core = {path = "../azalea-core"} -azalea-protocol = {path = "../azalea-protocol", optional = true} +azalea-nbt = {path = "../azalea-nbt"} uuid = "^1.1.2" - -[features] -default = ["protocol"] -protocol = ["dep:azalea-protocol"] diff --git a/azalea-entity/src/data.rs b/azalea-entity/src/data.rs index ddc6f57b..38ce671b 100644 --- a/azalea-entity/src/data.rs +++ b/azalea-entity/src/data.rs @@ -1,4 +1,12 @@ -pub type EntityMetadata = Vec; +use azalea_buf::McBufVarReadable; +use azalea_buf::{McBuf, McBufReadable, McBufWritable}; +use azalea_chat::component::Component; +use azalea_core::{BlockPos, Direction, Particle, Slot}; +use std::io::{Read, Write}; +use uuid::Uuid; + +#[derive(Clone, Debug)] +pub struct EntityMetadata(Vec); #[derive(Clone, Debug)] pub struct EntityDataItem { @@ -8,7 +16,7 @@ pub struct EntityDataItem { pub value: EntityDataValue, } -impl McBufReadable for Vec { +impl McBufReadable for EntityMetadata { fn read_into(buf: &mut impl Read) -> Result { let mut metadata = Vec::new(); loop { @@ -19,17 +27,17 @@ impl McBufReadable for Vec { let value = EntityDataValue::read_into(buf)?; metadata.push(EntityDataItem { index, value }); } - Ok(metadata) + Ok(EntityMetadata(metadata)) } } -impl McBufWritable for Vec { +impl McBufWritable for EntityMetadata { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { - for item in self { - buf.write_byte(item.index)?; + for item in &self.0 { + item.index.write_into(buf)?; item.value.write_into(buf)?; } - buf.write_byte(0xff)?; + 0xffu8.write_into(buf)?; Ok(()) } } @@ -63,20 +71,20 @@ pub enum EntityDataValue { impl McBufReadable for EntityDataValue { fn read_into(buf: &mut impl Read) -> Result { - let type_ = buf.read_varint()?; - Ok(match type_ { - 0 => EntityDataValue::Byte(buf.read_byte()?), - 1 => EntityDataValue::Int(buf.read_varint()?), - 2 => EntityDataValue::Float(buf.read_float()?), - 3 => EntityDataValue::String(buf.read_utf()?), + let data_type = i32::var_read_into(buf)?; + Ok(match data_type { + 0 => EntityDataValue::Byte(u8::read_into(buf)?), + 1 => EntityDataValue::Int(i32::read_into(buf)?), + 2 => EntityDataValue::Float(f32::read_into(buf)?), + 3 => EntityDataValue::String(String::read_into(buf)?), 4 => EntityDataValue::Component(Component::read_into(buf)?), 5 => EntityDataValue::OptionalComponent(Option::::read_into(buf)?), 6 => EntityDataValue::ItemStack(Slot::read_into(buf)?), - 7 => EntityDataValue::Boolean(buf.read_boolean()?), + 7 => EntityDataValue::Boolean(bool::read_into(buf)?), 8 => EntityDataValue::Rotations { - x: buf.read_float()?, - y: buf.read_float()?, - z: buf.read_float()?, + x: f32::read_into(buf)?, + y: f32::read_into(buf)?, + z: f32::read_into(buf)?, }, 9 => EntityDataValue::BlockPos(BlockPos::read_into(buf)?), 10 => EntityDataValue::OptionalBlockPos(Option::::read_into(buf)?), @@ -94,7 +102,7 @@ impl McBufReadable for EntityDataValue { 15 => EntityDataValue::Particle(Particle::read_into(buf)?), 16 => EntityDataValue::VillagerData(VillagerData::read_into(buf)?), 17 => EntityDataValue::OptionalUnsignedInt({ - let val = buf.read_varint()?; + let val = u32::var_read_into(buf)?; if val == 0 { None } else { @@ -102,7 +110,7 @@ impl McBufReadable for EntityDataValue { } }), 18 => EntityDataValue::Pose(Pose::read_into(buf)?), - _ => return Err(format!("Unknown entity data type: {}", type_)), + _ => return Err(format!("Unknown entity data type: {}", data_type)), }) } } diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index 2283142f..93ed5ea8 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -6,6 +6,7 @@ use azalea_protocol::packets::game::{ clientbound_add_entity_packet::ClientboundAddEntityPacket, clientbound_add_player_packet::ClientboundAddPlayerPacket, }; +pub use data::*; use uuid::Uuid; #[derive(Default, Debug)] @@ -13,10 +14,14 @@ pub struct Entity { /// The incrementing numerical id of the entity. pub id: u32, pub uuid: Uuid, - pos: EntityPos, + pub pos: EntityPos, } impl Entity { + pub fn new(id: u32, uuid: Uuid, pos: EntityPos) -> Self { + Self { id, uuid, pos } + } + pub fn pos(&self) -> &EntityPos { &self.pos } @@ -28,36 +33,6 @@ impl Entity { } } -#[cfg(feature = "protocol")] -impl From<&ClientboundAddEntityPacket> for Entity { - fn from(p: &ClientboundAddEntityPacket) -> Self { - Self { - id: p.id, - uuid: p.uuid, - pos: EntityPos { - x: p.x, - y: p.y, - z: p.z, - }, - } - } -} - -#[cfg(feature = "protocol")] -impl From<&ClientboundAddPlayerPacket> for Entity { - fn from(p: &ClientboundAddPlayerPacket) -> Self { - Self { - id: p.id, - uuid: p.uuid, - pos: EntityPos { - x: p.x, - y: p.y, - z: p.z, - }, - } - } -} - // #[cfg(test)] // mod tests { // #[test] diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml index dadb212c..280879dd 100755 --- a/azalea-protocol/Cargo.toml +++ b/azalea-protocol/Cargo.toml @@ -14,6 +14,7 @@ azalea-buf = {path = "../azalea-buf"} azalea-chat = {path = "../azalea-chat"} azalea-core = {path = "../azalea-core", optional = true} azalea-crypto = {path = "../azalea-crypto"} +azalea-entity = {path = "../azalea-entity"} azalea-nbt = {path = "../azalea-nbt"} byteorder = "^1.4.3" bytes = "^1.1.0" diff --git a/azalea-protocol/packet-macros/src/lib.rs b/azalea-protocol/packet-macros/src/lib.rs index 56b5dc52..58487cdd 100755 --- a/azalea-protocol/packet-macros/src/lib.rs +++ b/azalea-protocol/packet-macros/src/lib.rs @@ -25,13 +25,13 @@ fn as_packet_derive(input: TokenStream, state: proc_macro2::TokenStream) -> Toke } pub fn write(&self, buf: &mut impl std::io::Write) -> Result<(), std::io::Error> { - crate::mc_buf::McBufWritable::write_into(self, buf) + azalea_buf::McBufWritable::write_into(self, buf) } pub fn read( buf: &mut impl std::io::Read, ) -> Result<#state, String> { - use crate::mc_buf::McBufReadable; + use azalea_buf::McBufReadable; Ok(Self::read_into(buf)?.get()) } } diff --git a/azalea-protocol/src/lib.rs b/azalea-protocol/src/lib.rs index cadbb437..b1d1a9c4 100755 --- a/azalea-protocol/src/lib.rs +++ b/azalea-protocol/src/lib.rs @@ -5,7 +5,6 @@ use std::str::FromStr; #[cfg(feature = "connecting")] pub mod connect; -pub mod mc_buf; #[cfg(feature = "packets")] pub mod packets; pub mod read; diff --git a/azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs b/azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs index 9ef7e05c..0fc7b817 100644 --- a/azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_add_entity_packet.rs @@ -1,4 +1,7 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_core::EntityPos; +use azalea_entity::Entity; +use packet_macros::GamePacket; use uuid::Uuid; #[derive(Clone, Debug, McBuf, GamePacket)] @@ -22,3 +25,17 @@ pub struct ClientboundAddEntityPacket { pub y_vel: i16, pub z_vel: i16, } + +impl From<&ClientboundAddEntityPacket> for Entity { + fn from(p: &ClientboundAddEntityPacket) -> Self { + Self::new( + p.id, + p.uuid, + EntityPos { + x: p.x, + y: p.y, + z: p.z, + }, + ) + } +} diff --git a/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs b/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs index 4dae88a5..ddadc73f 100644 --- a/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_add_player_packet.rs @@ -1,4 +1,7 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_core::EntityPos; +use azalea_entity::Entity; +use packet_macros::GamePacket; use uuid::Uuid; /// This packet is sent by the server when a player comes into visible range, not when a player joins. @@ -13,3 +16,17 @@ pub struct ClientboundAddPlayerPacket { pub x_rot: i8, pub y_rot: i8, } + +impl From<&ClientboundAddPlayerPacket> for Entity { + fn from(p: &ClientboundAddPlayerPacket) -> Self { + Self::new( + p.id, + p.uuid, + EntityPos { + x: p.x, + y: p.y, + z: p.z, + }, + ) + } +} diff --git a/azalea-protocol/src/packets/game/clientbound_animate_packet.rs b/azalea-protocol/src/packets/game/clientbound_animate_packet.rs index 0bba1a25..e554da0a 100644 --- a/azalea-protocol/src/packets/game/clientbound_animate_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_animate_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundAnimatePacket { diff --git a/azalea-protocol/src/packets/game/clientbound_block_changed_ack_packet.rs b/azalea-protocol/src/packets/game/clientbound_block_changed_ack_packet.rs index a580440c..2901cb82 100644 --- a/azalea-protocol/src/packets/game/clientbound_block_changed_ack_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_block_changed_ack_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundBlockChangedAckPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_block_update_packet.rs b/azalea-protocol/src/packets/game/clientbound_block_update_packet.rs index f068cc7d..769270ee 100644 --- a/azalea-protocol/src/packets/game/clientbound_block_update_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_block_update_packet.rs @@ -1,5 +1,6 @@ +use azalea_buf::McBuf; use azalea_core::BlockPos; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundBlockUpdatePacket { diff --git a/azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs b/azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs index edda52d9..57cb1f69 100755 --- a/azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_change_difficulty_packet.rs @@ -1,5 +1,6 @@ -use azalea_core::difficulty::Difficulty; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_core::Difficulty; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundChangeDifficultyPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_chat_preview_packet.rs b/azalea-protocol/src/packets/game/clientbound_chat_preview_packet.rs index 58dd0722..75bf0cf7 100644 --- a/azalea-protocol/src/packets/game/clientbound_chat_preview_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_chat_preview_packet.rs @@ -1,5 +1,6 @@ +use azalea_buf::McBuf; use azalea_chat::component::Component; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundChatPreviewPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_container_set_content_packet.rs b/azalea-protocol/src/packets/game/clientbound_container_set_content_packet.rs index d38bbfda..721937a7 100644 --- a/azalea-protocol/src/packets/game/clientbound_container_set_content_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_container_set_content_packet.rs @@ -1,5 +1,6 @@ +use azalea_buf::McBuf; use azalea_core::Slot; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundContainerSetContentPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs index b9ccbba4..d01e7459 100755 --- a/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_custom_payload_packet.rs @@ -1,6 +1,7 @@ -use crate::mc_buf::UnsizedByteArray; -use azalea_core::resource_location::ResourceLocation; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_buf::UnsizedByteArray; +use azalea_core::ResourceLocation; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundCustomPayloadPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs b/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs index 648ca9e0..bf29a050 100755 --- a/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs @@ -1,8 +1,9 @@ use super::GamePacket; -use crate::mc_buf::McBufVarReadable; -use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; -use azalea_core::resource_location::ResourceLocation; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_buf::McBufVarReadable; +use azalea_buf::{McBufReadable, McBufWritable, Readable, Writable}; +use azalea_core::ResourceLocation; +use packet_macros::GamePacket; use std::{ hash::Hash, io::{Read, Write}, @@ -182,10 +183,10 @@ impl McBufReadable for BrigadierParser { 41 => Ok(BrigadierParser::Dimension), 42 => Ok(BrigadierParser::Time), 43 => Ok(BrigadierParser::ResourceOrTag { - registry_key: buf.read_resource_location()?, + registry_key: ResourceLocation::read_into(buf)?, }), 44 => Ok(BrigadierParser::Resource { - registry_key: buf.read_resource_location()?, + registry_key: ResourceLocation::read_into(buf)?, }), 45 => Ok(BrigadierParser::TemplateMirror), 46 => Ok(BrigadierParser::TemplateRotation), @@ -218,7 +219,7 @@ impl McBufReadable for BrigadierNodeStub { let _name = buf.read_utf()?; let _parser = BrigadierParser::read_into(buf)?; let _suggestions_type = if has_suggestions_type { - Some(buf.read_resource_location()?) + Some(ResourceLocation::read_into(buf)?) } else { None }; diff --git a/azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs b/azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs index c030d512..e5f35dc9 100644 --- a/azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_disconnect_packet.rs @@ -1,5 +1,6 @@ +use azalea_buf::McBuf; use azalea_chat::component::Component; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundDisconnectPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_entity_event_packet.rs b/azalea-protocol/src/packets/game/clientbound_entity_event_packet.rs index d0cc7222..a6f6e38d 100644 --- a/azalea-protocol/src/packets/game/clientbound_entity_event_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_entity_event_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; // we can't identify the status in azalea-protocol since they vary depending on the entity #[derive(Clone, Debug, McBuf, GamePacket)] diff --git a/azalea-protocol/src/packets/game/clientbound_entity_velocity_packet.rs b/azalea-protocol/src/packets/game/clientbound_entity_velocity_packet.rs index 07218c4e..7e2fa606 100644 --- a/azalea-protocol/src/packets/game/clientbound_entity_velocity_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_entity_velocity_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundEntityVelocityPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_game_event_packet.rs b/azalea-protocol/src/packets/game/clientbound_game_event_packet.rs index dd5f08f6..f5cb13a1 100644 --- a/azalea-protocol/src/packets/game/clientbound_game_event_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_game_event_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundGameEventPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_initialize_border_packet.rs b/azalea-protocol/src/packets/game/clientbound_initialize_border_packet.rs index a522eba3..f759cfc9 100644 --- a/azalea-protocol/src/packets/game/clientbound_initialize_border_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_initialize_border_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundInitializeBorderPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_keep_alive_packet.rs b/azalea-protocol/src/packets/game/clientbound_keep_alive_packet.rs index 18628c86..adaca872 100644 --- a/azalea-protocol/src/packets/game/clientbound_keep_alive_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_keep_alive_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundKeepAlivePacket { diff --git a/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs b/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs index 43bda0b6..f105bd6a 100644 --- a/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_level_chunk_with_light_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; use super::clientbound_light_update_packet::ClientboundLightUpdatePacketData; diff --git a/azalea-protocol/src/packets/game/clientbound_level_event_packet.rs b/azalea-protocol/src/packets/game/clientbound_level_event_packet.rs index 70926268..42c5f412 100644 --- a/azalea-protocol/src/packets/game/clientbound_level_event_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_level_event_packet.rs @@ -1,5 +1,6 @@ +use azalea_buf::McBuf; use azalea_core::BlockPos; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundLevelEventPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_level_particles_packet.rs b/azalea-protocol/src/packets/game/clientbound_level_particles_packet.rs index 9ed08d8a..df1174b7 100644 --- a/azalea-protocol/src/packets/game/clientbound_level_particles_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_level_particles_packet.rs @@ -1,5 +1,5 @@ -use crate::mc_buf::McBufVarReadable; -use crate::mc_buf::{McBufReadable, McBufWritable, ParticleData}; +use azalea_buf::{McBufReadable, McBufVarReadable, McBufWritable}; +use azalea_core::ParticleData; use packet_macros::GamePacket; use std::io::{Read, Write}; diff --git a/azalea-protocol/src/packets/game/clientbound_light_update_packet.rs b/azalea-protocol/src/packets/game/clientbound_light_update_packet.rs index f04987ac..adb6e33b 100644 --- a/azalea-protocol/src/packets/game/clientbound_light_update_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_light_update_packet.rs @@ -1,5 +1,6 @@ -use crate::mc_buf::BitSet; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::BitSet; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundLightUpdatePacket { diff --git a/azalea-protocol/src/packets/game/clientbound_login_packet.rs b/azalea-protocol/src/packets/game/clientbound_login_packet.rs index 6ddc6b5a..6b144bac 100755 --- a/azalea-protocol/src/packets/game/clientbound_login_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_login_packet.rs @@ -1,12 +1,13 @@ -use azalea_core::{game_type::GameType, resource_location::ResourceLocation, GlobalPos}; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_core::{GameType, GlobalPos, OptionalGameType, ResourceLocation}; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundLoginPacket { pub player_id: u32, pub hardcore: bool, pub game_type: GameType, - pub previous_game_type: Option, + pub previous_game_type: OptionalGameType, pub levels: Vec, pub registry_holder: azalea_nbt::Tag, pub dimension_type: ResourceLocation, diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs index 714917b7..3dc789a9 100644 --- a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs @@ -1,5 +1,6 @@ -use azalea_core::EntityPos; -use packet_macros::{GamePacket, McBuf}; +use azalea_core::{EntityPos, PositionDelta}; +use packet_macros::GamePacket; +use azalea_buf::McBuf; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundMoveEntityPosPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs index b02a2981..dd1d96e1 100644 --- a/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs @@ -1,5 +1,6 @@ -use super::clientbound_move_entity_pos_packet::PositionDelta; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_core::PositionDelta; +use packet_macros::GamePacket; /// This packet is sent by the server when an entity moves less then 8 blocks. #[derive(Clone, Debug, McBuf, GamePacket)] diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_rot_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_rot_packet.rs index c8d0170b..85515628 100644 --- a/azalea-protocol/src/packets/game/clientbound_move_entity_rot_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_move_entity_rot_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundMoveEntityRotPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs index c3387f7f..d351ab04 100755 --- a/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_abilities_packet.rs @@ -1,5 +1,6 @@ -use crate::mc_buf::{McBufReadable, McBufWritable, Readable}; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_buf::{McBufReadable, McBufWritable, Readable}; +use packet_macros::GamePacket; use std::io::{Read, Write}; #[derive(Clone, Debug, McBuf, GamePacket)] diff --git a/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs index e6941f25..4aac93f4 100644 --- a/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_chat_packet.rs @@ -1,6 +1,7 @@ +use azalea_buf::McBuf; use azalea_chat::component::Component; use azalea_crypto::SaltSignaturePair; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundPlayerChatPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_player_info_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_info_packet.rs index cb17f1f5..f216fde8 100644 --- a/azalea-protocol/src/packets/game/clientbound_player_info_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_info_packet.rs @@ -1,6 +1,7 @@ -use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; +use azalea_buf::McBuf; +use azalea_buf::{McBufReadable, McBufWritable, Readable, Writable}; use azalea_chat::component::Component; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; use std::io::{Read, Write}; use uuid::Uuid; diff --git a/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs index 0457269a..2c5504fa 100644 --- a/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs @@ -1,5 +1,6 @@ -use crate::mc_buf::{McBufReadable, McBufWritable, Readable}; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_buf::{McBufReadable, McBufWritable, Readable}; +use packet_macros::GamePacket; use std::io::{Read, Write}; #[derive(Clone, Debug, McBuf, GamePacket)] diff --git a/azalea-protocol/src/packets/game/clientbound_recipe_packet.rs b/azalea-protocol/src/packets/game/clientbound_recipe_packet.rs index e76504cc..a79bfcf1 100644 --- a/azalea-protocol/src/packets/game/clientbound_recipe_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_recipe_packet.rs @@ -1,6 +1,7 @@ -use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; -use azalea_core::resource_location::ResourceLocation; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_buf::{McBufReadable, McBufWritable, Readable, Writable}; +use azalea_core::ResourceLocation; +use packet_macros::GamePacket; use std::io::{Read, Write}; #[derive(Clone, Debug, McBuf, GamePacket)] diff --git a/azalea-protocol/src/packets/game/clientbound_remove_entities_packet.rs b/azalea-protocol/src/packets/game/clientbound_remove_entities_packet.rs index 8f51596d..8c76ec15 100644 --- a/azalea-protocol/src/packets/game/clientbound_remove_entities_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_remove_entities_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundRemoveEntitiesPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_rotate_head_packet.rs b/azalea-protocol/src/packets/game/clientbound_rotate_head_packet.rs index 71b485ae..dc7ec881 100644 --- a/azalea-protocol/src/packets/game/clientbound_rotate_head_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_rotate_head_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundRotateHeadPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_section_blocks_update_packet.rs b/azalea-protocol/src/packets/game/clientbound_section_blocks_update_packet.rs index 6c429edb..6ee27ed1 100644 --- a/azalea-protocol/src/packets/game/clientbound_section_blocks_update_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_section_blocks_update_packet.rs @@ -1,6 +1,7 @@ -use crate::mc_buf::{McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable}; +use azalea_buf::McBuf; +use azalea_buf::{McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable}; use azalea_core::{ChunkSectionBlockPos, ChunkSectionPos}; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; use std::io::{Read, Write}; #[derive(Clone, Debug, McBuf, GamePacket)] diff --git a/azalea-protocol/src/packets/game/clientbound_server_data_packet.rs b/azalea-protocol/src/packets/game/clientbound_server_data_packet.rs index 4c2d94e6..1dddfc1e 100644 --- a/azalea-protocol/src/packets/game/clientbound_server_data_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_server_data_packet.rs @@ -1,5 +1,6 @@ +use azalea_buf::McBuf; use azalea_chat::component::Component; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundServerDataPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs index 003b6ccc..a4ecaaab 100755 --- a/azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_carried_item_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; /// Sent to change the player's slot selection. #[derive(Clone, Debug, McBuf, GamePacket)] diff --git a/azalea-protocol/src/packets/game/clientbound_set_chunk_cache_center_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_chunk_cache_center_packet.rs index 7557c16b..ee86ec9d 100644 --- a/azalea-protocol/src/packets/game/clientbound_set_chunk_cache_center_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_chunk_cache_center_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundSetChunkCacheCenterPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_set_default_spawn_position_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_default_spawn_position_packet.rs index 7ac42c5c..9e9a7b87 100644 --- a/azalea-protocol/src/packets/game/clientbound_set_default_spawn_position_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_default_spawn_position_packet.rs @@ -1,5 +1,6 @@ +use azalea_buf::McBuf; use azalea_core::BlockPos; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundSetDefaultSpawnPositionPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_set_display_chat_preview_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_display_chat_preview_packet.rs index 46a0d582..8e2cfe70 100644 --- a/azalea-protocol/src/packets/game/clientbound_set_display_chat_preview_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_display_chat_preview_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundSetDisplayChatPreviewPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_set_entity_data_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_entity_data_packet.rs index 8a568689..cc76ddf1 100644 --- a/azalea-protocol/src/packets/game/clientbound_set_entity_data_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_entity_data_packet.rs @@ -1,5 +1,6 @@ -use crate::mc_buf::EntityMetadata; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_entity::EntityMetadata; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundSetEntityDataPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_set_entity_link_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_entity_link_packet.rs index e6e3af67..ec1ee0ec 100644 --- a/azalea-protocol/src/packets/game/clientbound_set_entity_link_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_entity_link_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundSetEntityLinkPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_set_equipment_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_equipment_packet.rs index 3acbd58f..aa352189 100644 --- a/azalea-protocol/src/packets/game/clientbound_set_equipment_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_equipment_packet.rs @@ -1,7 +1,8 @@ +use azalea_buf::McBuf; use azalea_core::Slot; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; -use crate::mc_buf::{McBufReadable, McBufWritable}; +use azalea_buf::{McBufReadable, McBufWritable}; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundSetEquipmentPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_set_experience_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_experience_packet.rs index bcb6393d..7387f6dc 100644 --- a/azalea-protocol/src/packets/game/clientbound_set_experience_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_experience_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundSetExperiencePacket { diff --git a/azalea-protocol/src/packets/game/clientbound_set_health_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_health_packet.rs index 6c75cf63..b99fe86a 100644 --- a/azalea-protocol/src/packets/game/clientbound_set_health_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_health_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundSetHealthPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_set_time_packet.rs b/azalea-protocol/src/packets/game/clientbound_set_time_packet.rs index 4cad0693..ea4437b7 100644 --- a/azalea-protocol/src/packets/game/clientbound_set_time_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_set_time_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundSetTimePacket { diff --git a/azalea-protocol/src/packets/game/clientbound_sound_packet.rs b/azalea-protocol/src/packets/game/clientbound_sound_packet.rs index fbc5830b..a8607599 100644 --- a/azalea-protocol/src/packets/game/clientbound_sound_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_sound_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundSoundPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_system_chat_packet.rs b/azalea-protocol/src/packets/game/clientbound_system_chat_packet.rs index dfa75a5b..c531fa1e 100644 --- a/azalea-protocol/src/packets/game/clientbound_system_chat_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_system_chat_packet.rs @@ -1,5 +1,6 @@ +use azalea_buf::McBuf; use azalea_chat::component::Component; -use packet_macros::{GamePacket, McBuf}; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundSystemChatPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_teleport_entity_packet.rs b/azalea-protocol/src/packets/game/clientbound_teleport_entity_packet.rs index c10db7b9..433b0727 100644 --- a/azalea-protocol/src/packets/game/clientbound_teleport_entity_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_teleport_entity_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundTeleportEntityPacket { diff --git a/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs index daa1ac93..43e4d69f 100644 --- a/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_advancements_packet.rs @@ -1,7 +1,7 @@ -use crate::packets::{McBufReadable, McBufWritable}; +use azalea_buf::{McBuf, McBufReadable, McBufWritable}; use azalea_chat::component::Component; -use azalea_core::{resource_location::ResourceLocation, Slot}; -use packet_macros::{GamePacket, McBuf}; +use azalea_core::{ResourceLocation, Slot}; +use packet_macros::GamePacket; use std::{ collections::HashMap, io::{Read, Write}, diff --git a/azalea-protocol/src/packets/game/clientbound_update_attributes_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_attributes_packet.rs index d0e7c9ee..3eca2a84 100644 --- a/azalea-protocol/src/packets/game/clientbound_update_attributes_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_attributes_packet.rs @@ -1,6 +1,7 @@ -use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; -use azalea_core::resource_location::ResourceLocation; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_buf::{McBufReadable, McBufWritable, Readable, Writable}; +use azalea_core::ResourceLocation; +use packet_macros::GamePacket; use std::io::{Read, Write}; use uuid::Uuid; diff --git a/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs index 27839919..05d57695 100644 --- a/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_recipes_packet.rs @@ -1,9 +1,10 @@ use std::io::{Read, Write}; -use azalea_core::{resource_location::ResourceLocation, Slot}; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_core::{ResourceLocation, Slot}; +use packet_macros::GamePacket; -use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; +use azalea_buf::{McBufReadable, McBufWritable, Readable, Writable}; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundUpdateRecipesPacket { @@ -129,8 +130,8 @@ impl McBufWritable for Recipe { impl McBufReadable for Recipe { fn read_into(buf: &mut impl Read) -> Result { - let recipe_type = buf.read_resource_location()?; - let identifier = buf.read_resource_location()?; + let recipe_type = ResourceLocation::read_into(buf)?; + let identifier = ResourceLocation::read_into(buf)?; // rust doesn't let us match ResourceLocation so we have to do a big // if-else chain :( diff --git a/azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs index 60794f03..cf3bc069 100755 --- a/azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_tags_packet.rs @@ -1,6 +1,8 @@ -use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; -use azalea_core::resource_location::ResourceLocation; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_buf::{McBufReadable, McBufWritable, Readable, Writable}; +use azalea_core::ResourceLocation; +use packet_macros::GamePacket; +use std::ops::Deref; use std::{ collections::HashMap, io::{Read, Write}, @@ -8,7 +10,7 @@ use std::{ #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundUpdateTagsPacket { - pub tags: HashMap>, + pub tags: TagMap, } #[derive(Clone, Debug)] @@ -17,12 +19,15 @@ pub struct Tags { pub elements: Vec, } -impl McBufReadable for HashMap> { +#[derive(Clone, Debug)] +pub struct TagMap(HashMap>); + +impl McBufReadable for TagMap { fn read_into(buf: &mut impl Read) -> Result { let length = buf.read_varint()? as usize; let mut data = HashMap::with_capacity(length); for _ in 0..length { - let tag_type = buf.read_resource_location()?; + let tag_type = ResourceLocation::read_into(buf)?; let tags_count = buf.read_varint()? as usize; let mut tags_vec = Vec::with_capacity(tags_count); for _ in 0..tags_count { @@ -31,14 +36,14 @@ impl McBufReadable for HashMap> { } data.insert(tag_type, tags_vec); } - Ok(data) + Ok(TagMap(data)) } } -impl McBufWritable for HashMap> { +impl McBufWritable for TagMap { fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> { buf.write_varint(self.len() as i32)?; - for (k, v) in self { + for (k, v) in &self.0 { k.write_into(buf)?; v.write_into(buf)?; } @@ -47,7 +52,7 @@ impl McBufWritable for HashMap> { } impl McBufReadable for Tags { fn read_into(buf: &mut impl Read) -> Result { - let name = buf.read_resource_location()?; + let name = ResourceLocation::read_into(buf)?; let elements = buf.read_int_id_list()?; Ok(Tags { name, elements }) } @@ -60,3 +65,11 @@ impl McBufWritable for Tags { Ok(()) } } + +impl Deref for TagMap { + type Target = HashMap>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs index 8288bd73..c0c40f75 100755 --- a/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_update_view_distance_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundUpdateViewDistancePacket { diff --git a/azalea-protocol/src/packets/game/serverbound_chat_command_packet.rs b/azalea-protocol/src/packets/game/serverbound_chat_command_packet.rs index 9ae0b79f..6371463b 100644 --- a/azalea-protocol/src/packets/game/serverbound_chat_command_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_chat_command_packet.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ServerboundChatCommandPacket { diff --git a/azalea-protocol/src/packets/game/serverbound_chat_preview_packet.rs b/azalea-protocol/src/packets/game/serverbound_chat_preview_packet.rs index 60535f69..32711d45 100644 --- a/azalea-protocol/src/packets/game/serverbound_chat_preview_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_chat_preview_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ServerboundChatPreviewPacket { diff --git a/azalea-protocol/src/packets/game/serverbound_custom_payload_packet.rs b/azalea-protocol/src/packets/game/serverbound_custom_payload_packet.rs index bef25b59..eecb920b 100644 --- a/azalea-protocol/src/packets/game/serverbound_custom_payload_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_custom_payload_packet.rs @@ -1,6 +1,7 @@ -use crate::mc_buf::UnsizedByteArray; -use azalea_core::resource_location::ResourceLocation; -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use azalea_buf::UnsizedByteArray; +use azalea_core::ResourceLocation; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ServerboundCustomPayloadPacket { diff --git a/azalea-protocol/src/packets/game/serverbound_keep_alive_packet.rs b/azalea-protocol/src/packets/game/serverbound_keep_alive_packet.rs index c430499e..1edc5a52 100644 --- a/azalea-protocol/src/packets/game/serverbound_keep_alive_packet.rs +++ b/azalea-protocol/src/packets/game/serverbound_keep_alive_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ServerboundKeepAlivePacket { diff --git a/azalea-protocol/src/packets/game/serverbound_move_player_packet_pos.rs b/azalea-protocol/src/packets/game/serverbound_move_player_packet_pos.rs index 65b28624..23f4050e 100644 --- a/azalea-protocol/src/packets/game/serverbound_move_player_packet_pos.rs +++ b/azalea-protocol/src/packets/game/serverbound_move_player_packet_pos.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ServerboundMovePlayerPacketPos { diff --git a/azalea-protocol/src/packets/game/serverbound_move_player_packet_pos_rot.rs b/azalea-protocol/src/packets/game/serverbound_move_player_packet_pos_rot.rs index 8070d1bf..9416461c 100644 --- a/azalea-protocol/src/packets/game/serverbound_move_player_packet_pos_rot.rs +++ b/azalea-protocol/src/packets/game/serverbound_move_player_packet_pos_rot.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ServerboundMovePlayerPacketPosRot { diff --git a/azalea-protocol/src/packets/game/serverbound_move_player_packet_rot.rs b/azalea-protocol/src/packets/game/serverbound_move_player_packet_rot.rs index 0c444d03..0952483a 100644 --- a/azalea-protocol/src/packets/game/serverbound_move_player_packet_rot.rs +++ b/azalea-protocol/src/packets/game/serverbound_move_player_packet_rot.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ServerboundMovePlayerPacketRot { diff --git a/azalea-protocol/src/packets/game/serverbound_move_player_packet_status_only.rs b/azalea-protocol/src/packets/game/serverbound_move_player_packet_status_only.rs index ef762caf..fb765c27 100644 --- a/azalea-protocol/src/packets/game/serverbound_move_player_packet_status_only.rs +++ b/azalea-protocol/src/packets/game/serverbound_move_player_packet_status_only.rs @@ -1,4 +1,5 @@ -use packet_macros::{GamePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ServerboundMovePlayerPacketStatusOnly { diff --git a/azalea-protocol/src/packets/handshake/client_intention_packet.rs b/azalea-protocol/src/packets/handshake/client_intention_packet.rs index 410c11ab..5f1987e2 100755 --- a/azalea-protocol/src/packets/handshake/client_intention_packet.rs +++ b/azalea-protocol/src/packets/handshake/client_intention_packet.rs @@ -1,5 +1,6 @@ use crate::packets::ConnectionProtocol; -use packet_macros::{HandshakePacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::HandshakePacket; use std::hash::Hash; #[derive(Hash, Clone, Debug, McBuf, HandshakePacket)] diff --git a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs index 1b1da87a..05310fb0 100755 --- a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs @@ -1,6 +1,6 @@ -use crate::mc_buf::UnsizedByteArray; -use azalea_core::resource_location::ResourceLocation; -use packet_macros::{LoginPacket, McBuf}; +use azalea_buf::{McBuf, UnsizedByteArray}; +use azalea_core::ResourceLocation; +use packet_macros::LoginPacket; use std::hash::Hash; #[derive(Hash, Clone, Debug, McBuf, LoginPacket)] diff --git a/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs b/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs index dd11f7f7..bd2edf38 100755 --- a/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs @@ -1,10 +1,8 @@ use std::io::{Read, Write}; use super::LoginPacket; -use crate::mc_buf::McBufReadable; -use crate::mc_buf::{Readable, Writable}; use azalea_auth::game_profile::GameProfile; -use azalea_core::serializable_uuid::SerializableUuid; +use azalea_buf::{McBufReadable, Readable, SerializableUuid, Writable}; use uuid::Uuid; #[derive(Clone, Debug)] diff --git a/azalea-protocol/src/packets/login/clientbound_hello_packet.rs b/azalea-protocol/src/packets/login/clientbound_hello_packet.rs index 58d48ffe..f3724c18 100755 --- a/azalea-protocol/src/packets/login/clientbound_hello_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_hello_packet.rs @@ -1,5 +1,5 @@ +use azalea_buf::McBuf; use packet_macros::LoginPacket; -use packet_macros::McBuf; #[derive(Clone, Debug, McBuf, LoginPacket)] pub struct ClientboundHelloPacket { diff --git a/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs b/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs index b1323f50..19b2d58e 100755 --- a/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs @@ -3,7 +3,7 @@ use std::{ io::{Read, Write}, }; -use crate::mc_buf::{Readable, Writable}; +use azalea_buf::{Readable, Writable}; use super::LoginPacket; diff --git a/azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs b/azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs index 9ab09e3b..acc68c82 100644 --- a/azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_login_disconnect_packet.rs @@ -1,5 +1,6 @@ +use azalea_buf::McBuf; use azalea_chat::component::Component; -use packet_macros::{LoginPacket, McBuf}; +use packet_macros::LoginPacket; #[derive(Clone, Debug, McBuf, LoginPacket)] pub struct ClientboundLoginDisconnectPacket { diff --git a/azalea-protocol/src/packets/login/serverbound_hello_packet.rs b/azalea-protocol/src/packets/login/serverbound_hello_packet.rs index bed93f04..57a3e4d8 100755 --- a/azalea-protocol/src/packets/login/serverbound_hello_packet.rs +++ b/azalea-protocol/src/packets/login/serverbound_hello_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{LoginPacket, McBuf}; +use azalea_buf::McBuf; +use packet_macros::LoginPacket; #[derive(Clone, Debug, McBuf, LoginPacket)] pub struct ServerboundHelloPacket { diff --git a/azalea-protocol/src/packets/login/serverbound_key_packet.rs b/azalea-protocol/src/packets/login/serverbound_key_packet.rs index d57b122a..12b7c4ec 100644 --- a/azalea-protocol/src/packets/login/serverbound_key_packet.rs +++ b/azalea-protocol/src/packets/login/serverbound_key_packet.rs @@ -1,8 +1,9 @@ +use azalea_buf::McBuf; use azalea_crypto::SaltSignaturePair; -use packet_macros::{LoginPacket, McBuf}; +use packet_macros::LoginPacket; use std::io::{Read, Write}; -use crate::mc_buf::{McBufReadable, McBufWritable}; +use azalea_buf::{McBufReadable, McBufWritable}; #[derive(Clone, Debug, McBuf, LoginPacket)] pub struct ServerboundKeyPacket { diff --git a/azalea-protocol/src/packets/mod.rs b/azalea-protocol/src/packets/mod.rs index 1cc79b79..738baa09 100755 --- a/azalea-protocol/src/packets/mod.rs +++ b/azalea-protocol/src/packets/mod.rs @@ -3,14 +3,11 @@ pub mod handshake; pub mod login; pub mod status; -use std::io::{Read, Write}; - -use crate::{ - connect::PacketFlow, - mc_buf::{McBufReadable, McBufWritable, Readable, Writable}, -}; +use crate::connect::PacketFlow; +use azalea_buf::{McBufReadable, McBufWritable, Readable, Writable}; use num_derive::FromPrimitive; use num_traits::FromPrimitive; +use std::io::{Read, Write}; pub const PROTOCOL_VERSION: u32 = 759; @@ -43,7 +40,7 @@ where fn write(&self, buf: &mut impl Write) -> Result<(), std::io::Error>; } -impl crate::mc_buf::McBufReadable for ConnectionProtocol { +impl azalea_buf::McBufReadable for ConnectionProtocol { fn read_into(buf: &mut impl Read) -> Result { ConnectionProtocol::from_i32(buf.read_varint()?) .ok_or_else(|| "Invalid intention".to_string()) diff --git a/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs b/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs index 2acdb8c2..e7fb4f2b 100755 --- a/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs +++ b/azalea-protocol/src/packets/status/clientbound_status_response_packet.rs @@ -4,7 +4,7 @@ use azalea_chat::component::Component; use serde::Deserialize; use serde_json::Value; -use crate::mc_buf::Readable; +use azalea_buf::Readable; use super::StatusPacket; diff --git a/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs b/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs index 3369e6a9..c19d7795 100755 --- a/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs +++ b/azalea-protocol/src/packets/status/serverbound_status_request_packet.rs @@ -1,4 +1,5 @@ -use packet_macros::{McBuf, StatusPacket}; +use azalea_buf::McBuf; +use packet_macros::StatusPacket; #[derive(Clone, Debug, McBuf, StatusPacket)] pub struct ServerboundStatusRequestPacket {} diff --git a/azalea-protocol/src/read.rs b/azalea-protocol/src/read.rs index b4ba17ea..e19b5538 100755 --- a/azalea-protocol/src/read.rs +++ b/azalea-protocol/src/read.rs @@ -1,8 +1,5 @@ -use crate::{ - connect::PacketFlow, - mc_buf::{read_varint_async, Readable}, - packets::ProtocolPacket, -}; +use crate::{connect::PacketFlow, packets::ProtocolPacket}; +use azalea_buf::{read_varint_async, Readable}; use azalea_crypto::Aes128CfbDec; use flate2::read::ZlibDecoder; use std::{ diff --git a/azalea-protocol/src/write.rs b/azalea-protocol/src/write.rs index 9291681c..ae9a6829 100755 --- a/azalea-protocol/src/write.rs +++ b/azalea-protocol/src/write.rs @@ -1,5 +1,6 @@ -use crate::{mc_buf::Writable, packets::ProtocolPacket, read::MAXIMUM_UNCOMPRESSED_LENGTH}; +use crate::{packets::ProtocolPacket, read::MAXIMUM_UNCOMPRESSED_LENGTH}; use async_compression::tokio::bufread::ZlibEncoder; +use azalea_buf::Writable; use azalea_crypto::Aes128CfbEnc; use std::fmt::Debug; use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt}; diff --git a/azalea-world/Cargo.toml b/azalea-world/Cargo.toml index 96942138..02f05b46 100644 --- a/azalea-world/Cargo.toml +++ b/azalea-world/Cargo.toml @@ -7,10 +7,10 @@ version = "0.1.0" [dependencies] azalea-block = {path = "../azalea-block"} +azalea-buf = {path = "../azalea-buf"} azalea-core = {path = "../azalea-core"} azalea-entity = {path = "../azalea-entity"} azalea-nbt = {path = "../azalea-nbt"} -azalea-protocol = {path = "../azalea-protocol"} log = "0.4.17" nohash-hasher = "0.2.0" diff --git a/azalea-world/src/chunk.rs b/azalea-world/src/chunk.rs index 77fa8786..27ef1ca7 100644 --- a/azalea-world/src/chunk.rs +++ b/azalea-world/src/chunk.rs @@ -2,8 +2,8 @@ use crate::palette::PalettedContainer; use crate::palette::PalettedContainerType; use crate::World; use azalea_block::BlockState; +use azalea_buf::{McBufReadable, McBufWritable}; use azalea_core::{BlockPos, ChunkBlockPos, ChunkPos, ChunkSectionBlockPos}; -use azalea_protocol::mc_buf::{McBufReadable, McBufWritable}; use std::fmt::Debug; use std::{ io::{Read, Write}, diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index 6e922bb9..cbc5c633 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -72,21 +72,21 @@ impl World { Ok(()) } - pub fn move_entity_with_delta(&mut self, entity_id: u32, delta: PositionDelta) -> Result<(), String> { - let entity = self - .entity_storage - .get_mut_by_id(entity_id) - .ok_or_else(|| "Moving entity that doesn't exist".to_string())?; - let old_chunk = ChunkPos::from(entity.pos()); - let new_chunk = ChunkPos::from(&new_pos); - // this is fine because we update the chunk below - entity.unsafe_move(new_pos); - if old_chunk != new_chunk { - self.entity_storage - .update_entity_chunk(entity_id, &old_chunk, &new_chunk); - } - Ok(()) - } + // pub fn move_entity_with_delta(&mut self, entity_id: u32, delta: PositionDelta) -> Result<(), String> { + // let entity = self + // .entity_storage + // .get_mut_by_id(entity_id) + // .ok_or_else(|| "Moving entity that doesn't exist".to_string())?; + // let old_chunk = ChunkPos::from(entity.pos()); + // let new_chunk = ChunkPos::from(&new_pos); + // // this is fine because we update the chunk below + // entity.unsafe_move(new_pos); + // if old_chunk != new_chunk { + // self.entity_storage + // .update_entity_chunk(entity_id, &old_chunk, &new_chunk); + // } + // Ok(()) + // } pub fn add_entity(&mut self, entity: Entity) { self.entity_storage.insert(entity); diff --git a/azalea-world/src/palette.rs b/azalea-world/src/palette.rs index be6f022c..4779dc4b 100644 --- a/azalea-world/src/palette.rs +++ b/azalea-world/src/palette.rs @@ -1,4 +1,4 @@ -use azalea_protocol::mc_buf::{McBufReadable, McBufVarReadable, McBufWritable, Readable, Writable}; +use azalea_buf::{McBufReadable, McBufVarReadable, McBufWritable, Readable, Writable}; use std::io::{Read, Write}; use crate::BitStorage; -- cgit v1.2.3 From 2cdbdcaa27c812e569c7c1a13d83182446e7f18b Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 23 Jun 2022 21:31:19 -0500 Subject: move_entity_with_delta --- azalea-client/src/client.rs | 14 +++----- azalea-core/src/delta.rs | 10 +++--- azalea-core/src/resource_location.rs | 3 +- azalea-entity/src/lib.rs | 2 +- .../game/clientbound_move_entity_pos_packet.rs | 6 ++-- azalea-world/src/lib.rs | 40 +++++++++++++--------- bot/src/main.rs | 2 +- 7 files changed, 42 insertions(+), 35 deletions(-) (limited to 'azalea-client/src') diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index a7841637..6efe521b 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -433,20 +433,16 @@ impl Client { // println!("Got rotate head packet {:?}", p); } GamePacket::ClientboundMoveEntityPosPacket(p) => { - // println!("Got move entity pos packet {:?}", p); + let mut state_lock = state.lock()?; + let world = state_lock.world.as_mut().unwrap(); + + world.move_entity_with_delta(p.entity_id, &p.delta)?; } GamePacket::ClientboundMoveEntityPosRotPacket(p) => { let mut state_lock = state.lock()?; let world = state_lock.world.as_mut().unwrap(); - // world.move_entity( - // p.entity_id, - // EntityPos { - // x: p.x, - // y: p.y, - // z: p.z, - // }, - // )?; + world.move_entity_with_delta(p.entity_id, &p.delta)?; } GamePacket::ClientboundMoveEntityRotPacket(p) => { println!("Got move entity rot packet {:?}", p); diff --git a/azalea-core/src/delta.rs b/azalea-core/src/delta.rs index 339e52cd..41923ffb 100644 --- a/azalea-core/src/delta.rs +++ b/azalea-core/src/delta.rs @@ -20,10 +20,12 @@ impl PositionDelta { } impl EntityPos { - pub fn apply_delta(&mut self, delta: &PositionDelta) { + pub fn with_delta(&self, delta: &PositionDelta) -> EntityPos { let (x, y, z) = delta.float(); - self.x += x; - self.y += y; - self.z += z; + EntityPos { + x: self.x + x, + y: self.y + y, + z: self.z + z, + } } } diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs index 61ae8a20..acca0c58 100755 --- a/azalea-core/src/resource_location.rs +++ b/azalea-core/src/resource_location.rs @@ -93,7 +93,8 @@ mod tests { let mut buf = Vec::new(); ResourceLocation::new("minecraft:dirt") .unwrap() - .write_into(&mut buf)?; + .write_into(&mut buf) + .unwrap(); let mut buf = Cursor::new(buf); diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index 93ed5ea8..065413a5 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -14,7 +14,7 @@ pub struct Entity { /// The incrementing numerical id of the entity. pub id: u32, pub uuid: Uuid, - pub pos: EntityPos, + pos: EntityPos, } impl Entity { diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs index 3dc789a9..63428dd8 100644 --- a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs @@ -1,11 +1,11 @@ -use azalea_core::{EntityPos, PositionDelta}; -use packet_macros::GamePacket; use azalea_buf::McBuf; +use azalea_core::PositionDelta; +use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundMoveEntityPosPacket { #[var] - pub entity_id: i32, + pub entity_id: u32, pub delta: PositionDelta, pub on_ground: bool, } diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index cbc5c633..bc73c13d 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -6,7 +6,7 @@ mod entity; mod palette; use azalea_block::BlockState; -use azalea_core::{BlockPos, ChunkPos, EntityPos}; +use azalea_core::{BlockPos, ChunkPos, EntityPos, PositionDelta}; use azalea_entity::Entity; pub use bit_storage::BitStorage; pub use chunk::{Chunk, ChunkStorage}; @@ -61,6 +61,7 @@ impl World { .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 @@ -72,21 +73,28 @@ impl World { Ok(()) } - // pub fn move_entity_with_delta(&mut self, entity_id: u32, delta: PositionDelta) -> Result<(), String> { - // let entity = self - // .entity_storage - // .get_mut_by_id(entity_id) - // .ok_or_else(|| "Moving entity that doesn't exist".to_string())?; - // let old_chunk = ChunkPos::from(entity.pos()); - // let new_chunk = ChunkPos::from(&new_pos); - // // this is fine because we update the chunk below - // entity.unsafe_move(new_pos); - // if old_chunk != new_chunk { - // self.entity_storage - // .update_entity_chunk(entity_id, &old_chunk, &new_chunk); - // } - // Ok(()) - // } + pub fn 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 new_pos = entity.pos().with_delta(delta); + + let old_chunk = ChunkPos::from(entity.pos()); + let new_chunk = ChunkPos::from(&new_pos); + // this is fine because we update the chunk below + + entity.unsafe_move(new_pos); + if old_chunk != new_chunk { + self.entity_storage + .update_entity_chunk(entity_id, &old_chunk, &new_chunk); + } + Ok(()) + } pub fn add_entity(&mut self, entity: Entity) { self.entity_storage.insert(entity); diff --git a/bot/src/main.rs b/bot/src/main.rs index 1cb209e5..02f802a5 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -6,7 +6,7 @@ async fn main() -> Result<(), Box> { println!("Hello, world!"); // let address = "95.111.249.143:10000"; - let address = "localhost:52722"; + let address = "localhost:49982"; // let response = azalea_client::ping::ping_server(&address.try_into().unwrap()) // .await // .unwrap(); -- cgit v1.2.3 From ce834aeca5d732a547b66554590232321e3d1829 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 23 Jun 2022 23:45:23 -0500 Subject: Fixes --- azalea-client/src/movement.rs | 2 +- azalea-crypto/src/lib.rs | 2 -- azalea-protocol/src/packets/game/mod.rs | 2 +- codegen/lib/code/packet.py | 3 ++- 4 files changed, 4 insertions(+), 5 deletions(-) (limited to 'azalea-client/src') diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index 9f5cd27c..c9cd62e9 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -1,5 +1,5 @@ -use azalea_core::EntityPos; use crate::Client; +use azalea_core::EntityPos; use azalea_protocol::packets::game::serverbound_move_player_packet_pos_rot::ServerboundMovePlayerPacketPosRot; impl Client { diff --git a/azalea-crypto/src/lib.rs b/azalea-crypto/src/lib.rs index 85705883..a5e797e8 100644 --- a/azalea-crypto/src/lib.rs +++ b/azalea-crypto/src/lib.rs @@ -79,8 +79,6 @@ pub fn decrypt_packet(cipher: &mut Aes128CfbDec, packet: &mut [u8]) { cipher.decrypt_blocks_inout_mut(chunks); } - - #[cfg(test)] mod tests { use super::*; diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 80cada9b..d3d6650c 100755 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -105,13 +105,13 @@ declare_state_packets!( 0x3c: clientbound_rotate_head_packet::ClientboundRotateHeadPacket, 0x3d: clientbound_section_blocks_update_packet::ClientboundSectionBlocksUpdatePacket, 0x3f: clientbound_server_data_packet::ClientboundServerDataPacket, - 0x44: clientbound_set_entity_link_packet::ClientboundSetEntityLinkPacket, 0x47: clientbound_set_carried_item_packet::ClientboundSetCarriedItemPacket, 0x48: clientbound_set_chunk_cache_center_packet::ClientboundSetChunkCacheCenterPacket, 0x49: clientbound_update_view_distance_packet::ClientboundUpdateViewDistancePacket, 0x4a: clientbound_set_default_spawn_position_packet::ClientboundSetDefaultSpawnPositionPacket, 0x4b: clientbound_set_display_chat_preview_packet::ClientboundSetDisplayChatPreviewPacket, 0x4d: clientbound_set_entity_data_packet::ClientboundSetEntityDataPacket, + 0x4e: clientbound_set_entity_link_packet::ClientboundSetEntityLinkPacket, 0x4f: clientbound_entity_velocity_packet::ClientboundEntityVelocityPacket, 0x50: clientbound_set_equipment_packet::ClientboundSetEquipmentPacket, 0x51: clientbound_set_experience_packet::ClientboundSetExperiencePacket, diff --git a/codegen/lib/code/packet.py b/codegen/lib/code/packet.py index c282bdb4..b4ca8be7 100644 --- a/codegen/lib/code/packet.py +++ b/codegen/lib/code/packet.py @@ -29,7 +29,8 @@ def generate_packet(burger_packets, mappings: Mappings, target_packet_id, target uses = set() generated_packet_code.append( f'#[derive(Clone, Debug, McBuf, {to_camel_case(state)}Packet)]') - uses.add(f'packet_macros::{{{to_camel_case(state)}Packet, McBuf}}') + uses.add(f'packet_macros::{to_camel_case(state)}Packet') + uses.add(f'packet_buf::McBuf') obfuscated_class_name = packet['class'].split('.')[0] class_name = mappings.get_class( -- cgit v1.2.3 From b030b0ea330c674415f7e30634957167b2fa6a6d Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 24 Jun 2022 23:10:59 -0500 Subject: start adding moving --- Cargo.lock | 2 + azalea-auth/Cargo.toml | 2 +- azalea-client/Cargo.toml | 1 + azalea-client/src/account.rs | 4 +- azalea-client/src/client.rs | 153 +++++++++++++++++--- azalea-client/src/movement.rs | 24 +++- azalea-client/src/player.rs | 24 +++- azalea-core/src/delta.rs | 59 ++++++-- azalea-core/src/position.rs | 157 ++++++++++++++------- azalea-entity/src/lib.rs | 30 +++- .../game/clientbound_move_entity_pos_packet.rs | 4 +- .../game/clientbound_move_entity_posrot_packet.rs | 4 +- azalea-world/Cargo.toml | 1 + azalea-world/src/entity.rs | 27 +++- azalea-world/src/lib.rs | 13 +- bot/src/main.rs | 52 ++++--- 16 files changed, 433 insertions(+), 124 deletions(-) (limited to 'azalea-client/src') diff --git a/Cargo.lock b/Cargo.lock index aeea0fb1..b9a60580 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,6 +122,7 @@ dependencies = [ "azalea-world", "owning_ref", "tokio", + "uuid", ] [[package]] @@ -219,6 +220,7 @@ dependencies = [ "azalea-nbt", "log", "nohash-hasher", + "uuid", ] [[package]] diff --git a/azalea-auth/Cargo.toml b/azalea-auth/Cargo.toml index a7a11b53..fe306186 100755 --- a/azalea-auth/Cargo.toml +++ b/azalea-auth/Cargo.toml @@ -6,4 +6,4 @@ version = "0.1.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -uuid = "^1.1.2" +uuid = "1.1.2" diff --git a/azalea-client/Cargo.toml b/azalea-client/Cargo.toml index 46ea8039..b39d6a49 100755 --- a/azalea-client/Cargo.toml +++ b/azalea-client/Cargo.toml @@ -14,3 +14,4 @@ azalea-protocol = {path = "../azalea-protocol"} azalea-world = {path = "../azalea-world"} owning_ref = "0.4.1" tokio = {version = "1.19.2", features = ["sync"]} +uuid = "1.1.2" diff --git a/azalea-client/src/account.rs b/azalea-client/src/account.rs index 0bcad630..56f4918a 100644 --- a/azalea-client/src/account.rs +++ b/azalea-client/src/account.rs @@ -1,8 +1,8 @@ +//! Connect to Minecraft servers. + use crate::Client; use azalea_protocol::ServerAddress; -///! Connect to Minecraft servers. - /// Something that can join Minecraft servers. pub struct Account { pub username: String, diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 6efe521b..943e0f9f 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -1,5 +1,6 @@ use crate::{Account, Player}; -use azalea_core::{ChunkPos, EntityPos, ResourceLocation}; +use azalea_auth::game_profile::GameProfile; +use azalea_core::{ChunkPos, EntityPos, PositionDelta, PositionDeltaTrait, ResourceLocation}; use azalea_entity::Entity; use azalea_protocol::{ connect::{GameConnection, HandshakeConnection}, @@ -63,6 +64,7 @@ pub enum ChatPacket { /// A player that you can control that is currently in a Minecraft server. pub struct Client { event_receiver: UnboundedReceiver, + game_profile: GameProfile, pub conn: Arc>, pub state: Arc>, // game_loop @@ -104,7 +106,7 @@ impl Client { ) .await; - let conn = loop { + let (conn, game_profile) = loop { let packet_result = conn.read().await; match packet_result { Ok(packet) => match packet { @@ -132,7 +134,7 @@ impl Client { } LoginPacket::ClientboundGameProfilePacket(p) => { println!("Got profile {:?}", p.game_profile); - break conn.game(); + break (conn.game(), p.game_profile); } LoginPacket::ClientboundLoginDisconnectPacket(p) => { println!("Got disconnect {:?}", p); @@ -154,6 +156,7 @@ impl Client { // we got the GameConnection, so the server is now connected :) let client = Client { + game_profile: game_profile.clone(), event_receiver: rx, conn: conn.clone(), state: Arc::new(Mutex::new(ClientState::default())), @@ -167,6 +170,7 @@ impl Client { conn.clone(), tx.clone(), game_loop_state.clone(), + game_profile.clone(), )); tokio::spawn(Self::game_tick_loop(conn, tx, game_loop_state)); @@ -177,21 +181,24 @@ impl Client { conn: Arc>, tx: UnboundedSender, state: Arc>, + game_profile: GameProfile, ) { loop { let r = conn.lock().await.read().await; match r { - Ok(packet) => match Self::handle(&packet, &tx, &state, &conn).await { - Ok(_) => {} - Err(e) => { - println!("Error handling packet: {:?}", e); - if IGNORE_ERRORS { - continue; - } else { - panic!("Error handling packet: {:?}", e); + Ok(packet) => { + match Self::handle(&packet, &tx, &state, &conn, &game_profile).await { + Ok(_) => {} + Err(e) => { + println!("Error handling packet: {:?}", e); + if IGNORE_ERRORS { + continue; + } else { + panic!("Error handling packet: {:?}", e); + } } } - }, + } Err(e) => { if IGNORE_ERRORS { println!("Error: {:?}", e); @@ -211,13 +218,14 @@ impl Client { tx: &UnboundedSender, state: &Arc>, conn: &Arc>, + game_profile: &GameProfile, ) -> Result<(), HandleError> { match packet { GamePacket::ClientboundLoginPacket(p) => { println!("Got login packet {:?}", p); { - let mut state = state.lock()?; + let mut state_lock = state.lock()?; // // write p into login.txt // std::io::Write::write_all( @@ -226,8 +234,6 @@ impl Client { // ) // .unwrap(); - state.player.entity.id = p.player_id; - // TODO: have registry_holder be a struct because this sucks rn // best way would be to add serde support to azalea-nbt @@ -281,7 +287,18 @@ impl Client { .as_int() .expect("min_y tag is not an int"); - state.world = Some(World::new(16, height, min_y)); + // the 16 here is our render distance + // i'll make this an actual setting later + state_lock.world = Some(World::new(16, height, min_y)); + + let entity = Entity::new(p.player_id, game_profile.uuid, EntityPos::default()); + state_lock + .world + .as_mut() + .expect("World doesn't exist! We should've gotten a login packet by now.") + .add_entity(entity); + + state_lock.player.set_entity_id(p.player_id); } conn.lock() @@ -334,6 +351,99 @@ impl Client { GamePacket::ClientboundPlayerPositionPacket(p) => { // TODO: reply with teleport confirm println!("Got player position packet {:?}", p); + + let mut state_lock = state.lock()?; + let player_entity_id = state_lock.player.entity_id; + let world = state_lock.world.as_mut().unwrap(); + let player_entity = world + .mut_entity_by_id(player_entity_id) + .expect("Player entity doesn't exist"); + let delta_movement = &player_entity.delta; + + let is_x_relative = p.relative_arguments.x; + let is_y_relative = p.relative_arguments.y; + let is_z_relative = p.relative_arguments.z; + + let (delta_x, new_pos_x) = if is_x_relative { + player_entity.old_pos.x += p.x; + (delta_movement.x(), player_entity.pos().x + p.x) + } else { + player_entity.old_pos.x = p.x; + (0.0, p.x) + }; + let (delta_y, new_pos_y) = if is_y_relative { + player_entity.old_pos.y += p.y; + (delta_movement.y(), player_entity.pos().y + p.y) + } else { + player_entity.old_pos.y = p.y; + (0.0, p.y) + }; + let (delta_z, new_pos_z) = if is_z_relative { + player_entity.old_pos.z += p.z; + (delta_movement.z(), player_entity.pos().z + p.z) + } else { + player_entity.old_pos.z = p.z; + (0.0, p.z) + }; + + let mut y_rot = p.y_rot; + let mut x_rot = p.x_rot; + if p.relative_arguments.x_rot { + y_rot += player_entity.x_rot; + } + if p.relative_arguments.y_rot { + x_rot += player_entity.y_rot; + } + + player_entity.delta = PositionDelta { + xa: delta_x, + ya: delta_y, + za: delta_z, + }; + player_entity.set_rotation(x_rot, y_rot); + // TODO: minecraft sets "xo", "yo", and "zo" here but idk what that means + // so investigate that ig + world + .move_entity( + player_entity_id, + EntityPos { + x: new_pos_x, + y: new_pos_y, + z: new_pos_z, + }, + ) + .expect("The player entity should always exist"); + + let mut state_lock = state.lock()?; + + let player = &state_lock.player; + let player_entity_id = player.entity_id; + + let world = state_lock.world.as_mut().unwrap(); + world.move_entity( + player_entity_id, + EntityPos { + x: p.x, + y: p.y, + z: p.z, + }, + )?; + + conn.lock() + .await + .write(ServerboundAcceptTeleportationPacket {}.get()) + .await; + conn.lock() + .await + .write( + ServerboundMovePlayerPacketPosRot { + identifier: ResourceLocation::new("brand").unwrap(), + // they don't have to know :) + data: "vanilla".into(), + } + .get(), + ) + .await; } GamePacket::ClientboundPlayerInfoPacket(p) => { println!("Got player info packet {:?}", p); @@ -534,13 +644,22 @@ impl Client { /// Gets the `World` the client is in. /// - /// This is basically a shortcut for `let world = client.state.lock().unwrap().world.as_ref().unwrap()`. + /// This is basically a shortcut for `client.state.lock().unwrap().world.as_ref().unwrap()`. /// If the client hasn't received a login packet yet, this will panic. pub fn world(&self) -> OwningRef, World> { let state_lock: std::sync::MutexGuard = self.state.lock().unwrap(); let state_lock_ref = OwningRef::new(state_lock); state_lock_ref.map(|state| state.world.as_ref().expect("World doesn't exist!")) } + + /// Gets the `Player` struct for our player. + /// + /// This is basically a shortcut for `client.state.lock().unwrap().player`. + pub fn player(&self) -> OwningRef, Player> { + let state_lock: std::sync::MutexGuard = self.state.lock().unwrap(); + let state_lock_ref = OwningRef::new(state_lock); + state_lock_ref.map(|state| &state.player) + } } impl From> for HandleError { diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index c9cd62e9..0402b15b 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -4,15 +4,29 @@ use azalea_protocol::packets::game::serverbound_move_player_packet_pos_rot::Serv impl Client { /// Set the client's position to the given coordinates. - pub async fn move_to(&mut self, pos: &EntityPos) { + pub async fn move_to(&mut self, new_pos: EntityPos) -> Result<(), String> { + let mut state_lock = self.state.lock().unwrap(); + + let world = state_lock.world.as_ref().unwrap(); + + let player = &state_lock.player; + let player_id = if let Some(player) = player.entity(world) { + player.id + } else { + return Err("Player entity not found".to_string()); + }; + + let world = state_lock.world.as_mut().unwrap(); + world.move_entity(player_id, new_pos)?; + self.conn .lock() .await .write( ServerboundMovePlayerPacketPosRot { - x: pos.x, - y: pos.y, - z: pos.z, + x: new_pos.x, + y: new_pos.y, + z: new_pos.z, x_rot: 0.0, y_rot: 0.0, on_ground: false, @@ -20,5 +34,7 @@ impl Client { .get(), ) .await; + + Ok(()) } } diff --git a/azalea-client/src/player.rs b/azalea-client/src/player.rs index 9a5ba8ab..ee0b9718 100644 --- a/azalea-client/src/player.rs +++ b/azalea-client/src/player.rs @@ -1,7 +1,27 @@ use azalea_entity::Entity; +use azalea_world::World; +use uuid::Uuid; #[derive(Default, Debug)] pub struct Player { - /// The entity attached to the player. There's some useful fields here. - pub entity: Entity, + /// The player's uuid. + pub uuid: Uuid, + /// The player's entity id. + pub entity_id: u32, +} + +impl Player { + /// Get the entity of the player in the world. + pub fn entity<'a>(&self, world: &'a World) -> Option<&'a Entity> { + // world.entity_by_uuid(&self.uuid) + world.entity_by_id(self.entity_id) + } + + pub fn set_uuid(&mut self, uuid: Uuid) { + self.uuid = uuid; + } + + pub fn set_entity_id(&mut self, entity_id: u32) { + self.entity_id = entity_id; + } } diff --git a/azalea-core/src/delta.rs b/azalea-core/src/delta.rs index 41923ffb..c0056411 100644 --- a/azalea-core/src/delta.rs +++ b/azalea-core/src/delta.rs @@ -1,15 +1,41 @@ use crate::EntityPos; pub use azalea_buf::McBuf; -/// Only works for up to 8 blocks -#[derive(Clone, Debug, McBuf)] +pub trait PositionDeltaTrait { + fn x(&self) -> f64; + fn y(&self) -> f64; + fn z(&self) -> f64; +} + +#[derive(Clone, Debug, McBuf, Default)] pub struct PositionDelta { - xa: i16, - ya: i16, - za: i16, + pub xa: f64, + pub ya: f64, + pub za: f64, +} + +/// Only works for up to 8 blocks +#[derive(Clone, Debug, McBuf, Default)] +pub struct PositionDelta8 { + pub xa: i16, + pub ya: i16, + pub za: i16, +} + +impl PositionDeltaTrait for PositionDelta { + fn x(&self) -> f64 { + self.xa + } + fn y(&self) -> f64 { + self.ya + } + fn z(&self) -> f64 { + self.za + } } -impl PositionDelta { +impl PositionDelta8 { + #[deprecated] pub fn float(&self) -> (f64, f64, f64) { ( (self.xa as f64) / 4096.0, @@ -19,13 +45,24 @@ impl PositionDelta { } } +impl PositionDeltaTrait for PositionDelta8 { + fn x(&self) -> f64 { + (self.xa as f64) / 4096.0 + } + fn y(&self) -> f64 { + (self.ya as f64) / 4096.0 + } + fn z(&self) -> f64 { + (self.za as f64) / 4096.0 + } +} + impl EntityPos { - pub fn with_delta(&self, delta: &PositionDelta) -> EntityPos { - let (x, y, z) = delta.float(); + pub fn with_delta(&self, delta: &dyn PositionDeltaTrait) -> EntityPos { EntityPos { - x: self.x + x, - y: self.y + y, - z: self.z + z, + x: self.x + delta.x(), + y: self.y + delta.y(), + z: self.z + delta.z(), } } } diff --git a/azalea-core/src/position.rs b/azalea-core/src/position.rs index 8caa5799..de8e2516 100644 --- a/azalea-core/src/position.rs +++ b/azalea-core/src/position.rs @@ -5,6 +5,12 @@ use std::{ ops::Rem, }; +pub trait PositionXYZ { + fn add_x(&self, n: T) -> Self; + fn add_y(&self, n: T) -> Self; + fn add_z(&self, n: T) -> Self; +} + #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct BlockPos { pub x: i32, @@ -30,6 +36,30 @@ impl Rem for BlockPos { } } +impl PositionXYZ for BlockPos { + fn add_x(&self, n: i32) -> Self { + BlockPos { + x: self.x + n, + y: self.y, + z: self.z, + } + } + fn add_y(&self, n: i32) -> Self { + BlockPos { + x: self.x, + y: self.y + n, + z: self.z, + } + } + fn add_z(&self, n: i32) -> Self { + BlockPos { + x: self.x, + y: self.y, + z: self.z + n, + } + } +} + #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)] pub struct ChunkPos { pub x: i32, @@ -42,15 +72,6 @@ impl ChunkPos { } } -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 { @@ -64,23 +85,6 @@ impl ChunkSectionPos { ChunkSectionPos { x, y, z } } } - -impl From 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 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 { @@ -94,17 +98,6 @@ impl ChunkBlockPos { 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 { @@ -122,6 +115,80 @@ impl ChunkSectionBlockPos { } } +/// A block pos with an attached dimension +#[derive(Debug, Clone)] +pub struct GlobalPos { + pub pos: BlockPos, + // this is actually a ResourceKey in Minecraft, but i don't think it matters? + pub dimension: ResourceLocation, +} + +#[derive(Debug, Clone, Copy, Default)] +pub struct EntityPos { + pub x: f64, + pub y: f64, + pub z: f64, +} + +impl PositionXYZ for EntityPos { + fn add_x(&self, n: f64) -> Self { + EntityPos { + x: self.x + n, + y: self.y, + z: self.z, + } + } + fn add_y(&self, n: f64) -> Self { + EntityPos { + x: self.x, + y: self.y + n, + z: self.z, + } + } + fn add_z(&self, n: f64) -> Self { + EntityPos { + x: self.x, + y: self.y, + z: self.z + n, + } + } +} + +impl From<&BlockPos> for ChunkPos { + fn from(pos: &BlockPos) -> Self { + ChunkPos { + x: pos.x.div_floor(16), + z: pos.z.div_floor(16), + } + } +} + +impl From 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 for ChunkPos { + fn from(pos: ChunkSectionPos) -> Self { + ChunkPos { x: pos.x, z: pos.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, + } + } +} + impl From<&BlockPos> for ChunkSectionBlockPos { fn from(pos: &BlockPos) -> Self { ChunkSectionBlockPos { @@ -141,22 +208,6 @@ impl From<&ChunkBlockPos> for ChunkSectionBlockPos { } } } - -/// A block pos with an attached dimension -#[derive(Debug, Clone)] -pub struct GlobalPos { - pub pos: BlockPos, - // this is actually a ResourceKey in Minecraft, but i don't think it matters? - pub dimension: ResourceLocation, -} - -#[derive(Debug, Clone, Default)] -pub struct EntityPos { - pub x: f64, - pub y: f64, - pub z: f64, -} - impl From<&EntityPos> for BlockPos { fn from(pos: &EntityPos) -> Self { BlockPos { diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index 065413a5..63c717d3 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -1,11 +1,6 @@ mod data; -use azalea_core::EntityPos; -#[cfg(feature = "protocol")] -use azalea_protocol::packets::game::{ - clientbound_add_entity_packet::ClientboundAddEntityPacket, - clientbound_add_player_packet::ClientboundAddPlayerPacket, -}; +use azalea_core::{EntityPos, PositionDelta}; pub use data::*; use uuid::Uuid; @@ -14,12 +9,27 @@ pub struct Entity { /// The incrementing numerical id of the entity. pub id: u32, pub uuid: Uuid, + /// The position of the entity right now. pos: EntityPos, + /// The position of the entity last tick. + pub old_pos: EntityPos, + pub delta: PositionDelta, + + pub x_rot: f32, + pub y_rot: f32, } impl Entity { pub fn new(id: u32, uuid: Uuid, pos: EntityPos) -> Self { - Self { id, uuid, pos } + Self { + id, + uuid, + pos, + old_pos: pos, + delta: PositionDelta::default(), + x_rot: 0.0, + y_rot: 0.0, + } } pub fn pos(&self) -> &EntityPos { @@ -31,6 +41,12 @@ impl Entity { pub fn unsafe_move(&mut self, new_pos: EntityPos) { self.pos = new_pos; } + + pub fn set_rotation(&mut self, x_rot: f32, y_rot: f32) { + self.x_rot = x_rot % 360.0; + self.y_rot = y_rot.clamp(-90.0, 90.0) % 360.0; + // TODO: minecraft also sets yRotO and xRotO to xRot and yRot ... but idk what they're used for so + } } // #[cfg(test)] diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs index 63428dd8..cd3e3148 100644 --- a/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_move_entity_pos_packet.rs @@ -1,11 +1,11 @@ use azalea_buf::McBuf; -use azalea_core::PositionDelta; +use azalea_core::PositionDelta8; use packet_macros::GamePacket; #[derive(Clone, Debug, McBuf, GamePacket)] pub struct ClientboundMoveEntityPosPacket { #[var] pub entity_id: u32, - pub delta: PositionDelta, + pub delta: PositionDelta8, pub on_ground: bool, } diff --git a/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs b/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs index dd1d96e1..e3422ac0 100644 --- a/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_move_entity_posrot_packet.rs @@ -1,5 +1,5 @@ use azalea_buf::McBuf; -use azalea_core::PositionDelta; +use azalea_core::PositionDelta8; use packet_macros::GamePacket; /// This packet is sent by the server when an entity moves less then 8 blocks. @@ -7,7 +7,7 @@ use packet_macros::GamePacket; pub struct ClientboundMoveEntityPosRotPacket { #[var] pub entity_id: u32, - pub delta: PositionDelta, + pub delta: PositionDelta8, pub y_rot: i8, pub x_rot: i8, pub on_ground: bool, diff --git a/azalea-world/Cargo.toml b/azalea-world/Cargo.toml index 02f05b46..ca890d82 100644 --- a/azalea-world/Cargo.toml +++ b/azalea-world/Cargo.toml @@ -13,6 +13,7 @@ azalea-entity = {path = "../azalea-entity"} azalea-nbt = {path = "../azalea-nbt"} log = "0.4.17" nohash-hasher = "0.2.0" +uuid = "1.1.2" [profile.release] lto = true diff --git a/azalea-world/src/entity.rs b/azalea-world/src/entity.rs index e4e9864f..5219e410 100644 --- a/azalea-world/src/entity.rs +++ b/azalea-world/src/entity.rs @@ -3,12 +3,13 @@ use azalea_entity::Entity; use log::warn; use nohash_hasher::{IntMap, IntSet}; use std::collections::HashMap; +use uuid::Uuid; #[derive(Debug)] pub struct EntityStorage { by_id: IntMap, - // TODO: this doesn't work yet (should be updated in the set_pos method in azalea-entity) by_chunk: HashMap>, + by_uuid: HashMap, } impl EntityStorage { @@ -16,6 +17,7 @@ impl EntityStorage { Self { by_id: IntMap::default(), by_chunk: HashMap::default(), + by_uuid: HashMap::default(), } } @@ -26,6 +28,7 @@ impl EntityStorage { .entry(ChunkPos::from(entity.pos())) .or_default() .insert(entity.id); + self.by_uuid.insert(entity.uuid, entity.id); self.by_id.insert(entity.id, entity); } @@ -34,9 +37,13 @@ impl EntityStorage { pub fn remove_by_id(&mut self, id: u32) { if let Some(entity) = self.by_id.remove(&id) { let entity_chunk = ChunkPos::from(entity.pos()); + let entity_uuid = entity.uuid; if self.by_chunk.remove(&entity_chunk).is_none() { warn!("Tried to remove entity with id {id} from chunk {entity_chunk:?} but it was not found."); } + if self.by_uuid.remove(&entity_uuid).is_none() { + warn!("Tried to remove entity with id {id} from uuid {entity_uuid:?} but it was not found."); + } } else { warn!("Tried to remove entity with id {id} but it was not found.") } @@ -54,11 +61,27 @@ impl EntityStorage { self.by_id.get_mut(&id) } + /// Get a reference to an entity by its uuid. + #[inline] + pub fn get_by_uuid(&self, uuid: &Uuid) -> Option<&Entity> { + self.by_uuid.get(uuid).and_then(|id| self.by_id.get(id)) + } + + /// Get a mutable reference to an entity by its uuid. + #[inline] + pub fn get_mut_by_uuid(&mut self, uuid: &Uuid) -> Option<&mut Entity> { + self.by_uuid.get(uuid).and_then(|id| self.by_id.get_mut(id)) + } + /// Clear all entities in a chunk. pub fn clear_chunk(&mut self, chunk: &ChunkPos) { if let Some(entities) = self.by_chunk.remove(chunk) { for entity_id in entities { - self.by_id.remove(&entity_id); + if let Some(entity) = self.by_id.remove(&entity_id) { + self.by_uuid.remove(&entity.uuid); + } else { + warn!("While clearing chunk {chunk:?}, found an entity that isn't in by_id {entity_id}."); + } } } } diff --git a/azalea-world/src/lib.rs b/azalea-world/src/lib.rs index bc73c13d..3afa4fee 100644 --- a/azalea-world/src/lib.rs +++ b/azalea-world/src/lib.rs @@ -6,7 +6,7 @@ mod entity; mod palette; use azalea_block::BlockState; -use azalea_core::{BlockPos, ChunkPos, EntityPos, PositionDelta}; +use azalea_core::{BlockPos, ChunkPos, EntityPos, PositionDelta8}; use azalea_entity::Entity; pub use bit_storage::BitStorage; pub use chunk::{Chunk, ChunkStorage}; @@ -16,6 +16,7 @@ use std::{ ops::{Index, IndexMut}, sync::{Arc, Mutex}, }; +use uuid::Uuid; #[cfg(test)] mod tests { @@ -76,7 +77,7 @@ impl World { pub fn move_entity_with_delta( &mut self, entity_id: u32, - delta: &PositionDelta, + delta: &PositionDelta8, ) -> Result<(), String> { let entity = self .entity_storage @@ -112,6 +113,14 @@ impl World { self.entity_storage.get_by_id(id) } + pub fn mut_entity_by_id(&mut self, id: u32) -> Option<&mut Entity> { + self.entity_storage.get_mut_by_id(id) + } + + pub fn entity_by_uuid(&self, uuid: &Uuid) -> Option<&Entity> { + self.entity_storage.get_by_uuid(uuid) + } + /// Get an iterator over all entities. #[inline] pub fn entities(&self) -> std::collections::hash_map::Values<'_, u32, Entity> { diff --git a/bot/src/main.rs b/bot/src/main.rs index 02f802a5..2976920b 100644 --- a/bot/src/main.rs +++ b/bot/src/main.rs @@ -1,12 +1,12 @@ use azalea_client::{Account, Event}; -use azalea_core::BlockPos; +use azalea_core::PositionXYZ; #[tokio::main] async fn main() -> Result<(), Box> { println!("Hello, world!"); // let address = "95.111.249.143:10000"; - let address = "localhost:49982"; + let address = "localhost:65399"; // let response = azalea_client::ping::ping_server(&address.try_into().unwrap()) // .await // .unwrap(); @@ -20,23 +20,37 @@ async fn main() -> Result<(), Box> { match e { // TODO: have a "loaded" or "ready" event that fires when all chunks are loaded Event::Login => {} - Event::GameTick => { - let world = client.world(); - if let Some(b) = world.find_one_entity(|e| { - e.uuid == uuid::uuid!("6536bfed-8695-48fd-83a1-ecd24cf2a0fd") - }) { - // let world = state.world.as_ref().unwrap(); - // world. - println!("{:?}", b); - } - // world.get_block_state(state.player.entity.pos); - // println!("{}", p.message.to_ansi(None)); - // if p.message.to_ansi(None) == " ok" { - // let state = client.state.lock(); - // let world = state.world.as_ref().unwrap(); - // let c = world.get_block_state(&BlockPos::new(5, 78, -2)).unwrap(); - // println!("block state: {:?}", c); - // } + // Event::GameTick => { + // let world = client.world(); + // if let Some(b) = world.find_one_entity(|e| { + // e.uuid == uuid::uuid!("6536bfed-8695-48fd-83a1-ecd24cf2a0fd") + // }) { + // // let world = state.world.as_ref().unwrap(); + // // world. + // println!("{:?}", b); + // } + // // world.get_block_state(state.player.entity.pos); + // // println!("{}", p.message.to_ansi(None)); + // // if p.message.to_ansi(None) == " ok" { + // // let state = client.state.lock(); + // // let world = state.world.as_ref().unwrap(); + // // let c = world.get_block_state(&BlockPos::new(5, 78, -2)).unwrap(); + // // println!("block state: {:?}", c); + // // } + // } + Event::Chat(msg) => { + let new_pos = { + let state_lock = client.state.lock().unwrap(); + let world = state_lock.world.as_ref().unwrap(); + let player = &state_lock.player; + let entity = player + .entity(&world) + .expect("Player entity is not in world"); + entity.pos().add_y(0.5) + }; + + println!("{:?}", new_pos); + client.move_to(new_pos).await.unwrap(); } _ => {} } -- cgit v1.2.3 From 41f61bf9c11ab58af4c1bd97c2cb40110b272449 Mon Sep 17 00:00:00 2001 From: mat Date: Fri, 24 Jun 2022 23:54:31 -0500 Subject: i hate mutexes --- azalea-client/src/client.rs | 161 ++++++++++----------- azalea-client/src/movement.rs | 5 + azalea-entity/src/lib.rs | 4 +- .../game/clientbound_player_position_packet.rs | 2 +- azalea-protocol/src/packets/game/mod.rs | 2 + .../serverbound_accept_teleportation_packet.rs | 8 + codegen/lib/code/packet.py | 2 +- 7 files changed, 97 insertions(+), 87 deletions(-) create mode 100644 azalea-protocol/src/packets/game/serverbound_accept_teleportation_packet.rs (limited to 'azalea-client/src') diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 943e0f9f..364abef6 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -8,8 +8,10 @@ use azalea_protocol::{ game::{ clientbound_player_chat_packet::ClientboundPlayerChatPacket, clientbound_system_chat_packet::ClientboundSystemChatPacket, + serverbound_accept_teleportation_packet::ServerboundAcceptTeleportationPacket, serverbound_custom_payload_packet::ServerboundCustomPayloadPacket, - serverbound_keep_alive_packet::ServerboundKeepAlivePacket, GamePacket, + serverbound_keep_alive_packet::ServerboundKeepAlivePacket, + serverbound_move_player_packet_pos_rot::ServerboundMovePlayerPacketPosRot, GamePacket, }, handshake::client_intention_packet::ClientIntentionPacket, login::{ @@ -166,6 +168,9 @@ impl Client { let game_loop_state = client.state.clone(); + // if you get an error right here that means you're doing something with locks wrong + // read the error to see where the issue is + // you might be able to just drop the lock or put it in its own scope to fix tokio::spawn(Self::protocol_loop( conn.clone(), tx.clone(), @@ -352,94 +357,84 @@ impl Client { // TODO: reply with teleport confirm println!("Got player position packet {:?}", p); - let mut state_lock = state.lock()?; - let player_entity_id = state_lock.player.entity_id; - let world = state_lock.world.as_mut().unwrap(); - let player_entity = world - .mut_entity_by_id(player_entity_id) - .expect("Player entity doesn't exist"); - let delta_movement = &player_entity.delta; - - let is_x_relative = p.relative_arguments.x; - let is_y_relative = p.relative_arguments.y; - let is_z_relative = p.relative_arguments.z; - - let (delta_x, new_pos_x) = if is_x_relative { - player_entity.old_pos.x += p.x; - (delta_movement.x(), player_entity.pos().x + p.x) - } else { - player_entity.old_pos.x = p.x; - (0.0, p.x) - }; - let (delta_y, new_pos_y) = if is_y_relative { - player_entity.old_pos.y += p.y; - (delta_movement.y(), player_entity.pos().y + p.y) - } else { - player_entity.old_pos.y = p.y; - (0.0, p.y) - }; - let (delta_z, new_pos_z) = if is_z_relative { - player_entity.old_pos.z += p.z; - (delta_movement.z(), player_entity.pos().z + p.z) - } else { - player_entity.old_pos.z = p.z; - (0.0, p.z) - }; - - let mut y_rot = p.y_rot; - let mut x_rot = p.x_rot; - if p.relative_arguments.x_rot { - y_rot += player_entity.x_rot; - } - if p.relative_arguments.y_rot { - x_rot += player_entity.y_rot; - } + let (new_pos, y_rot, x_rot) = { + let mut state_lock = state.lock()?; + let player_entity_id = state_lock.player.entity_id; + let world = state_lock.world.as_mut().unwrap(); + let player_entity = world + .mut_entity_by_id(player_entity_id) + .expect("Player entity doesn't exist"); + let delta_movement = &player_entity.delta; + + let is_x_relative = p.relative_arguments.x; + let is_y_relative = p.relative_arguments.y; + let is_z_relative = p.relative_arguments.z; + + let (delta_x, new_pos_x) = if is_x_relative { + player_entity.old_pos.x += p.x; + (delta_movement.x(), player_entity.pos().x + p.x) + } else { + player_entity.old_pos.x = p.x; + (0.0, p.x) + }; + let (delta_y, new_pos_y) = if is_y_relative { + player_entity.old_pos.y += p.y; + (delta_movement.y(), player_entity.pos().y + p.y) + } else { + player_entity.old_pos.y = p.y; + (0.0, p.y) + }; + let (delta_z, new_pos_z) = if is_z_relative { + player_entity.old_pos.z += p.z; + (delta_movement.z(), player_entity.pos().z + p.z) + } else { + player_entity.old_pos.z = p.z; + (0.0, p.z) + }; + + let mut y_rot = p.y_rot; + let mut x_rot = p.x_rot; + if p.relative_arguments.x_rot { + y_rot += player_entity.x_rot; + } + if p.relative_arguments.y_rot { + x_rot += player_entity.y_rot; + } - player_entity.delta = PositionDelta { - xa: delta_x, - ya: delta_y, - za: delta_z, + player_entity.delta = PositionDelta { + xa: delta_x, + ya: delta_y, + za: delta_z, + }; + player_entity.set_rotation(y_rot, x_rot); + // TODO: minecraft sets "xo", "yo", and "zo" here but idk what that means + // so investigate that ig + let new_pos = EntityPos { + x: new_pos_x, + y: new_pos_y, + z: new_pos_z, + }; + world + .move_entity(player_entity_id, new_pos) + .expect("The player entity should always exist"); + + (new_pos, y_rot, x_rot) }; - player_entity.set_rotation(x_rot, y_rot); - // TODO: minecraft sets "xo", "yo", and "zo" here but idk what that means - // so investigate that ig - world - .move_entity( - player_entity_id, - EntityPos { - x: new_pos_x, - y: new_pos_y, - z: new_pos_z, - }, - ) - .expect("The player entity should always exist"); - - let mut state_lock = state.lock()?; - let player = &state_lock.player; - let player_entity_id = player.entity_id; - - let world = state_lock.world.as_mut().unwrap(); - world.move_entity( - player_entity_id, - EntityPos { - x: p.x, - y: p.y, - z: p.z, - }, - )?; - - conn.lock() - .await - .write(ServerboundAcceptTeleportationPacket {}.get()) + let mut conn_lock = conn.lock().await; + conn_lock + .write(ServerboundAcceptTeleportationPacket { id: p.id }.get()) .await; - conn.lock() - .await + conn_lock .write( ServerboundMovePlayerPacketPosRot { - identifier: ResourceLocation::new("brand").unwrap(), - // they don't have to know :) - data: "vanilla".into(), + x: new_pos.x, + y: new_pos.y, + z: new_pos.z, + y_rot, + x_rot, + // this is always false + on_ground: false, } .get(), ) diff --git a/azalea-client/src/movement.rs b/azalea-client/src/movement.rs index 0402b15b..f74d48df 100644 --- a/azalea-client/src/movement.rs +++ b/azalea-client/src/movement.rs @@ -5,7 +5,9 @@ use azalea_protocol::packets::game::serverbound_move_player_packet_pos_rot::Serv impl Client { /// Set the client's position to the given coordinates. pub async fn move_to(&mut self, new_pos: EntityPos) -> Result<(), String> { + println!("obtaining lock on state"); let mut state_lock = self.state.lock().unwrap(); + println!("obtained lock on state"); let world = state_lock.world.as_ref().unwrap(); @@ -18,7 +20,9 @@ impl Client { let world = state_lock.world.as_mut().unwrap(); world.move_entity(player_id, new_pos)?; + drop(state_lock); + println!("obtaining lock on conn"); self.conn .lock() .await @@ -34,6 +38,7 @@ impl Client { .get(), ) .await; + println!("obtained lock on conn"); Ok(()) } diff --git a/azalea-entity/src/lib.rs b/azalea-entity/src/lib.rs index 63c717d3..9436d753 100644 --- a/azalea-entity/src/lib.rs +++ b/azalea-entity/src/lib.rs @@ -42,9 +42,9 @@ impl Entity { self.pos = new_pos; } - pub fn set_rotation(&mut self, x_rot: f32, y_rot: f32) { - self.x_rot = x_rot % 360.0; + pub fn set_rotation(&mut self, y_rot: f32, x_rot: f32) { self.y_rot = y_rot.clamp(-90.0, 90.0) % 360.0; + self.x_rot = x_rot % 360.0; // TODO: minecraft also sets yRotO and xRotO to xRot and yRot ... but idk what they're used for so } } diff --git a/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs index 2c5504fa..29f7c1a3 100644 --- a/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_player_position_packet.rs @@ -14,7 +14,7 @@ pub struct ClientboundPlayerPositionPacket { /// Client should confirm this packet with Teleport Confirm containing the /// same Teleport ID. #[var] - pub id: i32, + pub id: u32, pub dismount_vehicle: bool, } diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index d3d6650c..3e492dec 100755 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -49,6 +49,7 @@ pub mod clientbound_update_attributes_packet; pub mod clientbound_update_recipes_packet; pub mod clientbound_update_tags_packet; pub mod clientbound_update_view_distance_packet; +pub mod serverbound_accept_teleportation_packet; pub mod serverbound_chat_command_packet; pub mod serverbound_chat_preview_packet; pub mod serverbound_custom_payload_packet; @@ -63,6 +64,7 @@ use packet_macros::declare_state_packets; declare_state_packets!( GamePacket, Serverbound => { + 0x00: serverbound_accept_teleportation_packet::ServerboundAcceptTeleportationPacket, 0x03: serverbound_chat_command_packet::ServerboundChatCommandPacket, 0x05: serverbound_chat_preview_packet::ServerboundChatPreviewPacket, 0x0c: serverbound_custom_payload_packet::ServerboundCustomPayloadPacket, diff --git a/azalea-protocol/src/packets/game/serverbound_accept_teleportation_packet.rs b/azalea-protocol/src/packets/game/serverbound_accept_teleportation_packet.rs new file mode 100644 index 00000000..98a9f728 --- /dev/null +++ b/azalea-protocol/src/packets/game/serverbound_accept_teleportation_packet.rs @@ -0,0 +1,8 @@ +use azalea_buf::McBuf; +use packet_macros::GamePacket; + +#[derive(Clone, Debug, McBuf, GamePacket)] +pub struct ServerboundAcceptTeleportationPacket { + #[var] + pub id: u32, +} diff --git a/codegen/lib/code/packet.py b/codegen/lib/code/packet.py index b4ca8be7..de8254e7 100644 --- a/codegen/lib/code/packet.py +++ b/codegen/lib/code/packet.py @@ -30,7 +30,7 @@ def generate_packet(burger_packets, mappings: Mappings, target_packet_id, target generated_packet_code.append( f'#[derive(Clone, Debug, McBuf, {to_camel_case(state)}Packet)]') uses.add(f'packet_macros::{to_camel_case(state)}Packet') - uses.add(f'packet_buf::McBuf') + uses.add(f'azalea_buf::McBuf') obfuscated_class_name = packet['class'].split('.')[0] class_name = mappings.get_class( -- cgit v1.2.3 From a0b3b793f9f631a85f243271531640f07490c4ca Mon Sep 17 00:00:00 2001 From: mat Date: Sat, 25 Jun 2022 00:01:54 -0500 Subject: ClientboundUpdateMobEffectPacket --- azalea-client/src/client.rs | 3 +++ .../packets/game/clientbound_update_mob_effect_packet.rs | 16 ++++++++++++++++ azalea-protocol/src/packets/game/mod.rs | 2 ++ 3 files changed, 21 insertions(+) create mode 100644 azalea-protocol/src/packets/game/clientbound_update_mob_effect_packet.rs (limited to 'azalea-client/src') diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs index 364abef6..dfefe4ad 100644 --- a/azalea-client/src/client.rs +++ b/azalea-client/src/client.rs @@ -600,6 +600,9 @@ impl Client { GamePacket::ClientboundSetEquipmentPacket(p) => { println!("Got set equipment packet {:?}", p); } + GamePacket::ClientboundUpdateMobEffectPacket(p) => { + println!("Got update mob effect packet {:?}", p); + } _ => panic!("Unexpected packet {:?}", packet), } diff --git a/azalea-protocol/src/packets/game/clientbound_update_mob_effect_packet.rs b/azalea-protocol/src/packets/game/clientbound_update_mob_effect_packet.rs new file mode 100644 index 00000000..5a446c2f --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_update_mob_effect_packet.rs @@ -0,0 +1,16 @@ +use azalea_buf::McBuf; +use packet_macros::GamePacket; + +#[derive(Clone, Debug, McBuf, GamePacket)] +pub struct ClientboundUpdateMobEffectPacket { + #[var] + pub entity_id: u32, + // TODO: have an enum for this + #[var] + pub effect: u32, + pub effect_amplifier: u8, + #[var] + pub effect_duration_ticks: u32, + pub flags: u8, + pub factor_data: Option, +} diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 3e492dec..f407a697 100755 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -46,6 +46,7 @@ pub mod clientbound_system_chat_packet; pub mod clientbound_teleport_entity_packet; pub mod clientbound_update_advancements_packet; pub mod clientbound_update_attributes_packet; +pub mod clientbound_update_mob_effect_packet; pub mod clientbound_update_recipes_packet; pub mod clientbound_update_tags_packet; pub mod clientbound_update_view_distance_packet; @@ -124,6 +125,7 @@ declare_state_packets!( 0x63: clientbound_teleport_entity_packet::ClientboundTeleportEntityPacket, 0x64: clientbound_update_advancements_packet::ClientboundUpdateAdvancementsPacket, 0x65: clientbound_update_attributes_packet::ClientboundUpdateAttributesPacket, + 0x66: clientbound_update_mob_effect_packet::ClientboundUpdateMobEffectPacket, 0x67: clientbound_update_recipes_packet::ClientboundUpdateRecipesPacket, 0x68: clientbound_update_tags_packet::ClientboundUpdateTagsPacket, } -- cgit v1.2.3