rust_mc_proto/
lib.rs

1#[cfg(test)]
2mod tests;
3
4pub mod data;
5pub mod packet;
6
7pub mod prelude {
8    pub use crate::{DataReader, DataWriter};
9}
10
11pub use crate::{
12    data::{DataReader, DataWriter},
13    packet::Packet,
14};
15
16use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};
17use std::{
18    error::Error, fmt, io::{Read, Write}, net::{TcpStream, ToSocketAddrs}, usize
19};
20
21/// Minecraft protocol error
22#[derive(Debug)]
23pub enum ProtocolError {
24    AddressParseError,
25    DataRanOutError,
26    StringParseError,
27    StreamConnectError,
28    VarIntError,
29    ReadError,
30    WriteError,
31    ZlibError,
32    CloneError,
33    ConnectionClosedError
34}
35
36impl fmt::Display for ProtocolError {
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        write!(f, "An protocol error occured")
39    }
40}
41
42impl Error for ProtocolError {}
43
44/// Minecraft connection, wrapper for stream with compression
45pub struct MinecraftConnection<T: Read + Write> {
46    stream: T,
47    compression: Option<usize>,
48    compression_type: u32
49}
50
51impl<T: Read + Write> DataReader for MinecraftConnection<T> {
52    fn read_bytes(&mut self, size: usize) -> Result<Vec<u8>, ProtocolError> {
53        let mut buf = vec![0; size];
54        match self.stream.read_exact(&mut buf) {
55            Ok(_) => Ok(buf),
56            Err(_) => Err(ProtocolError::ReadError),
57        }
58    }
59}
60
61impl<T: Read + Write> DataWriter for MinecraftConnection<T> {
62    fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ProtocolError> {
63        self.stream.write_all(bytes).map_err(|_| ProtocolError::WriteError)
64    }
65}
66
67impl<T: Read + Write> MinecraftConnection<T> {
68    /// Create new MinecraftConnection from stream
69    pub fn new(stream: T) -> MinecraftConnection<T> {
70        MinecraftConnection {
71            stream,
72            compression: None,
73            compression_type: 1,
74        }
75    }
76
77    /// Set compression threshold
78    pub fn set_compression(&mut self, threshold: Option<usize>) {
79        self.compression = threshold;
80    }
81
82    /// Get compression threshold
83    pub fn compression(&self) -> Option<usize> {
84        self.compression
85    }
86
87    /// Set compression type
88    ///
89    /// `compression_type` is integer from 0 (none) to 9 (longest)
90    /// 1 is fast compression
91    /// 6 is normal compression
92    pub fn set_compression_type(&mut self, compression_type: u32) {
93        self.compression_type = compression_type;
94    }
95
96    /// Get compression type
97    ///
98    /// `compression_type` is integer from 0 (none) to 9 (longest)
99    /// 1 is fast compression
100    /// 6 is normal compression
101    pub fn compression_type(&self) -> u32 {
102        self.compression_type
103    }
104
105    /// Get mutable reference of stream
106    pub fn get_mut(&mut self) -> &mut T {
107        &mut self.stream
108    }
109
110    /// Get immutable reference of stream
111    pub fn get_ref(&self) -> &T {
112        &self.stream
113    }
114
115    /// Read [`Packet`](Packet) from connection
116    pub fn read_packet(&mut self) -> Result<Packet, ProtocolError> {
117        read_packet(&mut self.stream, self.compression)
118    }
119
120    /// Write [`Packet`](Packet) to connection
121    pub fn write_packet(&mut self, packet: &Packet) -> Result<(), ProtocolError> {
122        write_packet(&mut self.stream, self.compression, self.compression_type, packet)
123    }
124}
125
126impl<T: Read + Write + Clone> MinecraftConnection<T> {
127    /// Clone MinecraftConnection with compression and stream
128    pub fn clone(&mut self) -> MinecraftConnection<T> {
129        MinecraftConnection {
130            stream: self.stream.clone(),
131            compression: self.compression,
132            compression_type: self.compression_type,
133        }
134    }
135}
136
137impl MinecraftConnection<TcpStream> {
138    /// Connect to Minecraft Server with TcpStream
139    pub fn connect(addr: impl ToSocketAddrs) -> Result<MinecraftConnection<TcpStream>, ProtocolError> {
140        let addr = match addr.to_socket_addrs() {
141            Ok(mut i) => match i.next() {
142                Some(i) => i,
143                None => return Err(ProtocolError::AddressParseError),
144            },
145            Err(_) => return Err(ProtocolError::AddressParseError),
146        };
147
148        let stream: TcpStream = match TcpStream::connect(&addr) {
149            Ok(i) => i,
150            Err(_) => return Err(ProtocolError::StreamConnectError),
151        };
152
153        Ok(MinecraftConnection {
154            stream,
155            compression: None,
156            compression_type: 1,
157        })
158    }
159
160    /// Close TcpStream
161    pub fn close(&mut self) {
162        let _ = self.stream.shutdown(std::net::Shutdown::Both);
163    }
164
165    /// Try clone MinecraftConnection with compression and stream
166    pub fn try_clone(&self) -> Result<MinecraftConnection<TcpStream>, ProtocolError> {
167        match self.stream.try_clone() {
168            Ok(stream) => Ok(MinecraftConnection {
169                stream,
170                compression: self.compression,
171                compression_type: self.compression_type,
172            }),
173            _ => Err(ProtocolError::CloneError),
174        }
175    }
176}
177
178fn compress_zlib(bytes: &[u8], compression: u32) -> Result<Vec<u8>, ProtocolError> {
179    let mut encoder = ZlibEncoder::new(Vec::new(), Compression::new(compression));
180    encoder.write_all(bytes).or(Err(ProtocolError::ZlibError))?;
181    encoder.finish().or(Err(ProtocolError::ZlibError))
182}
183
184fn decompress_zlib(bytes: &[u8]) -> Result<Vec<u8>, ProtocolError> {
185    let mut decoder = ZlibDecoder::new(bytes);
186    let mut output = Vec::new();
187    decoder
188        .read_to_end(&mut output)
189        .or(Err(ProtocolError::ZlibError))?;
190    Ok(output)
191}
192
193/// MinecraftConnection shorter alias
194pub type MCConn<T> = MinecraftConnection<T>;
195
196/// MinecraftConnection\<TcpStream\> shorter alias
197pub type MCConnTcp = MinecraftConnection<TcpStream>;
198
199
200/// Read [`Packet`](Packet) from stream
201///
202/// `compression` here is atomic usize
203/// usize::MAX means that compression is disabled
204///
205/// `ordering` is order how to load atomic
206pub fn read_packet<T: Read>(
207    stream: &mut T,
208    compression: Option<usize>
209) -> Result<Packet, ProtocolError> {
210    let mut data: Vec<u8>;
211
212    let packet_length = stream.read_usize_varint_size()?;
213
214    if compression.is_some() {
215        let data_length = stream.read_usize_varint_size()?;
216
217        data = stream.read_bytes(packet_length.0 - data_length.1)?;
218
219        if data_length.0 != 0 {
220            data = decompress_zlib(&data)?;
221        }
222    } else {
223        data = stream.read_bytes(packet_length.0 as usize)?;
224    }
225
226    Ok(Packet::from_data(&data)?)
227}
228
229/// Write [`Packet`](Packet) to stream
230///
231/// `compression` here is usize
232/// usize::MAX means that compression is disabled
233///
234/// `ordering` is order how to load atomic
235///
236/// `compression_type` is integer from 0 (none) to 9 (longest)
237/// 1 is fast compression
238/// 6 is normal compression
239pub fn write_packet<T: Write>(
240    stream: &mut T,
241    compression: Option<usize>,
242    compression_type: u32,
243    packet: &Packet,
244) -> Result<(), ProtocolError> {
245    let mut buf = Vec::new();
246
247    let mut data_buf = Vec::new();
248    data_buf.write_varint(packet.id() as i32)?;
249    data_buf.write_bytes(packet.get_bytes())?;
250
251    if let Some(compression) = compression {
252        let mut packet_buf = Vec::new();
253
254        if data_buf.len() >= compression {
255            let compressed_data = compress_zlib(&data_buf, compression_type)?;
256            packet_buf.write_varint(data_buf.len() as i32)?;
257            packet_buf
258                .write_all(&compressed_data)
259                .or(Err(ProtocolError::WriteError))?;
260        } else {
261            packet_buf.write_varint(0)?;
262            packet_buf.write_bytes(&data_buf)?;
263        }
264
265        buf.write_varint(packet_buf.len() as i32)?;
266        buf.write_bytes(&packet_buf)?;
267    } else {
268        buf.write_varint(data_buf.len() as i32)?;
269        buf.write_bytes(&data_buf)?;
270    }
271
272    stream.write_bytes(&buf)?;
273
274    Ok(())
275}