From 9b50886c30f3e9129e054b019581264c9e6cadaf Mon Sep 17 00:00:00 2001 From: mat Date: Wed, 27 Apr 2022 18:00:50 +0000 Subject: [PATCH] player info packet --- azalea-client/src/connect.rs | 3 + azalea-protocol/README.md | 4 + azalea-protocol/src/mc_buf/read.rs | 57 +++++++++- azalea-protocol/src/mc_buf/write.rs | 49 ++++++++- .../clientbound_declare_commands_packet.rs | 4 +- .../game/clientbound_entity_event_packet.rs | 1 - .../game/clientbound_player_info_packet.rs | 101 ++++++++++++++++++ azalea-protocol/src/packets/game/mod.rs | 2 + .../login/clientbound_game_profile_packet.rs | 2 + 9 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 azalea-protocol/src/packets/game/clientbound_player_info_packet.rs diff --git a/azalea-client/src/connect.rs b/azalea-client/src/connect.rs index 78370d7e..f5909f9c 100755 --- a/azalea-client/src/connect.rs +++ b/azalea-client/src/connect.rs @@ -127,6 +127,9 @@ pub async fn join_server(address: &ServerAddress) -> Result<(), String> { // TODO: reply with teleport confirm println!("Got player position packet {:?}", p); } + GamePacket::ClientboundPlayerInfoPacket(p) => { + println!("Got player info packet {:?}", p); + } }, Err(e) => { panic!("Error: {:?}", e); diff --git a/azalea-protocol/README.md b/azalea-protocol/README.md index 90ea72e5..69b28bef 100755 --- a/azalea-protocol/README.md +++ b/azalea-protocol/README.md @@ -1,3 +1,7 @@ # Azalea Protocol Sent and receive Minecraft packets. You should probably use `azalea` or `azalea-client` instead. + +The goal is to **only** support the latest Minecraft version in order to ease development. + +This is not yet complete, search for `TODO` in the code for things that need to be done. diff --git a/azalea-protocol/src/mc_buf/read.rs b/azalea-protocol/src/mc_buf/read.rs index d3382e0a..e90b4b10 100755 --- a/azalea-protocol/src/mc_buf/read.rs +++ b/azalea-protocol/src/mc_buf/read.rs @@ -1,11 +1,12 @@ use async_trait::async_trait; use azalea_chat::component::Component; use azalea_core::{ - difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, Slot, - SlotData, + difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, + serializable_uuid::SerializableUuid, Slot, SlotData, }; use serde::Deserialize; use tokio::io::{AsyncRead, AsyncReadExt}; +use uuid::Uuid; use super::{UnsizedByteArray, MAX_STRING_LENGTH}; @@ -29,6 +30,7 @@ pub trait Readable { async fn read_short(&mut self) -> Result; async fn read_float(&mut self) -> Result; async fn read_double(&mut self) -> Result; + async fn read_uuid(&mut self) -> Result; } #[async_trait] @@ -207,6 +209,15 @@ where Err(_) => Err("Error reading double".to_string()), } } + + async fn read_uuid(&mut self) -> Result { + Ok(Uuid::from_int_array([ + self.read_int().await? as u32, + self.read_int().await? as u32, + self.read_int().await? as u32, + self.read_int().await? as u32, + ])) + } } #[async_trait] @@ -439,6 +450,37 @@ impl McBufReadable for Option { } } +// Option +#[async_trait] +impl McBufReadable for Option { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + let present = buf.read_boolean().await?; + Ok(if present { + Some(buf.read_utf().await?) + } else { + None + }) + } +} +// Option +#[async_trait] +impl McBufReadable for Option { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + let present = buf.read_boolean().await?; + Ok(if present { + Some(Component::read_into(buf).await?) + } else { + None + }) + } +} + // azalea_nbt::Tag #[async_trait] impl McBufReadable for azalea_nbt::Tag { @@ -493,3 +535,14 @@ impl McBufReadable for Slot { Ok(Slot::Present(SlotData { id, count, nbt })) } } + +// Uuid +#[async_trait] +impl McBufReadable for Uuid { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + buf.read_uuid().await + } +} diff --git a/azalea-protocol/src/mc_buf/write.rs b/azalea-protocol/src/mc_buf/write.rs index b57ad786..bd5e3f52 100755 --- a/azalea-protocol/src/mc_buf/write.rs +++ b/azalea-protocol/src/mc_buf/write.rs @@ -2,10 +2,12 @@ use super::{UnsizedByteArray, MAX_STRING_LENGTH}; use async_trait::async_trait; use azalea_chat::component::Component; use azalea_core::{ - difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, Slot, + difficulty::Difficulty, game_type::GameType, resource_location::ResourceLocation, + serializable_uuid::SerializableUuid, Slot, }; use byteorder::{BigEndian, WriteBytesExt}; use std::io::Write; +use uuid::Uuid; #[async_trait] pub trait Writable { @@ -43,6 +45,7 @@ pub trait Writable { ) -> Result<(), std::io::Error>; fn write_float(&mut self, n: f32) -> Result<(), std::io::Error>; fn write_double(&mut self, n: f64) -> Result<(), std::io::Error>; + fn write_uuid(&mut self, uuid: &Uuid) -> Result<(), std::io::Error>; } #[async_trait] @@ -163,6 +166,15 @@ impl Writable for Vec { ) -> Result<(), std::io::Error> { self.write_utf(&location.to_string()) } + + fn write_uuid(&mut self, uuid: &Uuid) -> Result<(), std::io::Error> { + 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(()) + } } pub trait McBufWritable @@ -317,6 +329,32 @@ impl McBufWritable for Option { } } +// Option +impl McBufWritable for Option { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + if let Some(s) = self { + buf.write_boolean(true)?; + buf.write_utf(s)?; + } else { + buf.write_boolean(false)?; + }; + Ok(()) + } +} + +// Option +impl McBufWritable for Option { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + if let Some(s) = self { + buf.write_boolean(true)?; + s.write_into(buf)?; + } else { + buf.write_boolean(false)?; + }; + Ok(()) + } +} + // azalea_nbt::Tag impl McBufWritable for azalea_nbt::Tag { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { @@ -365,3 +403,12 @@ impl McBufWritable for Slot { Ok(()) } } + +// Slot +impl McBufWritable for Uuid { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_uuid(self)?; + + Ok(()) + } +} 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 0a16440e..b22570c1 100755 --- a/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs +++ b/azalea-protocol/src/packets/game/clientbound_declare_commands_packet.rs @@ -1,5 +1,5 @@ use super::GamePacket; -use crate::mc_buf::{McBufReadable, McBufWritable, Readable}; +use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; use async_trait::async_trait; use azalea_core::resource_location::ResourceLocation; use std::hash::Hash; @@ -115,7 +115,7 @@ impl McBufReadable for BrigadierString { } impl McBufWritable for BrigadierString { fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { - buf.write_i8(*self as i8); + buf.write_byte(*self as u8)?; Ok(()) } } 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 82b0072d..55e4d419 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,3 @@ -use azalea_core::{game_type::GameType, resource_location::ResourceLocation}; use packet_macros::GamePacket; // we can't identify the status in azalea-protocol since they vary depending on the entity diff --git a/azalea-protocol/src/packets/game/clientbound_player_info_packet.rs b/azalea-protocol/src/packets/game/clientbound_player_info_packet.rs new file mode 100644 index 00000000..370e6f83 --- /dev/null +++ b/azalea-protocol/src/packets/game/clientbound_player_info_packet.rs @@ -0,0 +1,101 @@ +// i don't know the actual name of this packet, i couldn't find it in the source code + +use crate::mc_buf::{McBufReadable, McBufWritable, Readable, Writable}; +use async_trait::async_trait; +use azalea_chat::component::Component; +use packet_macros::{GamePacket, McBufReadable, McBufWritable}; +use tokio::io::AsyncRead; +use uuid::Uuid; + +#[derive(Clone, Debug, GamePacket)] +pub struct ClientboundPlayerInfoPacket { + pub action: Action, +} + +#[derive(Clone, Debug)] +pub enum Action { + AddPlayer(Vec), + UpdateGameMode(Vec), + UpdateLatency(Vec), + UpdateDisplayName(Vec), + RemovePlayer(Vec), +} + +#[derive(Clone, Debug, McBufReadable, McBufWritable)] +pub struct PlayerProperty { + name: String, + value: String, + signature: Option, +} + +#[derive(Clone, Debug, McBufReadable, McBufWritable)] +pub struct AddPlayer { + uuid: Uuid, + properties: Vec, + #[varint] + gamemode: u32, + #[varint] + ping: i32, + display_name: Option, +} + +#[derive(Clone, Debug, McBufReadable, McBufWritable)] +pub struct UpdateGameMode { + uuid: Uuid, + #[varint] + gamemode: u32, +} + +#[derive(Clone, Debug, McBufReadable, McBufWritable)] +pub struct UpdateLatency { + uuid: Uuid, + #[varint] + ping: i32, +} + +#[derive(Clone, Debug, McBufReadable, McBufWritable)] +pub struct UpdateDisplayName { + uuid: Uuid, + display_name: Option, +} +#[derive(Clone, Debug, McBufReadable, McBufWritable)] +pub struct RemovePlayer { + uuid: Uuid, +} + +#[async_trait] +impl McBufReadable for Action { + async fn read_into(buf: &mut R) -> Result + where + R: AsyncRead + std::marker::Unpin + std::marker::Send, + { + let id = buf.read_byte().await?; + Ok(match id { + 0 => Action::AddPlayer(Vec::::read_into(buf).await?), + 1 => Action::UpdateGameMode(Vec::::read_into(buf).await?), + 2 => Action::UpdateLatency(Vec::::read_into(buf).await?), + 3 => Action::UpdateDisplayName(Vec::::read_into(buf).await?), + 4 => Action::RemovePlayer(Vec::::read_into(buf).await?), + _ => panic!("Unknown player info action id: {}", id), + }) + } +} +impl McBufWritable for Action { + fn write_into(&self, buf: &mut Vec) -> Result<(), std::io::Error> { + buf.write_byte(match self { + Action::AddPlayer(_) => 0, + Action::UpdateGameMode(_) => 1, + Action::UpdateLatency(_) => 2, + Action::UpdateDisplayName(_) => 3, + Action::RemovePlayer(_) => 4, + })?; + match self { + Action::AddPlayer(players) => players.write_into(buf)?, + Action::UpdateGameMode(players) => players.write_into(buf)?, + Action::UpdateLatency(players) => players.write_into(buf)?, + Action::UpdateDisplayName(players) => players.write_into(buf)?, + Action::RemovePlayer(players) => players.write_into(buf)?, + } + Ok(()) + } +} diff --git a/azalea-protocol/src/packets/game/mod.rs b/azalea-protocol/src/packets/game/mod.rs index 436e0cc2..0a9f1621 100755 --- a/azalea-protocol/src/packets/game/mod.rs +++ b/azalea-protocol/src/packets/game/mod.rs @@ -5,6 +5,7 @@ pub mod clientbound_disconnect_packet; pub mod clientbound_entity_event_packet; pub mod clientbound_login_packet; pub mod clientbound_player_abilities_packet; +pub mod clientbound_player_info_packet; pub mod clientbound_player_position_packet; pub mod clientbound_recipe_packet; pub mod clientbound_set_carried_item_packet; @@ -25,6 +26,7 @@ declare_state_packets!( 0x18: clientbound_custom_payload_packet::ClientboundCustomPayloadPacket, 0x26: clientbound_login_packet::ClientboundLoginPacket, 0x32: clientbound_player_abilities_packet::ClientboundPlayerAbilitiesPacket, + 0x36: clientbound_player_info_packet::ClientboundPlayerInfoPacket, 0x38: clientbound_player_position_packet::ClientboundPlayerPositionPacket, 0x39: clientbound_recipe_packet::ClientboundRecipePacket, 0x48: clientbound_set_carried_item_packet::ClientboundSetCarriedItemPacket, 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 c54aa819..ccf0f482 100755 --- a/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs @@ -9,6 +9,7 @@ pub struct ClientboundGameProfilePacket { pub game_profile: GameProfile, } +// TODO: add derives to GameProfile and have an impl McBufReadable/Writable for GameProfile impl ClientboundGameProfilePacket { pub fn get(self) -> LoginPacket { LoginPacket::ClientboundGameProfilePacket(self) @@ -25,6 +26,7 @@ impl ClientboundGameProfilePacket { pub async fn read( buf: &mut T, ) -> Result { + // TODO: we have a thing to read from the uuid now let uuid = Uuid::from_int_array([ buf.read_int().await? as u32, buf.read_int().await? as u32,