#[cfg(test)] mod tests; pub mod data; pub mod packet; pub mod prelude { pub use crate::{DataReader, DataWriter}; } pub use crate::{ data::{DataReader, DataWriter}, packet::Packet, }; use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression}; use std::{ error::Error, fmt, io::{Read, Write}, net::{TcpStream, ToSocketAddrs}, usize }; /// Minecraft protocol error #[derive(Debug)] pub enum ProtocolError { AddressParseError, DataRanOutError, StringParseError, StreamConnectError, VarIntError, ReadError, WriteError, ZlibError, CloneError, ConnectionClosedError } impl fmt::Display for ProtocolError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "An protocol error occured") } } impl Error for ProtocolError {} /// Minecraft connection, wrapper for stream with compression pub struct MinecraftConnection { stream: T, compression: Option, compression_type: u32 } impl DataReader for MinecraftConnection { fn read_bytes(&mut self, size: usize) -> Result, ProtocolError> { let mut buf = vec![0; size]; match self.stream.read_exact(&mut buf) { Ok(_) => Ok(buf), Err(_) => Err(ProtocolError::ReadError), } } } impl DataWriter for MinecraftConnection { fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ProtocolError> { self.stream.write_all(bytes).map_err(|_| ProtocolError::WriteError) } } impl MinecraftConnection { /// Create new MinecraftConnection from stream pub fn new(stream: T) -> MinecraftConnection { MinecraftConnection { stream, compression: None, compression_type: 1, } } /// Set compression threshold pub fn set_compression(&mut self, threshold: Option) { self.compression = threshold; } /// Get compression threshold pub fn compression(&self) -> Option { self.compression } /// Set compression type /// /// `compression_type` is integer from 0 (none) to 9 (longest) /// 1 is fast compression /// 6 is normal compression pub fn set_compression_type(&mut self, compression_type: u32) { self.compression_type = compression_type; } /// Get compression type /// /// `compression_type` is integer from 0 (none) to 9 (longest) /// 1 is fast compression /// 6 is normal compression pub fn compression_type(&self) -> u32 { self.compression_type } /// Get mutable reference of stream pub fn get_mut(&mut self) -> &mut T { &mut self.stream } /// Get immutable reference of stream pub fn get_ref(&self) -> &T { &self.stream } /// Read [`Packet`](Packet) from connection pub fn read_packet(&mut self) -> Result { read_packet(&mut self.stream, self.compression) } /// Write [`Packet`](Packet) to connection pub fn write_packet(&mut self, packet: &Packet) -> Result<(), ProtocolError> { write_packet(&mut self.stream, self.compression, self.compression_type, packet) } } impl MinecraftConnection { /// Clone MinecraftConnection with compression and stream pub fn clone(&mut self) -> MinecraftConnection { MinecraftConnection { stream: self.stream.clone(), compression: self.compression, compression_type: self.compression_type, } } } impl MinecraftConnection { /// Connect to Minecraft Server with TcpStream pub fn connect(addr: impl ToSocketAddrs) -> Result, ProtocolError> { let addr = match addr.to_socket_addrs() { Ok(mut i) => match i.next() { Some(i) => i, None => return Err(ProtocolError::AddressParseError), }, Err(_) => return Err(ProtocolError::AddressParseError), }; let stream: TcpStream = match TcpStream::connect(&addr) { Ok(i) => i, Err(_) => return Err(ProtocolError::StreamConnectError), }; Ok(MinecraftConnection { stream, compression: None, compression_type: 1, }) } /// Close TcpStream pub fn close(&mut self) { let _ = self.stream.shutdown(std::net::Shutdown::Both); } /// Try clone MinecraftConnection with compression and stream pub fn try_clone(&self) -> Result, ProtocolError> { match self.stream.try_clone() { Ok(stream) => Ok(MinecraftConnection { stream, compression: self.compression, compression_type: self.compression_type, }), _ => Err(ProtocolError::CloneError), } } } fn compress_zlib(bytes: &[u8], compression: u32) -> Result, ProtocolError> { let mut encoder = ZlibEncoder::new(Vec::new(), Compression::new(compression)); encoder.write_all(bytes).or(Err(ProtocolError::ZlibError))?; encoder.finish().or(Err(ProtocolError::ZlibError)) } fn decompress_zlib(bytes: &[u8]) -> Result, ProtocolError> { let mut decoder = ZlibDecoder::new(bytes); let mut output = Vec::new(); decoder .read_to_end(&mut output) .or(Err(ProtocolError::ZlibError))?; Ok(output) } /// MinecraftConnection shorter alias pub type MCConn = MinecraftConnection; /// MinecraftConnection\ shorter alias pub type MCConnTcp = MinecraftConnection; /// Read [`Packet`](Packet) from stream /// /// `compression` here is atomic usize /// usize::MAX means that compression is disabled /// /// `ordering` is order how to load atomic pub fn read_packet( stream: &mut T, compression: Option ) -> Result { let mut data: Vec; let packet_length = stream.read_usize_varint_size()?; if compression.is_some() { let data_length = stream.read_usize_varint_size()?; data = stream.read_bytes(packet_length.0 - data_length.1)?; if data_length.0 != 0 { data = decompress_zlib(&data)?; } } else { data = stream.read_bytes(packet_length.0 as usize)?; } Ok(Packet::from_data(&data)?) } /// Write [`Packet`](Packet) to stream /// /// `compression` here is usize /// usize::MAX means that compression is disabled /// /// `ordering` is order how to load atomic /// /// `compression_type` is integer from 0 (none) to 9 (longest) /// 1 is fast compression /// 6 is normal compression pub fn write_packet( stream: &mut T, compression: Option, compression_type: u32, packet: &Packet, ) -> Result<(), ProtocolError> { let mut buf = Vec::new(); let mut data_buf = Vec::new(); data_buf.write_varint(packet.id() as i32)?; data_buf.write_bytes(packet.get_bytes())?; if let Some(compression) = compression { let mut packet_buf = Vec::new(); if data_buf.len() >= compression { let compressed_data = compress_zlib(&data_buf, compression_type)?; packet_buf.write_varint(data_buf.len() as i32)?; packet_buf .write_all(&compressed_data) .or(Err(ProtocolError::WriteError))?; } else { packet_buf.write_varint(0)?; packet_buf.write_bytes(&data_buf)?; } buf.write_varint(packet_buf.len() as i32)?; buf.write_bytes(&packet_buf)?; } else { buf.write_varint(data_buf.len() as i32)?; buf.write_bytes(&data_buf)?; } stream.write_bytes(&buf)?; Ok(()) }