From 764dfda8ed7cdc5c42f2c0b6a8152dbaf9c95bcd Mon Sep 17 00:00:00 2001 From: Vladislav Golub Date: Tue, 24 Dec 2019 21:30:07 +0300 Subject: [PATCH] Add login client bound packets --- src/lib.rs | 35 ++++++++- src/login.rs | 201 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 225 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6cb06a0..fcb7e2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,19 @@ //! This crate implements Minecraft protocol. //! //! Information about protocol can be found at https://wiki.vg/Protocol. -use byteorder::{ReadBytesExt, WriteBytesExt}; use io::Error as IoError; -use mc_varint::{VarIntRead, VarIntWrite}; -use serde_json::error::Error as JsonError; use std::io; use std::io::{Read, Write}; use std::string::FromUtf8Error; +use byteorder::ReadBytesExt; +use byteorder::WriteBytesExt; +use mc_varint::{VarIntRead, VarIntWrite}; +use serde_json::error::Error as JsonError; +use uuid::parser::ParseError as UuidParseError; + +use crate::chat::Message; + pub mod chat; pub mod login; pub mod status; @@ -72,6 +77,9 @@ pub enum DecodeError { }, /// Boolean are parsed from byte. Valid byte value are 0 or 1. NonBoolValue, + UuidParseError { + uuid_parse_error: UuidParseError, + }, } impl From for DecodeError { @@ -92,6 +100,12 @@ impl From for DecodeError { } } +impl From for DecodeError { + fn from(uuid_parse_error: UuidParseError) -> Self { + DecodeError::UuidParseError { uuid_parse_error } + } +} + trait Packet { type Output; @@ -107,6 +121,8 @@ trait PacketRead { fn read_string(&mut self, max_length: u32) -> Result; fn read_byte_array(&mut self) -> Result, DecodeError>; + + fn read_chat_message(&mut self) -> Result; } /// Trait adds additional helper methods for `Write` to write protocol data. @@ -116,6 +132,8 @@ trait PacketWrite { fn write_string(&mut self, value: &str, max_length: u32) -> Result<(), EncodeError>; fn write_byte_array(&mut self, value: &[u8]) -> Result<(), EncodeError>; + + fn write_chat_message(&mut self, value: &Message) -> Result<(), EncodeError>; } impl PacketRead for R { @@ -148,6 +166,13 @@ impl PacketRead for R { Ok(buf) } + + fn read_chat_message(&mut self) -> Result { + let json = self.read_string(STRING_MAX_LENGTH)?; + let message = Message::from_json(&json)?; + + Ok(message) + } } impl PacketWrite for W { @@ -180,4 +205,8 @@ impl PacketWrite for W { Ok(()) } + + fn write_chat_message(&mut self, value: &Message) -> Result<(), EncodeError> { + self.write_string(&value.to_json()?, STRING_MAX_LENGTH) + } } diff --git a/src/login.rs b/src/login.rs index e13e554..d7b6717 100644 --- a/src/login.rs +++ b/src/login.rs @@ -1,9 +1,14 @@ -use crate::{DecodeError, EncodeError, Packet, PacketRead, PacketWrite}; -use mc_varint::{VarIntRead, VarIntWrite}; use std::io::{Read, Write}; -/// Login maximum length. +use mc_varint::{VarIntRead, VarIntWrite}; +use uuid::Uuid; + +use crate::chat::Message; +use crate::{DecodeError, EncodeError, Packet, PacketRead, PacketWrite, STRING_MAX_LENGTH}; + const LOGIN_MAX_LENGTH: u32 = 16; +const SERVER_ID_MAX_LENGTH: u32 = 20; +const HYPHENATED_UUID_LENGTH: u32 = 36; pub enum LoginServerBoundPacket { LoginStart(LoginStart), @@ -12,11 +17,11 @@ pub enum LoginServerBoundPacket { } pub enum LoginClientBoundPacket { - Disconnect, - EncryptionRequest, - LoginSuccess, - SetCompression, - LoginPluginRequest, + LoginDisconnect(LoginDisconnect), + EncryptionRequest(EncryptionRequest), + LoginSuccess(LoginSuccess), + SetCompression(SetCompression), + LoginPluginRequest(LoginPluginRequest), } impl LoginServerBoundPacket { @@ -160,3 +165,183 @@ impl Packet for LoginPluginResponse { }) } } + +pub struct LoginDisconnect { + pub reason: Message, +} + +impl LoginDisconnect { + pub fn new(reason: Message) -> LoginClientBoundPacket { + let login_disconnect = LoginDisconnect { reason }; + + LoginClientBoundPacket::LoginDisconnect(login_disconnect) + } +} + +impl Packet for LoginDisconnect { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_chat_message(&self.reason)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let reason = reader.read_chat_message()?; + + Ok(LoginDisconnect { reason }) + } +} + +pub struct EncryptionRequest { + pub server_id: String, + pub public_key: Vec, + pub verify_token: Vec, +} + +impl EncryptionRequest { + pub fn new( + server_id: String, + public_key: Vec, + verify_token: Vec, + ) -> LoginClientBoundPacket { + let encryption_request = EncryptionRequest { + server_id, + public_key, + verify_token, + }; + + LoginClientBoundPacket::EncryptionRequest(encryption_request) + } +} + +impl Packet for EncryptionRequest { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_string(&self.server_id, SERVER_ID_MAX_LENGTH)?; + writer.write_byte_array(&self.public_key)?; + writer.write_byte_array(&self.verify_token)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let server_id = reader.read_string(SERVER_ID_MAX_LENGTH)?; + let public_key = reader.read_byte_array()?; + let verify_token = reader.read_byte_array()?; + + Ok(EncryptionRequest { + server_id, + public_key, + verify_token, + }) + } +} + +pub struct LoginSuccess { + pub uuid: Uuid, + pub username: String, +} + +impl LoginSuccess { + pub fn new(uuid: Uuid, username: String) -> LoginClientBoundPacket { + let login_success = LoginSuccess { uuid, username }; + + LoginClientBoundPacket::LoginSuccess(login_success) + } +} + +impl Packet for LoginSuccess { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + let uuid_hyphenated_string = self.uuid.to_hyphenated().to_string(); + + writer.write_string(&uuid_hyphenated_string, HYPHENATED_UUID_LENGTH)?; + writer.write_string(&self.username, LOGIN_MAX_LENGTH)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let uuid_hyphenated_string = reader.read_string(HYPHENATED_UUID_LENGTH)?; + + let uuid = Uuid::parse_str(&uuid_hyphenated_string)?; + let username = reader.read_string(LOGIN_MAX_LENGTH)?; + + Ok(LoginSuccess { uuid, username }) + } +} + +pub struct SetCompression { + pub threshold: i32, +} + +impl SetCompression { + pub fn new(threshold: i32) -> LoginClientBoundPacket { + let set_compression = SetCompression { threshold }; + + LoginClientBoundPacket::SetCompression(set_compression) + } +} + +impl Packet for SetCompression { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_var_i32(self.threshold)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let threshold = reader.read_var_i32()?; + + Ok(SetCompression { threshold }) + } +} + +pub struct LoginPluginRequest { + pub message_id: i32, + pub channel: String, + pub data: Vec, +} + +impl LoginPluginRequest { + pub fn new(message_id: i32, channel: String, data: Vec) -> LoginClientBoundPacket { + let login_plugin_request = LoginPluginRequest { + message_id, + channel, + data, + }; + + LoginClientBoundPacket::LoginPluginRequest(login_plugin_request) + } +} + +impl Packet for LoginPluginRequest { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_var_i32(self.message_id)?; + writer.write_string(&self.channel, STRING_MAX_LENGTH)?; + writer.write_all(&self.data)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let message_id = reader.read_var_i32()?; + let channel = reader.read_string(STRING_MAX_LENGTH)?; + let mut data = Vec::new(); + reader.read_to_end(data.as_mut())?; + + Ok(LoginPluginRequest { + message_id, + channel, + data, + }) + } +}