Add own varint/varlong support

This commit is contained in:
Vladislavs Golubs 2020-04-15 03:59:03 +03:00
parent 3e86e216c9
commit e6a4cc6679
2 changed files with 103 additions and 6 deletions

View File

@ -11,12 +11,11 @@ keywords = ["minecraft", "protocol", "packet", "io"]
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
minecraft-protocol-derive = { path = "../protocol-derive" } minecraft-protocol-derive = { version = "0.0.0", path = "../protocol-derive" }
byteorder = "1" byteorder = "1"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
uuid = { version = "0.7", features = ["v4", "serde"] } uuid = { version = "0.7", features = ["v4", "serde"] }
mc-varint = { git = "https://github.com/luojia65/mc-varint" }
num-traits = "0.2" num-traits = "0.2"
num-derive = "0.2" num-derive = "0.2"
named-binary-tag = "0.2" named-binary-tag = "0.2"

View File

@ -3,11 +3,10 @@
//! Information about protocol can be found at https://wiki.vg/Protocol. //! Information about protocol can be found at https://wiki.vg/Protocol.
use io::Error as IoError; use io::Error as IoError;
use std::io; use std::io;
use std::io::{Read, Write}; use std::io::{Cursor, Read, Write};
use std::string::FromUtf8Error; use std::string::FromUtf8Error;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use mc_varint::{VarIntRead, VarIntWrite};
use serde_json::error::Error as JsonError; use serde_json::error::Error as JsonError;
use uuid::parser::ParseError as UuidParseError; use uuid::parser::ParseError as UuidParseError;
@ -94,6 +93,9 @@ pub enum DecodeError {
TagDecodeError { TagDecodeError {
tag_decode_error: TagDecodeError, tag_decode_error: TagDecodeError,
}, },
VarIntTooLong {
max_bytes: usize,
},
} }
impl From<IoError> for DecodeError { impl From<IoError> for DecodeError {
@ -136,6 +138,56 @@ trait Decoder {
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError>; fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError>;
} }
macro_rules! write_signed_var_int (
($type: ident, $name: ident) => (
fn $name(&mut self, mut value: $type) -> Result<(), EncodeError> {
loop {
let mut byte = (value & 0b01111111) as u8;
value = value >> 7;
if value != 0 {
byte |= 0b10000000;
}
self.write_u8(byte)?;
if value == 0 {
break;
}
}
Ok(())
}
)
);
macro_rules! read_signed_var_int (
($type: ident, $name: ident, $max_bytes: expr) => (
fn $name(&mut self) -> Result<$type, DecodeError> {
let mut bytes = 0;
let mut output = 0;
loop {
let byte = self.read_u8()?;
let value = (byte & 0b01111111) as $type;
output |= value << 7 * bytes;
bytes += 1;
if bytes > $max_bytes {
return Err(DecodeError::VarIntTooLong { max_bytes: $max_bytes })
}
if (byte & 0b10000000) == 0 {
break;
}
}
Ok(output)
}
);
);
/// Trait adds additional helper methods for `Write` to write protocol data. /// Trait adds additional helper methods for `Write` to write protocol data.
trait EncoderWriteExt { trait EncoderWriteExt {
fn write_bool(&mut self, value: bool) -> Result<(), EncodeError>; fn write_bool(&mut self, value: bool) -> Result<(), EncodeError>;
@ -149,6 +201,10 @@ trait EncoderWriteExt {
fn write_enum<T: ToPrimitive>(&mut self, value: &T) -> Result<(), EncodeError>; fn write_enum<T: ToPrimitive>(&mut self, value: &T) -> Result<(), EncodeError>;
fn write_compound_tag(&mut self, value: &CompoundTag) -> Result<(), EncodeError>; fn write_compound_tag(&mut self, value: &CompoundTag) -> Result<(), EncodeError>;
fn write_var_i32(&mut self, value: i32) -> Result<(), EncodeError>;
fn write_var_i64(&mut self, value: i64) -> Result<(), EncodeError>;
} }
/// Trait adds additional helper methods for `Read` to read protocol data. /// Trait adds additional helper methods for `Read` to read protocol data.
@ -164,6 +220,10 @@ trait DecoderReadExt {
fn read_enum<T: FromPrimitive>(&mut self) -> Result<T, DecodeError>; fn read_enum<T: FromPrimitive>(&mut self) -> Result<T, DecodeError>;
fn read_compound_tag(&mut self) -> Result<CompoundTag, DecodeError>; fn read_compound_tag(&mut self) -> Result<CompoundTag, DecodeError>;
fn read_var_i32(&mut self) -> Result<i32, DecodeError>;
fn read_var_i64(&mut self) -> Result<i64, DecodeError>;
} }
impl<W: Write> EncoderWriteExt for W { impl<W: Write> EncoderWriteExt for W {
@ -213,6 +273,9 @@ impl<W: Write> EncoderWriteExt for W {
Ok(()) Ok(())
} }
write_signed_var_int!(i32, write_var_i32);
write_signed_var_int!(i64, write_var_i64);
} }
impl<R: Read> DecoderReadExt for R { impl<R: Read> DecoderReadExt for R {
@ -263,6 +326,9 @@ impl<R: Read> DecoderReadExt for R {
fn read_compound_tag(&mut self) -> Result<CompoundTag, DecodeError> { fn read_compound_tag(&mut self) -> Result<CompoundTag, DecodeError> {
Ok(nbt::decode::read_compound_tag(self)?) Ok(nbt::decode::read_compound_tag(self)?)
} }
read_signed_var_int!(i32, read_var_i32, 5);
read_signed_var_int!(i64, read_var_i64, 10);
} }
impl Encoder for u8 { impl Encoder for u8 {
@ -481,7 +547,7 @@ macro_rules! impl_json_encoder_decoder (
mod var_int { mod var_int {
use crate::{DecodeError, EncodeError}; use crate::{DecodeError, EncodeError};
use mc_varint::{VarIntRead, VarIntWrite}; use crate::{DecoderReadExt, EncoderWriteExt};
use std::io::{Read, Write}; use std::io::{Read, Write};
pub fn encode<W: Write>(value: &i32, writer: &mut W) -> Result<(), EncodeError> { pub fn encode<W: Write>(value: &i32, writer: &mut W) -> Result<(), EncodeError> {
@ -497,7 +563,7 @@ mod var_int {
mod var_long { mod var_long {
use crate::{DecodeError, EncodeError}; use crate::{DecodeError, EncodeError};
use mc_varint::{VarIntRead, VarIntWrite}; use crate::{DecoderReadExt, EncoderWriteExt};
use std::io::{Read, Write}; use std::io::{Read, Write};
pub fn encode<W: Write>(value: &i64, writer: &mut W) -> Result<(), EncodeError> { pub fn encode<W: Write>(value: &i64, writer: &mut W) -> Result<(), EncodeError> {
@ -550,3 +616,35 @@ mod uuid_hyp_str {
Ok(uuid) Ok(uuid)
} }
} }
#[test]
fn test_read_variable_i32_2_bytes_value() {
let mut cursor = Cursor::new(vec![0b10101100, 0b00000010]);
let value = cursor.read_var_i32().unwrap();
assert_eq!(value, 300);
}
#[test]
fn test_read_variable_i32_5_bytes_value() {
let mut cursor = Cursor::new(vec![0xff, 0xff, 0xff, 0xff, 0x07]);
let value = cursor.read_var_i32().unwrap();
assert_eq!(value, 2147483647);
}
#[test]
fn test_write_variable_i32_2_bytes_value() {
let mut cursor = Cursor::new(Vec::with_capacity(5));
cursor.write_var_i32(300).unwrap();
assert_eq!(cursor.into_inner(), vec![0b10101100, 0b00000010]);
}
#[test]
fn test_write_variable_i32_5_bytes_value() {
let mut cursor = Cursor::new(Vec::with_capacity(5));
cursor.write_var_i32(2147483647).unwrap();
assert_eq!(cursor.into_inner(), vec![0xff, 0xff, 0xff, 0xff, 0x07]);
}