add a few more login packets

This commit is contained in:
mat 2021-12-16 17:51:05 -06:00
parent 999116ed7c
commit 227ba5511d
15 changed files with 181 additions and 24 deletions

9
Cargo.lock generated
View file

@ -30,6 +30,13 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "azalea-auth"
version = "0.1.0"
dependencies = [
"uuid",
]
[[package]] [[package]]
name = "azalea-chat" name = "azalea-chat"
version = "0.1.0" version = "0.1.0"
@ -59,7 +66,9 @@ version = "0.1.0"
dependencies = [ dependencies = [
"async-recursion", "async-recursion",
"async-trait", "async-trait",
"azalea-auth",
"azalea-chat", "azalea-chat",
"azalea-core",
"byteorder", "byteorder",
"bytes", "bytes",
"serde", "serde",

View file

@ -6,4 +6,5 @@ members = [
"azalea-protocol", "azalea-protocol",
"azalea-chat", "azalea-chat",
"azalea-core", "azalea-core",
"azalea-auth",
] ]

9
azalea-auth/Cargo.toml Normal file
View file

@ -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"

View file

@ -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<String, String>,
}
impl GameProfile {
pub fn new(uuid: Uuid, name: String) -> Self {
GameProfile {
uuid,
name,
properties: HashMap::new(),
}
}
}

3
azalea-auth/src/lib.rs Normal file
View file

@ -0,0 +1,3 @@
//! Handle Minecraft authentication.
pub mod game_profile;

View file

@ -22,8 +22,6 @@ lazy_static! {
/// A chat component /// A chat component
impl 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 { pub fn get_base_mut(&mut self) -> &mut BaseComponent {
match self { match self {
Self::Text(c) => &mut c.base, Self::Text(c) => &mut c.base,

View file

@ -1,14 +1,15 @@
//! A resource, like minecraft:stone //! A resource, like minecraft:stone
pub struct ResourceLocation<'a> { #[derive(Hash, Clone, Debug)]
pub namespace: &'a str, pub struct ResourceLocation {
pub path: &'a str, pub namespace: String,
pub path: String,
} }
static DEFAULT_NAMESPACE: &str = "minecraft"; static DEFAULT_NAMESPACE: &str = "minecraft";
// static REALMS_NAMESPACE: &str = "realms"; // static REALMS_NAMESPACE: &str = "realms";
impl<'a> ResourceLocation<'a> { impl ResourceLocation {
pub fn new(resource_string: &str) -> Result<ResourceLocation, String> { pub fn new(resource_string: &str) -> Result<ResourceLocation, String> {
let sep_byte_position_option = resource_string.chars().position(|c| c == ':'); let sep_byte_position_option = resource_string.chars().position(|c| c == ':');
let (namespace, path) = if let Some(sep_byte_position) = sep_byte_position_option { let (namespace, path) = if let Some(sep_byte_position) = sep_byte_position_option {
@ -23,7 +24,16 @@ impl<'a> ResourceLocation<'a> {
} else { } else {
(DEFAULT_NAMESPACE, resource_string) (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)
} }
} }

View file

@ -26,7 +26,7 @@ impl SerializableUuid for Uuid {
let most = ((array[0] as u64) << 32) | ((array[1] as u64) & 0xFFFFFFFF); let most = ((array[0] as u64) << 32) | ((array[1] as u64) & 0xFFFFFFFF);
let least = ((array[2] as u64) << 32) | ((array[3] 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)
} }
} }

View file

@ -8,7 +8,9 @@ version = "0.1.0"
[dependencies] [dependencies]
async-recursion = "^0.3.2" async-recursion = "^0.3.2"
async-trait = "0.1.51" async-trait = "0.1.51"
azalea-auth = {path = "../azalea-auth"}
azalea-chat = {path = "../azalea-chat"} azalea-chat = {path = "../azalea-chat"}
azalea-core = {path = "../azalea-core"}
byteorder = "^1.4.3" byteorder = "^1.4.3"
bytes = "^1.1.0" bytes = "^1.1.0"
serde = {version = "1.0.130", features = ["serde_derive"]} serde = {version = "1.0.130", features = ["serde_derive"]}

View file

@ -1,6 +1,6 @@
//! Utilities for reading and writing for the Minecraft protocol //! 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 async_trait::async_trait;
use byteorder::{BigEndian, WriteBytesExt}; use byteorder::{BigEndian, WriteBytesExt};
@ -35,6 +35,7 @@ pub trait Writable {
fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error>; fn write_utf(&mut self, string: &str) -> Result<(), std::io::Error>;
fn write_short(&mut self, n: u16) -> 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_byte_array(&mut self, bytes: &[u8]) -> Result<(), std::io::Error>;
fn write_int(&mut self, n: i32) -> Result<(), std::io::Error>;
} }
#[async_trait] #[async_trait]
@ -79,7 +80,8 @@ impl Writable for Vec<u8> {
} }
fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), std::io::Error> { 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> { fn write_varint(&mut self, mut value: i32) -> Result<(), std::io::Error> {
@ -122,6 +124,10 @@ impl Writable for Vec<u8> {
self.write_varint(bytes.len() as i32)?; self.write_varint(bytes.len() as i32)?;
self.write_bytes(bytes) self.write_bytes(bytes)
} }
fn write_int(&mut self, n: i32) -> Result<(), std::io::Error> {
WriteBytesExt::write_i32::<BigEndian>(self, n)
}
} }
#[async_trait] #[async_trait]
@ -133,6 +139,7 @@ pub trait Readable {
async fn read_utf(&mut self) -> Result<String, String>; async fn read_utf(&mut self) -> Result<String, String>;
async fn read_utf_with_len(&mut self, max_length: u32) -> Result<String, String>; async fn read_utf_with_len(&mut self, max_length: u32) -> Result<String, String>;
async fn read_byte(&mut self) -> Result<u8, String>; async fn read_byte(&mut self) -> Result<u8, String>;
async fn read_int(&mut self) -> Result<i32, String>;
} }
#[async_trait] #[async_trait]
@ -225,6 +232,13 @@ where
Err(_) => Err("Error reading byte".to_string()), Err(_) => Err("Error reading byte".to_string()),
} }
} }
async fn read_int(&mut self) -> Result<i32, String> {
match AsyncReadExt::read_i32(self).await {
Ok(r) => Ok(r),
Err(_) => Err("Error reading int".to_string()),
}
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -1,15 +1,13 @@
use super::LoginPacket;
use crate::mc_buf::{Readable, Writable};
use azalea_core::resource_location::ResourceLocation;
use std::hash::Hash; use std::hash::Hash;
use tokio::io::BufReader; use tokio::io::BufReader;
use crate::mc_buf::{Readable, Writable};
use super::LoginPacket;
#[derive(Hash, Clone, Debug)] #[derive(Hash, Clone, Debug)]
pub struct ClientboundCustomQueryPacket { pub struct ClientboundCustomQueryPacket {
pub transaction_id: u32, pub transaction_id: u32,
// TODO: this should be a resource location pub identifier: ResourceLocation,
pub identifier: String,
pub data: Vec<u8>, pub data: Vec<u8>,
} }
@ -20,7 +18,7 @@ impl ClientboundCustomQueryPacket {
pub fn write(&self, buf: &mut Vec<u8>) { pub fn write(&self, buf: &mut Vec<u8>) {
buf.write_varint(self.transaction_id as i32).unwrap(); 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(); buf.write_bytes(&self.data).unwrap();
} }
@ -28,8 +26,7 @@ impl ClientboundCustomQueryPacket {
buf: &mut BufReader<T>, buf: &mut BufReader<T>,
) -> Result<LoginPacket, String> { ) -> Result<LoginPacket, String> {
let transaction_id = buf.read_varint().await? as u32; let transaction_id = buf.read_varint().await? as u32;
// TODO: this should be a resource location let identifier = ResourceLocation::new(&buf.read_utf().await?)?;
let identifier = buf.read_utf().await?;
let data = buf.read_bytes(1048576).await?; let data = buf.read_bytes(1048576).await?;
Ok(ClientboundCustomQueryPacket { Ok(ClientboundCustomQueryPacket {
transaction_id, transaction_id,

View file

@ -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<u8>) {
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<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
buf: &mut BufReader<T>,
) -> Result<LoginPacket, String> {
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),
}
}
}

View file

@ -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<u8>) {
buf.write_varint(self.compression_threshold).unwrap();
}
pub async fn read<T: tokio::io::AsyncRead + std::marker::Unpin + std::marker::Send>(
buf: &mut BufReader<T>,
) -> Result<LoginPacket, String> {
let compression_threshold = buf.read_varint().await?;
Ok(ClientboundLoginCompressionPacket {
compression_threshold
}
.get())
}
}

View file

@ -1,5 +1,7 @@
pub mod clientbound_custom_query_packet; pub mod clientbound_custom_query_packet;
pub mod clientbound_game_profile_packet;
pub mod clientbound_hello_packet; pub mod clientbound_hello_packet;
pub mod clientbound_login_compression_packet;
pub mod serverbound_hello_packet; pub mod serverbound_hello_packet;
use async_trait::async_trait; use async_trait::async_trait;
@ -15,8 +17,12 @@ where
Self: Sized, Self: Sized,
{ {
ClientboundCustomQueryPacket(clientbound_custom_query_packet::ClientboundCustomQueryPacket), ClientboundCustomQueryPacket(clientbound_custom_query_packet::ClientboundCustomQueryPacket),
ServerboundHelloPacket(serverbound_hello_packet::ServerboundHelloPacket), ClientboundGameProfilePacket(clientbound_game_profile_packet::ClientboundGameProfilePacket),
ClientboundHelloPacket(clientbound_hello_packet::ClientboundHelloPacket), ClientboundHelloPacket(clientbound_hello_packet::ClientboundHelloPacket),
ClientboundLoginCompressionPacket(
clientbound_login_compression_packet::ClientboundLoginCompressionPacket,
),
ServerboundHelloPacket(serverbound_hello_packet::ServerboundHelloPacket),
} }
#[async_trait] #[async_trait]
@ -24,16 +30,20 @@ impl ProtocolPacket for LoginPacket {
fn id(&self) -> u32 { fn id(&self) -> u32 {
match self { match self {
LoginPacket::ClientboundCustomQueryPacket(_packet) => 0x04, LoginPacket::ClientboundCustomQueryPacket(_packet) => 0x04,
LoginPacket::ServerboundHelloPacket(_packet) => 0x00, LoginPacket::ClientboundGameProfilePacket(_packet) => 0x02,
LoginPacket::ClientboundHelloPacket(_packet) => 0x01, LoginPacket::ClientboundHelloPacket(_packet) => 0x01,
LoginPacket::ClientboundLoginCompressionPacket(_packet) => 0x03,
LoginPacket::ServerboundHelloPacket(_packet) => 0x00,
} }
} }
fn write(&self, buf: &mut Vec<u8>) { fn write(&self, buf: &mut Vec<u8>) {
match self { match self {
LoginPacket::ClientboundCustomQueryPacket(packet) => packet.write(buf), 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::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 { Ok(match flow {
PacketFlow::ServerToClient => match id { PacketFlow::ServerToClient => match id {
0x01 => clientbound_hello_packet::ClientboundHelloPacket::read(buf).await?, 0x01 => clientbound_hello_packet::ClientboundHelloPacket::read(buf).await?,
0x02 => {
clientbound_game_profile_packet::ClientboundGameProfilePacket::read(buf).await?
}
0x04 => { 0x04 => {
clientbound_custom_query_packet::ClientboundCustomQueryPacket::read(buf).await? 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)), _ => return Err(format!("Unknown ServerToClient status packet id: {}", id)),
}, },
PacketFlow::ClientToServer => match id { PacketFlow::ClientToServer => match id {

View file

@ -10,7 +10,9 @@ pub async fn write_packet(packet: impl ProtocolPacket, stream: &mut TcpStream) {
// write the packet id // write the packet id
let mut id_and_data_buf = vec![]; 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); packet.write(&mut id_and_data_buf);
// write the packet data // 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 // make a new buffer that has the length at the beginning
// and id+data at the end // and id+data at the end
let mut complete_buf: Vec<u8> = Vec::new(); let mut complete_buf: Vec<u8> = 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); complete_buf.append(&mut id_and_data_buf);
// finally, write and flush to the stream // finally, write and flush to the stream