#[cfg(test)] mod tests; pub mod data_buffer; pub mod packet; pub mod zigzag; pub use crate::{ data_buffer::{DataBufferReader, DataBufferWriter}, packet::Packet, }; use bytebuffer::ByteBuffer; use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression}; use std::{ error::Error, fmt, io::{Read, Write}, net::{TcpStream, ToSocketAddrs}, sync::{ atomic::{AtomicUsize, Ordering}, Arc, }, }; /// Minecraft protocol error #[derive(Debug)] pub enum ProtocolError { AddressParseError, DataRanOutError, StringParseError, StreamConnectError, VarIntError, ReadError, WriteError, ZlibError, UnsignedShortError, CloneError, } 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: Arc, compression_type: u32, } impl MinecraftConnection { /// Connect to Minecraft Server with TcpStream pub fn connect(addr: &str) -> 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: Arc::new(AtomicUsize::new(usize::MAX)), compression_type: 1, }) } /// Close TcpStream pub fn close(&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.clone(), compression_type: 1, }), _ => Err(ProtocolError::CloneError), } } } impl DataBufferReader 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 DataBufferWriter for MinecraftConnection { fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ProtocolError> { match self.stream.write_all(bytes) { Ok(_) => Ok(()), Err(_) => Err(ProtocolError::WriteError), } } } impl MinecraftConnection { /// Create new MinecraftConnection from stream pub fn new(stream: T) -> MinecraftConnection { MinecraftConnection { stream, compression: Arc::new(AtomicUsize::new(usize::MAX)), compression_type: 1, } } /// Set compression threshold pub fn set_compression(&mut self, threshold: Option) { self.compression.store( match threshold { Some(t) => t, None => usize::MAX, }, Ordering::Relaxed, ); } /// Get compression threshold pub fn compression(&self) -> Option { let threshold = self.compression.load(Ordering::Relaxed); if threshold == usize::MAX { None } else { Some(threshold) } } /// 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_atomic( &mut self.stream, self.compression.clone(), Ordering::Relaxed, ) } /// Write [`Packet`](Packet) to connection pub fn write_packet(&mut self, packet: &Packet) -> Result<(), ProtocolError> { write_packet_atomic( &mut self.stream, self.compression.clone(), Ordering::Relaxed, 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.clone(), compression_type: self.compression_type, } } } 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_atomic( stream: &mut T, compression: Arc, ordering: Ordering, ) -> Result { let mut data: Vec; let packet_length = stream.read_usize_varint_size()?; let compress_threashold = compression.load(ordering); if compress_threashold != usize::MAX { 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)?; } Ok(Packet::from_data(&data)?) } /// Write [`Packet`](Packet) to stream /// /// `compression` here is atomic 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_atomic( stream: &mut T, compression: Arc, ordering: Ordering, compression_type: u32, packet: &Packet, ) -> Result<(), ProtocolError> { let mut buf = ByteBuffer::new(); let mut data_buf = ByteBuffer::new(); data_buf.write_u8_varint(packet.id())?; data_buf.write_buffer(packet.buffer())?; let compress_threshold = compression.load(ordering); if compress_threshold != usize::MAX { let mut packet_buf = ByteBuffer::new(); if data_buf.len() >= compress_threshold { let compressed_data = compress_zlib(data_buf.as_bytes(), compression_type)?; packet_buf.write_usize_varint(data_buf.len())?; packet_buf .write_all(&compressed_data) .or(Err(ProtocolError::WriteError))?; } else { packet_buf.write_usize_varint(0)?; packet_buf.write_buffer(&data_buf)?; } buf.write_usize_varint(packet_buf.len())?; buf.write_buffer(&packet_buf)?; } else { buf.write_usize_varint(data_buf.len())?; buf.write_buffer(&data_buf)?; } stream.write_buffer(&buf)?; Ok(()) }