diff --git a/src/lib.rs b/src/lib.rs index b9f2810..6cb06a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ //! 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; @@ -9,17 +10,23 @@ use std::io::{Read, Write}; use std::string::FromUtf8Error; pub mod chat; +pub mod login; pub mod status; /// Current supported protocol version. pub const PROTOCOL_VERSION: usize = 498; /// String maximum length. -const MAX_STRING_LENGTH: usize = 32_768; +const STRING_MAX_LENGTH: u32 = 32_768; /// Possible errors while encoding packet. pub enum EncodeError { - /// String length can't be more than `MAX_STRING_LENGTH` value. - StringTooLong, + /// String length can't be more than provided value. + StringTooLong { + /// String length. + length: usize, + /// Max string length. + max_length: u32, + }, IOError { io_error: IoError, }, @@ -46,8 +53,13 @@ pub enum DecodeError { UnknownPacketType { type_id: u8, }, - /// String length can't be more than `MAX_STRING_LENGTH` value. - StringTooLong, + /// String length can't be more than provided value. + StringTooLong { + /// String length. + length: u32, + /// Max string length. + max_length: u32, + }, IOError { io_error: IoError, }, @@ -58,6 +70,8 @@ pub enum DecodeError { Utf8Error { utf8_error: FromUtf8Error, }, + /// Boolean are parsed from byte. Valid byte value are 0 or 1. + NonBoolValue, } impl From for DecodeError { @@ -88,20 +102,36 @@ trait Packet { /// Trait adds additional helper methods for `Read` to read protocol data. trait PacketRead { - fn read_string(&mut self) -> Result; + fn read_bool(&mut self) -> Result; + + fn read_string(&mut self, max_length: u32) -> Result; + + fn read_byte_array(&mut self) -> Result, DecodeError>; } /// Trait adds additional helper methods for `Write` to write protocol data. trait PacketWrite { - fn write_string(&mut self, value: &str) -> Result<(), EncodeError>; + fn write_bool(&mut self, value: bool) -> Result<(), EncodeError>; + + fn write_string(&mut self, value: &str, max_length: u32) -> Result<(), EncodeError>; + + fn write_byte_array(&mut self, value: &[u8]) -> Result<(), EncodeError>; } impl PacketRead for R { - fn read_string(&mut self) -> Result { + fn read_bool(&mut self) -> Result { + match self.read_u8()? { + 0 => Ok(false), + 1 => Ok(true), + _ => Err(DecodeError::NonBoolValue), + } + } + + fn read_string(&mut self, max_length: u32) -> Result { let length = self.read_var_u32()?; - if length > MAX_STRING_LENGTH as u32 { - return Err(DecodeError::StringTooLong); + if length > max_length as u32 { + return Err(DecodeError::StringTooLong { length, max_length }); } let mut buf = vec![0; length as usize]; @@ -109,12 +139,33 @@ impl PacketRead for R { Ok(String::from_utf8(buf)?) } + + fn read_byte_array(&mut self) -> Result, DecodeError> { + let length = self.read_var_u32()?; + + let mut buf = vec![0; length as usize]; + self.read_exact(&mut buf)?; + + Ok(buf) + } } impl PacketWrite for W { - fn write_string(&mut self, value: &str) -> Result<(), EncodeError> { - if value.len() > MAX_STRING_LENGTH { - return Err(EncodeError::StringTooLong); + fn write_bool(&mut self, value: bool) -> Result<(), EncodeError> { + if value { + self.write_u8(1)?; + } else { + self.write_u8(0)?; + } + + Ok(()) + } + + fn write_string(&mut self, value: &str, max_length: u32) -> Result<(), EncodeError> { + let length = value.len(); + + if length > max_length as usize { + return Err(EncodeError::StringTooLong { length, max_length }); } self.write_var_u32(value.len() as u32)?; @@ -122,4 +173,11 @@ impl PacketWrite for W { Ok(()) } + + fn write_byte_array(&mut self, value: &[u8]) -> Result<(), EncodeError> { + self.write_var_u32(value.len() as u32)?; + self.write_all(value)?; + + Ok(()) + } } diff --git a/src/login.rs b/src/login.rs new file mode 100644 index 0000000..e13e554 --- /dev/null +++ b/src/login.rs @@ -0,0 +1,162 @@ +use crate::{DecodeError, EncodeError, Packet, PacketRead, PacketWrite}; +use mc_varint::{VarIntRead, VarIntWrite}; +use std::io::{Read, Write}; + +/// Login maximum length. +const LOGIN_MAX_LENGTH: u32 = 16; + +pub enum LoginServerBoundPacket { + LoginStart(LoginStart), + EncryptionResponse(EncryptionResponse), + LoginPluginResponse(LoginPluginResponse), +} + +pub enum LoginClientBoundPacket { + Disconnect, + EncryptionRequest, + LoginSuccess, + SetCompression, + LoginPluginRequest, +} + +impl LoginServerBoundPacket { + pub fn get_type_id(&self) -> u8 { + match self { + LoginServerBoundPacket::LoginStart(_) => 0x00, + LoginServerBoundPacket::EncryptionResponse(_) => 0x01, + LoginServerBoundPacket::LoginPluginResponse(_) => 0x02, + } + } + + pub fn decode(type_id: u8, reader: &mut R) -> Result { + match type_id { + 0x00 => { + let login_start = LoginStart::decode(reader)?; + + Ok(LoginServerBoundPacket::LoginStart(login_start)) + } + 0x01 => { + let encryption_response = EncryptionResponse::decode(reader)?; + + Ok(LoginServerBoundPacket::EncryptionResponse( + encryption_response, + )) + } + 0x02 => { + let login_plugin_response = LoginPluginResponse::decode(reader)?; + + Ok(LoginServerBoundPacket::LoginPluginResponse( + login_plugin_response, + )) + } + _ => Err(DecodeError::UnknownPacketType { type_id }), + } + } +} + +pub struct LoginStart { + pub name: String, +} + +impl LoginStart { + pub fn new(name: String) -> LoginServerBoundPacket { + let login_start = LoginStart { name }; + + LoginServerBoundPacket::LoginStart(login_start) + } +} + +impl Packet for LoginStart { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_string(&self.name, LOGIN_MAX_LENGTH) + } + + fn decode(reader: &mut R) -> Result { + let name = reader.read_string(LOGIN_MAX_LENGTH)?; + + Ok(LoginStart { name }) + } +} + +pub struct EncryptionResponse { + pub shared_secret: Vec, + pub verify_token: Vec, +} + +impl EncryptionResponse { + pub fn new(shared_secret: Vec, verify_token: Vec) -> LoginServerBoundPacket { + let encryption_response = EncryptionResponse { + shared_secret, + verify_token, + }; + + LoginServerBoundPacket::EncryptionResponse(encryption_response) + } +} + +impl Packet for EncryptionResponse { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_byte_array(&self.shared_secret)?; + writer.write_byte_array(&self.verify_token)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let shared_secret = reader.read_byte_array()?; + let verify_token = reader.read_byte_array()?; + + Ok(EncryptionResponse { + shared_secret, + verify_token, + }) + } +} + +pub struct LoginPluginResponse { + pub message_id: i32, + pub successful: bool, + pub data: Vec, +} + +impl LoginPluginResponse { + pub fn new(message_id: i32, successful: bool, data: Vec) -> LoginServerBoundPacket { + let login_plugin_response = LoginPluginResponse { + message_id, + successful, + data, + }; + + LoginServerBoundPacket::LoginPluginResponse(login_plugin_response) + } +} + +impl Packet for LoginPluginResponse { + type Output = Self; + + fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { + writer.write_var_i32(self.message_id)?; + writer.write_bool(self.successful)?; + writer.write_all(&self.data)?; + + Ok(()) + } + + fn decode(reader: &mut R) -> Result { + let message_id = reader.read_var_i32()?; + let successful = reader.read_bool()?; + + let mut data = Vec::new(); + reader.read_to_end(data.as_mut())?; + + Ok(LoginPluginResponse { + message_id, + successful, + data, + }) + } +} diff --git a/src/status.rs b/src/status.rs index 21595e1..9bc5cb5 100644 --- a/src/status.rs +++ b/src/status.rs @@ -1,4 +1,4 @@ -use crate::{DecodeError, EncodeError, Packet, PacketWrite}; +use crate::{DecodeError, EncodeError, Packet, PacketWrite, STRING_MAX_LENGTH}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use serde::{Deserialize, Serialize}; use std::io::{Read, Write}; @@ -143,7 +143,7 @@ impl Packet for StatusResponse { fn encode(&self, writer: &mut W) -> Result<(), EncodeError> { let json = serde_json::to_string(&self.server_status)?; - writer.write_string(&json)?; + writer.write_string(&json, STRING_MAX_LENGTH)?; Ok(()) }