-
Notifications
You must be signed in to change notification settings - Fork 10
Add missing derives, favicon field and new Packet struct.
#21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
0b6185a
0506fc9
37c6837
b0034dd
fab8ee7
12ac52d
12f1086
71e34f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| use std::io::{self, Read, Write}; | ||
|
|
||
| use flate2::read::ZlibDecoder; | ||
| use flate2::write::ZlibEncoder; | ||
| use flate2::Compression; | ||
| use minecraft_protocol_derive::{Decoder, Encoder}; | ||
|
|
||
| use crate::decoder::{Decoder, DecoderReadExt}; | ||
| use crate::encoder::{Encoder, EncoderWriteExt}; | ||
| use crate::error::{DecodeError, EncodeError}; | ||
|
|
||
| #[derive(Debug, Clone)] | ||
| pub struct Packet { | ||
| pub id: i32, | ||
| pub data: Vec<u8>, | ||
| } | ||
|
|
||
| impl Packet { | ||
| pub fn encode<W: Write>( | ||
| self, | ||
| writer: &mut W, | ||
| compression_threshold: Option<i32>, | ||
| ) -> Result<(), EncodeError> { | ||
| let mut buf = Vec::new(); | ||
| let packet = RawPacket { | ||
| id: self.id, | ||
| data: self.data, | ||
| }; | ||
| if let Some(threshold) = compression_threshold { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nth: match are more preferable when you use else branch |
||
| CompressedRawPacket { packet, threshold }.encode(&mut buf)?; | ||
| } else { | ||
| packet.encode(&mut buf)?; | ||
| } | ||
|
|
||
| writer.write_var_i32(buf.len() as i32)?; | ||
| writer.write_all(&buf)?; | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| pub fn decode<R: Read>(reader: &mut R, compressed: bool) -> Result<Packet, DecodeError> { | ||
| let len = match reader.read_var_i32() { | ||
| Ok(len) => len as usize, | ||
| Err(DecodeError::IoError { io_error }) | ||
| if io_error.kind() == io::ErrorKind::UnexpectedEof => | ||
| { | ||
| return Err(DecodeError::Incomplete { bytes_needed: 1 }) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not correct because read var int may vary from 1 to 4 bytes
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is used only as an indication, so it should be read as “at least 1”. If more are needed, it will happen in the next iteration.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it should be an |
||
| } | ||
| Err(err) => return Err(err.into()), | ||
| }; | ||
|
|
||
| let mut buf = Vec::with_capacity(len); | ||
| let bytes_read = reader.take(len as u64).read_to_end(&mut buf)?; | ||
|
|
||
| if bytes_read != len { | ||
| return Err(DecodeError::Incomplete { | ||
| bytes_needed: len - bytes_read, | ||
| }); | ||
| } | ||
|
|
||
| let RawPacket { id, data } = if compressed { | ||
| CompressedRawPacket::decode(&mut buf.as_slice())? | ||
| } else { | ||
| RawPacket::decode(&mut buf.as_slice())? | ||
| }; | ||
|
|
||
| Ok(Self { id, data }) | ||
| } | ||
| } | ||
|
|
||
| #[derive(Debug, Clone, Encoder, Decoder)] | ||
| struct RawPacket { | ||
| #[data_type(with = "var_int")] | ||
| pub id: i32, | ||
| #[data_type(with = "rest")] | ||
| pub data: Vec<u8>, | ||
| } | ||
|
|
||
| #[derive(Debug, Clone)] | ||
| struct CompressedRawPacket { | ||
| packet: RawPacket, | ||
| threshold: i32, | ||
| } | ||
|
|
||
| impl Encoder for CompressedRawPacket { | ||
| fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> { | ||
| let mut packet_buf = Vec::new(); | ||
| self.packet.encode(&mut packet_buf)?; | ||
|
|
||
| let data_len = packet_buf.len() as i32; | ||
| if self.threshold >= 0 && data_len > self.threshold { | ||
| writer.write_var_i32(data_len)?; | ||
| let mut encoder = ZlibEncoder::new(writer, Compression::default()); | ||
| encoder.write_all(&packet_buf)?; | ||
| encoder.finish()?; | ||
| } else { | ||
| writer.write_var_i32(0)?; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why zero?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://wiki.vg/Protocol#With_compression
|
||
| writer.write_all(&packet_buf)?; | ||
| }; | ||
|
|
||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| impl Decoder for CompressedRawPacket { | ||
| type Output = RawPacket; | ||
|
|
||
| fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> { | ||
| let data_len = reader.read_var_i32()? as usize; | ||
| let packet = if data_len == 0 { | ||
| RawPacket::decode(reader)? | ||
| } else { | ||
| let mut decompressed = Vec::with_capacity(data_len); | ||
| ZlibDecoder::new(reader).read_to_end(&mut decompressed)?; | ||
|
|
||
| if decompressed.len() != data_len { | ||
| return Err(DecodeError::DecompressionError); | ||
| } | ||
|
|
||
| RawPacket::decode(&mut decompressed.as_slice())? | ||
| }; | ||
|
|
||
| Ok(packet) | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use std::convert::TryInto; | ||
|
|
||
| use super::*; | ||
|
|
||
| use crate::version::v1_14_4::status::*; | ||
|
|
||
| const PING_REQUEST_BYTES: &'static [u8] = | ||
| include_bytes!("../test/packet/status/ping_request.dat"); | ||
|
|
||
| fn ping_request_packet_bytes() -> Vec<u8> { | ||
| let len = (1 + PING_REQUEST_BYTES.len()).try_into().unwrap(); | ||
| let mut vec = vec![len, 1]; | ||
| vec.extend(PING_REQUEST_BYTES); | ||
| vec | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_uncompressed_packet_encode() { | ||
| let ping_request = PingRequest { | ||
| time: 1577735845610, | ||
| }; | ||
|
|
||
| let mut data = Vec::new(); | ||
| ping_request.encode(&mut data).unwrap(); | ||
|
|
||
| let packet = Packet { id: 1, data }; | ||
|
|
||
| let mut vec = Vec::new(); | ||
| packet.encode(&mut vec, None).unwrap(); | ||
|
|
||
| assert_eq!(vec, ping_request_packet_bytes()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_uncompressed_packet_decode() { | ||
| let vec = ping_request_packet_bytes(); | ||
| let packet = Packet::decode(&mut vec.as_slice(), false).unwrap(); | ||
|
|
||
| assert_eq!(packet.id, 1); | ||
| assert_eq!(packet.data, PING_REQUEST_BYTES); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_compressed_packet_encode_decode() { | ||
| let ping_request = PingRequest { | ||
| time: 1577735845610, | ||
| }; | ||
|
|
||
| let mut data = Vec::new(); | ||
| ping_request.encode(&mut data).unwrap(); | ||
|
|
||
| let packet = Packet { id: 1, data }; | ||
|
|
||
| let mut vec = Vec::new(); | ||
| packet.encode(&mut vec, Some(0)).unwrap(); | ||
|
|
||
| let packet = Packet::decode(&mut vec.as_slice(), true).unwrap(); | ||
|
|
||
| assert_eq!(packet.id, 1); | ||
| assert_eq!(packet.data, PING_REQUEST_BYTES); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When you will intergrate protocol in a network library like tokio this structure would not be necessary. There are more steps in packet codec. Like encryption and compression.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have an example for how this works?
I am currently converting a
tokio::net::TcpStreamintostd::net::TcpStreamso I can usePacket::decodewith it, so this would definitely be an improvement over what I have now.