Add login server bound packets

This commit is contained in:
Vladislav Golub 2019-12-24 19:44:56 +03:00
parent acc098bc4e
commit 3d6dd8ed87
3 changed files with 235 additions and 15 deletions

View File

@ -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<IoError> 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<String, DecodeError>;
fn read_bool(&mut self) -> Result<bool, DecodeError>;
fn read_string(&mut self, max_length: u32) -> Result<String, DecodeError>;
fn read_byte_array(&mut self) -> Result<Vec<u8>, 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<R: Read> PacketRead for R {
fn read_string(&mut self) -> Result<String, DecodeError> {
fn read_bool(&mut self) -> Result<bool, DecodeError> {
match self.read_u8()? {
0 => Ok(false),
1 => Ok(true),
_ => Err(DecodeError::NonBoolValue),
}
}
fn read_string(&mut self, max_length: u32) -> Result<String, DecodeError> {
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<R: Read> PacketRead for R {
Ok(String::from_utf8(buf)?)
}
fn read_byte_array(&mut self) -> Result<Vec<u8>, DecodeError> {
let length = self.read_var_u32()?;
let mut buf = vec![0; length as usize];
self.read_exact(&mut buf)?;
Ok(buf)
}
}
impl<W: Write> 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<W: Write> 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(())
}
}

162
src/login.rs Normal file
View File

@ -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<R: Read>(type_id: u8, reader: &mut R) -> Result<Self, DecodeError> {
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<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
writer.write_string(&self.name, LOGIN_MAX_LENGTH)
}
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
let name = reader.read_string(LOGIN_MAX_LENGTH)?;
Ok(LoginStart { name })
}
}
pub struct EncryptionResponse {
pub shared_secret: Vec<u8>,
pub verify_token: Vec<u8>,
}
impl EncryptionResponse {
pub fn new(shared_secret: Vec<u8>, verify_token: Vec<u8>) -> LoginServerBoundPacket {
let encryption_response = EncryptionResponse {
shared_secret,
verify_token,
};
LoginServerBoundPacket::EncryptionResponse(encryption_response)
}
}
impl Packet for EncryptionResponse {
type Output = Self;
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
writer.write_byte_array(&self.shared_secret)?;
writer.write_byte_array(&self.verify_token)?;
Ok(())
}
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
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<u8>,
}
impl LoginPluginResponse {
pub fn new(message_id: i32, successful: bool, data: Vec<u8>) -> LoginServerBoundPacket {
let login_plugin_response = LoginPluginResponse {
message_id,
successful,
data,
};
LoginServerBoundPacket::LoginPluginResponse(login_plugin_response)
}
}
impl Packet for LoginPluginResponse {
type Output = Self;
fn encode<W: Write>(&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<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
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,
})
}
}

View File

@ -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<W: Write>(&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(())
}