From 227ba5511d50af8c7c46a47e09db7f55a0ed84b7 Mon Sep 17 00:00:00 2001 From: mat Date: Thu, 16 Dec 2021 17:51:05 -0600 Subject: [PATCH] add a few more login packets --- Cargo.lock | 9 +++++ Cargo.toml | 1 + azalea-auth/Cargo.toml | 9 +++++ azalea-auth/src/game_profile.rs | 20 ++++++++++ azalea-auth/src/lib.rs | 3 ++ azalea-chat/src/component.rs | 2 - azalea-core/src/resource_location.rs | 20 +++++++--- azalea-core/src/serializable_uuid.rs | 2 +- azalea-protocol/Cargo.toml | 2 + azalea-protocol/src/mc_buf.rs | 18 ++++++++- .../login/clientbound_custom_query_packet.rs | 15 +++---- .../login/clientbound_game_profile_packet.rs | 39 +++++++++++++++++++ .../clientbound_login_compression_packet.rs | 32 +++++++++++++++ azalea-protocol/src/packets/login/mod.rs | 25 ++++++++++-- azalea-protocol/src/write.rs | 8 +++- 15 files changed, 181 insertions(+), 24 deletions(-) create mode 100644 azalea-auth/Cargo.toml create mode 100644 azalea-auth/src/game_profile.rs create mode 100644 azalea-auth/src/lib.rs create mode 100644 azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs create mode 100644 azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs diff --git a/Cargo.lock b/Cargo.lock index e999fcc1..1f6baa86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,13 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "azalea-auth" +version = "0.1.0" +dependencies = [ + "uuid", +] + [[package]] name = "azalea-chat" version = "0.1.0" @@ -59,7 +66,9 @@ version = "0.1.0" dependencies = [ "async-recursion", "async-trait", + "azalea-auth", "azalea-chat", + "azalea-core", "byteorder", "bytes", "serde", diff --git a/Cargo.toml b/Cargo.toml index 22f3e5a8..0cf441eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ members = [ "azalea-protocol", "azalea-chat", "azalea-core", + "azalea-auth", ] diff --git a/azalea-auth/Cargo.toml b/azalea-auth/Cargo.toml new file mode 100644 index 00000000..aa9b7bdb --- /dev/null +++ b/azalea-auth/Cargo.toml @@ -0,0 +1,9 @@ +[package] +edition = "2021" +name = "azalea-auth" +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" diff --git a/azalea-auth/src/game_profile.rs b/azalea-auth/src/game_profile.rs new file mode 100644 index 00000000..a186a208 --- /dev/null +++ b/azalea-auth/src/game_profile.rs @@ -0,0 +1,20 @@ +use std::collections::HashMap; + +use uuid::Uuid; + +#[derive(Hash, Clone, Debug)] +pub struct GameProfile { + pub uuid: Uuid, + pub name: String, + pub properties: HashMap, +} + +impl GameProfile { + pub fn new(uuid: Uuid, name: String) -> Self { + GameProfile { + uuid, + name, + properties: HashMap::new(), + } + } +} diff --git a/azalea-auth/src/lib.rs b/azalea-auth/src/lib.rs new file mode 100644 index 00000000..773ea1d9 --- /dev/null +++ b/azalea-auth/src/lib.rs @@ -0,0 +1,3 @@ +//! Handle Minecraft authentication. + +pub mod game_profile; diff --git a/azalea-chat/src/component.rs b/azalea-chat/src/component.rs index 1ed1f836..9f065c47 100644 --- a/azalea-chat/src/component.rs +++ b/azalea-chat/src/component.rs @@ -22,8 +22,6 @@ lazy_static! { /// A chat component impl Component { - // TODO: is it possible to use a macro so this doesn't have to be duplicated? - pub fn get_base_mut(&mut self) -> &mut BaseComponent { match self { Self::Text(c) => &mut c.base, diff --git a/azalea-core/src/resource_location.rs b/azalea-core/src/resource_location.rs index df706e7a..c1fed451 100644 --- a/azalea-core/src/resource_location.rs +++ b/azalea-core/src/resource_location.rs @@ -1,14 +1,15 @@ //! A resource, like minecraft:stone -pub struct ResourceLocation<'a> { - pub namespace: &'a str, - pub path: &'a str, +#[derive(Hash, Clone, Debug)] +pub struct ResourceLocation { + pub namespace: String, + pub path: String, } static DEFAULT_NAMESPACE: &str = "minecraft"; // static REALMS_NAMESPACE: &str = "realms"; -impl<'a> ResourceLocation<'a> { +impl ResourceLocation { pub fn new(resource_string: &str) -> Result { let sep_byte_position_option = resource_string.chars().position(|c| c == ':'); let (namespace, path) = if let Some(sep_byte_position) = sep_byte_position_option { @@ -23,7 +24,16 @@ impl<'a> ResourceLocation<'a> { } else { (DEFAULT_NAMESPACE, resource_string) }; - Ok(ResourceLocation { namespace, path }) + Ok(ResourceLocation { + namespace: namespace.to_string(), + path: path.to_string(), + }) + } +} + +impl std::fmt::Display for ResourceLocation { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}:{}", self.namespace, self.path) } } diff --git a/azalea-core/src/serializable_uuid.rs b/azalea-core/src/serializable_uuid.rs index 473d8254..f8c03b60 100644 --- a/azalea-core/src/serializable_uuid.rs +++ b/azalea-core/src/serializable_uuid.rs @@ -26,7 +26,7 @@ impl SerializableUuid for Uuid { 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).into()) + Uuid::from_u128(((most as u128) << 64) | least as u128) } } diff --git a/azalea-protocol/Cargo.toml b/azalea-protocol/Cargo.toml index 2b8a97f2..cf97f089 100644 --- a/azalea-protocol/Cargo.toml +++ b/azalea-protocol/Cargo.toml @@ -8,7 +8,9 @@ version = "0.1.0" [dependencies] async-recursion = "^0.3.2" async-trait = "0.1.51" +azalea-auth = {path = "../azalea-auth"} azalea-chat = {path = "../azalea-chat"} +azalea-core = {path = "../azalea-core"} byteorder = "^1.4.3" bytes = "^1.1.0" serde = {version = "1.0.130", features = ["serde_derive"]} diff --git a/azalea-protocol/src/mc_buf.rs b/azalea-protocol/src/mc_buf.rs index 5a1ac274..0a47f637 100644 --- a/azalea-protocol/src/mc_buf.rs +++ b/azalea-protocol/src/mc_buf.rs @@ -1,6 +1,6 @@ //! Utilities for reading and writing for the Minecraft protocol -use std::{future::Future, io::Write}; +use std::io::Write; use async_trait::async_trait; use byteorder::{BigEndian, WriteBytesExt}; @@ -35,6 +35,7 @@ pub trait Writable { fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error>; fn write_short(&mut self, n: u16) -> Result<(), std::io::Error>; fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error>; + fn write_int(&mut self, n: i32) -> Result<(), std::io::Error>; } #[async_trait] @@ -79,7 +80,8 @@ impl Writable for Vec { } fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { - Ok(self.extend_from_slice(bytes)) + self.extend_from_slice(bytes); + Ok(()) } fn write_varint(&mut self, mut value: i32) -> Result<(), std::io::Error> { @@ -122,6 +124,10 @@ impl Writable for Vec { 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) + } } #[async_trait] @@ -133,6 +139,7 @@ pub trait Readable { async fn read_utf(&mut self) -> Result; async fn read_utf_with_len(&mut self, max_length: u32) -> Result; async fn read_byte(&mut self) -> Result; + async fn read_int(&mut self) -> Result; } #[async_trait] @@ -225,6 +232,13 @@ where Err(_) => Err("Error reading byte".to_string()), } } + + async fn read_int(&mut self) -> Result { + match AsyncReadExt::read_i32(self).await { + Ok(r) => Ok(r), + Err(_) => Err("Error reading int".to_string()), + } + } } #[cfg(test)] 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 8f501fc9..2c66bfa3 100644 --- a/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs +++ b/azalea-protocol/src/packets/login/clientbound_custom_query_packet.rs @@ -1,15 +1,13 @@ +use super::LoginPacket; +use crate::mc_buf::{Readable, Writable}; +use azalea_core::resource_location::ResourceLocation; use std::hash::Hash; use tokio::io::BufReader; -use crate::mc_buf::{Readable, Writable}; - -use super::LoginPacket; - #[derive(Hash, Clone, Debug)] pub struct ClientboundCustomQueryPacket { pub transaction_id: u32, - // TODO: this should be a resource location - pub identifier: String, + pub identifier: ResourceLocation, pub data: Vec, } @@ -20,7 +18,7 @@ impl ClientboundCustomQueryPacket { pub fn write(&self, buf: &mut Vec) { buf.write_varint(self.transaction_id as i32).unwrap(); - buf.write_utf(&self.identifier).unwrap(); + buf.write_utf(self.identifier.to_string().as_str()).unwrap(); buf.write_bytes(&self.data).unwrap(); } @@ -28,8 +26,7 @@ impl ClientboundCustomQueryPacket { buf: &mut BufReader, ) -> Result { let transaction_id = buf.read_varint().await? as u32; - // TODO: this should be a resource location - let identifier = buf.read_utf().await?; + let identifier = ResourceLocation::new(&buf.read_utf().await?)?; let data = buf.read_bytes(1048576).await?; Ok(ClientboundCustomQueryPacket { transaction_id, diff --git a/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs b/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs new file mode 100644 index 00000000..88166c54 --- /dev/null +++ b/azalea-protocol/src/packets/login/clientbound_game_profile_packet.rs @@ -0,0 +1,39 @@ +use super::LoginPacket; +use crate::mc_buf::{Readable, Writable}; +use azalea_auth::game_profile::GameProfile; +use azalea_core::{resource_location::ResourceLocation, serializable_uuid::SerializableUuid}; +use std::hash::Hash; +use tokio::io::BufReader; + +#[derive(Hash, Clone, Debug)] +pub struct ClientboundGameProfilePacket { + pub game_profile: GameProfile, +} + +impl ClientboundGameProfilePacket { + pub fn get(self) -> LoginPacket { + LoginPacket::ClientboundGameProfilePacket(self) + } + + pub fn write(&self, buf: &mut Vec) { + for n in self.game_profile.uuid.to_int_array() { + buf.write_int(n as i32).unwrap(); + } + buf.write_utf(self.game_profile.name.as_str()).unwrap(); + } + + pub async fn read( + buf: &mut BufReader, + ) -> Result { + let uuid = SerializableUuid::from_int_array( + buf.read_int().await?, + buf.read_int().await?, + buf.read_int().await?, + buf.read_int().await?, + ); + let name = buf.read_utf(16).await?; + ClientboundGameProfilePacket { + game_profile: GameProfile::new(uuid, name), + } + } +} diff --git a/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs b/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs new file mode 100644 index 00000000..87fc6e03 --- /dev/null +++ b/azalea-protocol/src/packets/login/clientbound_login_compression_packet.rs @@ -0,0 +1,32 @@ +use std::hash::Hash; +use tokio::io::BufReader; + +use crate::mc_buf::Readable; + +use super::LoginPacket; + +#[derive(Hash, Clone, Debug)] +pub struct ClientboundLoginCompressionPacket { + pub compression_threshold: i32, +} + +impl ClientboundLoginCompressionPacket { + pub fn get(self) -> LoginPacket { + LoginPacket::ClientboundLoginCompressionPacket(self) + } + + pub fn write(&self, buf: &mut Vec) { + buf.write_varint(self.compression_threshold).unwrap(); + } + + pub async fn read( + buf: &mut BufReader, + ) -> Result { + let compression_threshold = buf.read_varint().await?; + + Ok(ClientboundLoginCompressionPacket { + compression_threshold + } + .get()) + } +} diff --git a/azalea-protocol/src/packets/login/mod.rs b/azalea-protocol/src/packets/login/mod.rs index f0ed6717..7fee684a 100644 --- a/azalea-protocol/src/packets/login/mod.rs +++ b/azalea-protocol/src/packets/login/mod.rs @@ -1,5 +1,7 @@ pub mod clientbound_custom_query_packet; +pub mod clientbound_game_profile_packet; pub mod clientbound_hello_packet; +pub mod clientbound_login_compression_packet; pub mod serverbound_hello_packet; use async_trait::async_trait; @@ -15,8 +17,12 @@ where Self: Sized, { ClientboundCustomQueryPacket(clientbound_custom_query_packet::ClientboundCustomQueryPacket), - ServerboundHelloPacket(serverbound_hello_packet::ServerboundHelloPacket), + ClientboundGameProfilePacket(clientbound_game_profile_packet::ClientboundGameProfilePacket), ClientboundHelloPacket(clientbound_hello_packet::ClientboundHelloPacket), + ClientboundLoginCompressionPacket( + clientbound_login_compression_packet::ClientboundLoginCompressionPacket, + ), + ServerboundHelloPacket(serverbound_hello_packet::ServerboundHelloPacket), } #[async_trait] @@ -24,16 +30,20 @@ impl ProtocolPacket for LoginPacket { fn id(&self) -> u32 { match self { LoginPacket::ClientboundCustomQueryPacket(_packet) => 0x04, - LoginPacket::ServerboundHelloPacket(_packet) => 0x00, + LoginPacket::ClientboundGameProfilePacket(_packet) => 0x02, LoginPacket::ClientboundHelloPacket(_packet) => 0x01, + LoginPacket::ClientboundLoginCompressionPacket(_packet) => 0x03, + LoginPacket::ServerboundHelloPacket(_packet) => 0x00, } } fn write(&self, buf: &mut Vec) { match self { LoginPacket::ClientboundCustomQueryPacket(packet) => packet.write(buf), - LoginPacket::ServerboundHelloPacket(packet) => packet.write(buf), + LoginPacket::ClientboundGameProfilePacket(packet) => packet.write(buf), LoginPacket::ClientboundHelloPacket(packet) => packet.write(buf), + LoginPacket::ClientboundLoginCompressionPacket(packet) => packet.write(buf), + LoginPacket::ServerboundHelloPacket(packet) => packet.write(buf), } } @@ -49,9 +59,18 @@ impl ProtocolPacket for LoginPacket { Ok(match flow { PacketFlow::ServerToClient => match id { 0x01 => clientbound_hello_packet::ClientboundHelloPacket::read(buf).await?, + 0x02 => { + clientbound_game_profile_packet::ClientboundGameProfilePacket::read(buf).await? + } 0x04 => { clientbound_custom_query_packet::ClientboundCustomQueryPacket::read(buf).await? } + 0x03 => { + clientbound_login_compression_packet::ClientboundLoginCompressionPacket::read( + buf, + ) + .await? + } _ => return Err(format!("Unknown ServerToClient status packet id: {}", id)), }, PacketFlow::ClientToServer => match id { diff --git a/azalea-protocol/src/write.rs b/azalea-protocol/src/write.rs index 3d8540eb..bf9fd0aa 100644 --- a/azalea-protocol/src/write.rs +++ b/azalea-protocol/src/write.rs @@ -10,7 +10,9 @@ pub async fn write_packet(packet: impl ProtocolPacket, stream: &mut TcpStream) { // write the packet id let mut id_and_data_buf = vec![]; - id_and_data_buf.write_varint(packet.id() as i32); + id_and_data_buf + .write_varint(packet.id() as i32) + .expect("Writing packet id failed"); packet.write(&mut id_and_data_buf); // write the packet data @@ -18,7 +20,9 @@ pub async fn write_packet(packet: impl ProtocolPacket, stream: &mut TcpStream) { // make a new buffer that has the length at the beginning // and id+data at the end let mut complete_buf: Vec = Vec::new(); - complete_buf.write_varint(id_and_data_buf.len() as i32); + complete_buf + .write_varint(id_and_data_buf.len() as i32) + .expect("Writing packet length failed"); complete_buf.append(&mut id_and_data_buf); // finally, write and flush to the stream