MeexReay a91d262271
Some checks failed
Rust / build (push) Failing after 35s
varint alias functions
2025-05-01 14:36:30 +03:00

275 lines
7.7 KiB
Rust
Executable File

#[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<T: Read + Write> {
stream: T,
compression: Option<usize>,
compression_type: u32
}
impl<T: Read + Write> DataReader for MinecraftConnection<T> {
fn read_bytes(&mut self, size: usize) -> Result<Vec<u8>, ProtocolError> {
let mut buf = vec![0; size];
match self.stream.read_exact(&mut buf) {
Ok(_) => Ok(buf),
Err(_) => Err(ProtocolError::ReadError),
}
}
}
impl<T: Read + Write> DataWriter for MinecraftConnection<T> {
fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ProtocolError> {
self.stream.write_all(bytes).map_err(|_| ProtocolError::WriteError)
}
}
impl<T: Read + Write> MinecraftConnection<T> {
/// Create new MinecraftConnection from stream
pub fn new(stream: T) -> MinecraftConnection<T> {
MinecraftConnection {
stream,
compression: None,
compression_type: 1,
}
}
/// Set compression threshold
pub fn set_compression(&mut self, threshold: Option<usize>) {
self.compression = threshold;
}
/// Get compression threshold
pub fn compression(&self) -> Option<usize> {
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<Packet, ProtocolError> {
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<T: Read + Write + Clone> MinecraftConnection<T> {
/// Clone MinecraftConnection with compression and stream
pub fn clone(&mut self) -> MinecraftConnection<T> {
MinecraftConnection {
stream: self.stream.clone(),
compression: self.compression,
compression_type: self.compression_type,
}
}
}
impl MinecraftConnection<TcpStream> {
/// Connect to Minecraft Server with TcpStream
pub fn connect(addr: impl ToSocketAddrs) -> Result<MinecraftConnection<TcpStream>, 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<MinecraftConnection<TcpStream>, 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<Vec<u8>, 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<Vec<u8>, 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<T> = MinecraftConnection<T>;
/// MinecraftConnection\<TcpStream\> shorter alias
pub type MCConnTcp = MinecraftConnection<TcpStream>;
/// 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<T: Read>(
stream: &mut T,
compression: Option<usize>
) -> Result<Packet, ProtocolError> {
let mut data: Vec<u8>;
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<T: Write>(
stream: &mut T,
compression: Option<usize>,
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(())
}