Add bitfield decoder
This commit is contained in:
parent
5a2b7e81fa
commit
2d8174678b
@ -16,3 +16,7 @@ proc-macro = true
|
|||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
syn = "1.0"
|
syn = "1.0"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
minecraft-protocol = { version = "0.1.0", path = "../protocol" }
|
||||||
|
byteorder = "1"
|
||||||
|
@ -5,7 +5,7 @@ use proc_macro2::TokenStream as TokenStream2;
|
|||||||
use proc_macro2::{Ident, Span};
|
use proc_macro2::{Ident, Span};
|
||||||
use quote::{quote, TokenStreamExt};
|
use quote::{quote, TokenStreamExt};
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, Lit, Meta, NestedMeta};
|
use syn::{parse_macro_input, Data, DeriveInput, Field, Fields, Lit, Meta, NestedMeta, Type};
|
||||||
|
|
||||||
#[proc_macro_derive(Packet, attributes(packet))]
|
#[proc_macro_derive(Packet, attributes(packet))]
|
||||||
pub fn derive_packet(input: proc_macro::TokenStream) -> TokenStream1 {
|
pub fn derive_packet(input: proc_macro::TokenStream) -> TokenStream1 {
|
||||||
@ -19,6 +19,8 @@ pub fn derive_packet(input: proc_macro::TokenStream) -> TokenStream1 {
|
|||||||
let encoder = impl_encoder_trait(name, fields);
|
let encoder = impl_encoder_trait(name, fields);
|
||||||
let decoder = impl_decoder_trait(name, fields);
|
let decoder = impl_decoder_trait(name, fields);
|
||||||
|
|
||||||
|
println!("{}", decoder);
|
||||||
|
|
||||||
TokenStream1::from(quote! {
|
TokenStream1::from(quote! {
|
||||||
#encoder
|
#encoder
|
||||||
|
|
||||||
@ -64,6 +66,10 @@ fn impl_encoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn impl_decoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 {
|
fn impl_decoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 {
|
||||||
|
let mut bitfields = vec![];
|
||||||
|
let total_fields = fields.iter().count();
|
||||||
|
let mut current = 0;
|
||||||
|
|
||||||
let decode = quote_field(fields, |field| {
|
let decode = quote_field(fields, |field| {
|
||||||
let name = &field.ident;
|
let name = &field.ident;
|
||||||
let ty = &field.ty;
|
let ty = &field.ty;
|
||||||
@ -71,27 +77,57 @@ fn impl_decoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 {
|
|||||||
let unparsed_meta = get_packet_field_meta(field);
|
let unparsed_meta = get_packet_field_meta(field);
|
||||||
let parsed_meta = parse_packet_field_meta(&unparsed_meta);
|
let parsed_meta = parse_packet_field_meta(&unparsed_meta);
|
||||||
|
|
||||||
// This is special case because max length are used only for strings.
|
let mut result = quote!();
|
||||||
if let Some(max_length) = parsed_meta.max_length {
|
|
||||||
return quote! {
|
// When we encounter a bitfield, we skip writing until we get
|
||||||
let #name = crate::DecoderReadExt::read_string(reader, #max_length)?;
|
// to the last bit field or the last field.
|
||||||
|
match &parsed_meta.bitfield {
|
||||||
|
Some(bitfield) => {
|
||||||
|
bitfields.push((field.clone(), bitfield.clone()));
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
result.append_all(quote_decoder_bitfield(&bitfields));
|
||||||
|
bitfields.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current += 1;
|
||||||
|
|
||||||
|
if !bitfields.is_empty() {
|
||||||
|
return if current == total_fields {
|
||||||
|
result.append_all(quote_decoder_bitfield(&bitfields));
|
||||||
|
bitfields.clear();
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
quote!()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match parsed_meta.module {
|
// This is special case because max length are used only for strings.
|
||||||
Some(module) => {
|
if let Some(max_length) = parsed_meta.max_length {
|
||||||
let module_ident = Ident::new(&module, Span::call_site());
|
result.append_all(quote! {
|
||||||
|
let #name = crate::DecoderReadExt::read_string(reader, #max_length)?;
|
||||||
|
});
|
||||||
|
|
||||||
quote! {
|
return result;
|
||||||
let #name = crate::#module_ident::decode(reader)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match &parsed_meta.module {
|
||||||
|
Some(module) => {
|
||||||
|
let module_ident = Ident::new(module, Span::call_site());
|
||||||
|
|
||||||
|
result.append_all(quote! {
|
||||||
|
let #name = crate::#module_ident::decode(reader)?;
|
||||||
|
})
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
quote! {
|
result.append_all(quote! {
|
||||||
let #name = <#ty as crate::Decoder>::decode(reader)?;
|
let #name = <#ty as crate::Decoder>::decode(reader)?;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return result;
|
||||||
});
|
});
|
||||||
|
|
||||||
let create = quote_field(fields, |field| {
|
let create = quote_field(fields, |field| {
|
||||||
@ -122,11 +158,148 @@ fn impl_decoder_trait(name: &Ident, fields: &Fields) -> TokenStream2 {
|
|||||||
struct PacketFieldMeta {
|
struct PacketFieldMeta {
|
||||||
module: Option<String>,
|
module: Option<String>,
|
||||||
max_length: Option<u16>,
|
max_length: Option<u16>,
|
||||||
|
bitfield: Option<Bitfield>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
struct Bitfield {
|
||||||
|
size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! decode_bitmask_value (
|
||||||
|
($value: expr, $name: ident) => (
|
||||||
|
fn $name(
|
||||||
|
result: &mut TokenStream2,
|
||||||
|
name: &Option<Ident>,
|
||||||
|
ty: &Type,
|
||||||
|
bitfield_size: usize,
|
||||||
|
current: usize,
|
||||||
|
signed: bool,
|
||||||
|
) {
|
||||||
|
let mask = (2 << (bitfield_size - 1)) - $value;
|
||||||
|
let bool = match ty {
|
||||||
|
Type::Path(type_path) => type_path
|
||||||
|
.path
|
||||||
|
.get_ident()
|
||||||
|
.expect("Failed to get ident")
|
||||||
|
.to_string() == "bool",
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if bool {
|
||||||
|
if current > 0 {
|
||||||
|
result.append_all(quote! {
|
||||||
|
let mut #name = ((encoded >> #current) & #mask) != 0;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result.append_all(quote! {
|
||||||
|
let mut #name = (encoded & #mask) != 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if current > 0 {
|
||||||
|
result.append_all(quote! {
|
||||||
|
let mut #name = ((encoded >> #current) & #mask) as #ty;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
result.append_all(quote! {
|
||||||
|
let mut #name = (encoded & #mask) as #ty;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if signed {
|
||||||
|
let subtract = mask + 1;
|
||||||
|
let overflow = subtract >> 1;
|
||||||
|
|
||||||
|
result.append_all(quote! {
|
||||||
|
if #name > #overflow as #ty {
|
||||||
|
#name -= #subtract as #ty;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
decode_bitmask_value!(1i64, decode_i64_bitmask_value);
|
||||||
|
decode_bitmask_value!(1i32, decode_i32_bitmask_value);
|
||||||
|
decode_bitmask_value!(1i16, decode_i16_bitmask_value);
|
||||||
|
decode_bitmask_value!(1i8, decode_i8_bitmask_value);
|
||||||
|
|
||||||
|
fn quote_decoder_bitfield(bitfields: &Vec<(Field, Bitfield)>) -> TokenStream2 {
|
||||||
|
if bitfields.is_empty() {
|
||||||
|
return quote!();
|
||||||
|
}
|
||||||
|
|
||||||
|
let total_size: usize = bitfields.iter().map(|(_, b)| b.size).sum();
|
||||||
|
|
||||||
|
let mut result = read_decoder_bitfield_quote(total_size);
|
||||||
|
let mut current = total_size;
|
||||||
|
|
||||||
|
for (field, bitfield) in bitfields {
|
||||||
|
current -= bitfield.size;
|
||||||
|
|
||||||
|
let name = &field.ident;
|
||||||
|
let ty = &field.ty;
|
||||||
|
|
||||||
|
let signed = match ty {
|
||||||
|
Type::Path(type_path) => type_path
|
||||||
|
.path
|
||||||
|
.get_ident()
|
||||||
|
.expect("Failed to get ident")
|
||||||
|
.to_string()
|
||||||
|
.starts_with("i"),
|
||||||
|
_ => panic!("Unexpected bitfield type"),
|
||||||
|
};
|
||||||
|
|
||||||
|
match total_size {
|
||||||
|
_ if total_size == 64 => {
|
||||||
|
decode_i64_bitmask_value(&mut result, name, ty, bitfield.size, current, signed)
|
||||||
|
}
|
||||||
|
_ if total_size == 32 => {
|
||||||
|
decode_i32_bitmask_value(&mut result, name, ty, bitfield.size, current, signed)
|
||||||
|
}
|
||||||
|
_ if total_size == 16 => {
|
||||||
|
decode_i16_bitmask_value(&mut result, name, ty, bitfield.size, current, signed)
|
||||||
|
}
|
||||||
|
_ => decode_i8_bitmask_value(&mut result, name, ty, bitfield.size, current, signed),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_decoder_bitfield_quote(total_size: usize) -> TokenStream2 {
|
||||||
|
match total_size {
|
||||||
|
_ if total_size == 64 => {
|
||||||
|
quote! {
|
||||||
|
let encoded = ::byteorder::ReadBytesExt::read_i64::<::byteorder::BigEndian>(reader)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ if total_size == 32 => {
|
||||||
|
quote! {
|
||||||
|
let encoded = ::byteorder::ReadBytesExt::read_i32::<::byteorder::BigEndian>(reader)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ if total_size == 16 => {
|
||||||
|
quote! {
|
||||||
|
let encoded = ::byteorder::ReadBytesExt::read_i16::<::byteorder::BigEndian>(reader)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ if total_size == 8 => {
|
||||||
|
quote! {
|
||||||
|
let encoded = ::byteorder::ReadBytesExt::read_i8(reader)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("Bitfield size must be aligned to 8, 16, 32 or 64"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_packet_field_meta(meta_list: &Vec<NestedMeta>) -> PacketFieldMeta {
|
fn parse_packet_field_meta(meta_list: &Vec<NestedMeta>) -> PacketFieldMeta {
|
||||||
let mut module = None;
|
let mut module = None;
|
||||||
let mut max_length = None;
|
let mut max_length = None;
|
||||||
|
let mut bitfield = None;
|
||||||
|
|
||||||
for meta in meta_list {
|
for meta in meta_list {
|
||||||
match meta {
|
match meta {
|
||||||
@ -147,14 +320,46 @@ fn parse_packet_field_meta(meta_list: &Vec<NestedMeta>) -> PacketFieldMeta {
|
|||||||
},
|
},
|
||||||
path => panic!(
|
path => panic!(
|
||||||
"Received unrecognized attribute : \"{}\"",
|
"Received unrecognized attribute : \"{}\"",
|
||||||
path.get_ident().unwrap()
|
path.get_ident().expect("Failed to get ident")
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
_ => panic!("Expected only named meta values"),
|
NestedMeta::Meta(Meta::List(meta_list)) if meta_list.path.is_ident("bitfield") => {
|
||||||
|
let mut size = None;
|
||||||
|
|
||||||
|
for meta in &meta_list.nested {
|
||||||
|
match meta {
|
||||||
|
NestedMeta::Meta(Meta::NameValue(named_meta)) => match &named_meta.path {
|
||||||
|
path if path.is_ident("size") => match &named_meta.lit {
|
||||||
|
Lit::Int(lit_int) => {
|
||||||
|
size = Some(
|
||||||
|
lit_int
|
||||||
|
.base10_parse::<usize>()
|
||||||
|
.expect("Failed to parse size attribute"),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => panic!("\"size\" attribute value must be integer"),
|
||||||
|
},
|
||||||
|
path => panic!(
|
||||||
|
"Received unrecognized attribute : \"{}\"",
|
||||||
|
path.get_ident().expect("Failed to get ident")
|
||||||
|
),
|
||||||
|
},
|
||||||
|
_ => panic!("Unexpected field meta"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PacketFieldMeta { module, max_length }
|
let size = size.unwrap_or_else(|| panic!("Size must be specified in bitfield"));
|
||||||
|
bitfield = Some(Bitfield { size })
|
||||||
|
}
|
||||||
|
_ => panic!("Unexpected field meta"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PacketFieldMeta {
|
||||||
|
module,
|
||||||
|
max_length,
|
||||||
|
bitfield,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_packet_field_meta(field: &Field) -> Vec<NestedMeta> {
|
fn get_packet_field_meta(field: &Field) -> Vec<NestedMeta> {
|
||||||
@ -171,7 +376,7 @@ fn get_packet_field_meta(field: &Field) -> Vec<NestedMeta> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn quote_field<F: Fn(&Field) -> TokenStream2>(fields: &Fields, func: F) -> TokenStream2 {
|
fn quote_field<F: FnMut(&Field) -> TokenStream2>(fields: &Fields, mut func: F) -> TokenStream2 {
|
||||||
let mut output = quote!();
|
let mut output = quote!();
|
||||||
|
|
||||||
match fields {
|
match fields {
|
||||||
|
60
protocol-derive/tests/bitfield_abilities.rs
Normal file
60
protocol-derive/tests/bitfield_abilities.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate minecraft_protocol_derive;
|
||||||
|
|
||||||
|
use minecraft_protocol::decoder::Decoder;
|
||||||
|
use minecraft_protocol::encoder::Encoder;
|
||||||
|
use minecraft_protocol::error::{DecodeError, EncodeError};
|
||||||
|
|
||||||
|
#[derive(Packet)]
|
||||||
|
pub struct Abilities {
|
||||||
|
#[packet(bitfield(size = 4))]
|
||||||
|
pub _unused: u8,
|
||||||
|
#[packet(bitfield(size = 1))]
|
||||||
|
pub creative_mode: bool,
|
||||||
|
#[packet(bitfield(size = 1))]
|
||||||
|
pub allow_flying: bool,
|
||||||
|
#[packet(bitfield(size = 1))]
|
||||||
|
pub flying: bool,
|
||||||
|
#[packet(bitfield(size = 1))]
|
||||||
|
pub invulnerable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::Abilities;
|
||||||
|
use minecraft_protocol::decoder::Decoder;
|
||||||
|
use minecraft_protocol::encoder::Encoder;
|
||||||
|
use minecraft_protocol::error::{DecodeError, EncodeError};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_abilities_i8_bitfield() {
|
||||||
|
let abilities = Abilities {
|
||||||
|
_unused: 0,
|
||||||
|
creative_mode: true,
|
||||||
|
allow_flying: true,
|
||||||
|
flying: true,
|
||||||
|
invulnerable: true,
|
||||||
|
};
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
|
||||||
|
abilities
|
||||||
|
.encode(&mut vec)
|
||||||
|
.expect("Failed to encode abilities");
|
||||||
|
assert_eq!(vec, [15]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_abilities_i8_bitfield() {
|
||||||
|
let value = 15i8;
|
||||||
|
|
||||||
|
let vec = value.to_be_bytes().to_vec();
|
||||||
|
let mut cursor = Cursor::new(vec);
|
||||||
|
|
||||||
|
let abilities = Abilities::decode(&mut cursor).expect("Failed to decode abilities");
|
||||||
|
assert!(abilities.invulnerable);
|
||||||
|
assert!(abilities.flying);
|
||||||
|
assert!(abilities.allow_flying);
|
||||||
|
assert!(abilities.creative_mode);
|
||||||
|
}
|
||||||
|
}
|
107
protocol-derive/tests/bitfield_half.rs
Normal file
107
protocol-derive/tests/bitfield_half.rs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate minecraft_protocol_derive;
|
||||||
|
|
||||||
|
use minecraft_protocol::decoder::Decoder;
|
||||||
|
use minecraft_protocol::encoder::Encoder;
|
||||||
|
use minecraft_protocol::error::{DecodeError, EncodeError};
|
||||||
|
|
||||||
|
#[derive(Packet)]
|
||||||
|
pub struct HalfLong {
|
||||||
|
#[packet(bitfield(size = 32))]
|
||||||
|
pub _unused: u32,
|
||||||
|
#[packet(bitfield(size = 32))]
|
||||||
|
pub value: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Packet)]
|
||||||
|
pub struct HalfInt {
|
||||||
|
#[packet(bitfield(size = 16))]
|
||||||
|
pub _unused: u16,
|
||||||
|
#[packet(bitfield(size = 16))]
|
||||||
|
pub value: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Packet)]
|
||||||
|
pub struct HalfShort {
|
||||||
|
#[packet(bitfield(size = 8))]
|
||||||
|
pub _unused: u8,
|
||||||
|
#[packet(bitfield(size = 8))]
|
||||||
|
pub value: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::{HalfInt, HalfLong, HalfShort};
|
||||||
|
use minecraft_protocol::decoder::Decoder;
|
||||||
|
use minecraft_protocol::encoder::Encoder;
|
||||||
|
use minecraft_protocol::error::{DecodeError, EncodeError};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_half_long_u64_bitfield() {
|
||||||
|
let half = HalfLong {
|
||||||
|
_unused: 0,
|
||||||
|
value: u32::MAX,
|
||||||
|
};
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
|
||||||
|
half.encode(&mut vec).expect("Failed to encode half");
|
||||||
|
assert_eq!(vec, u32::MAX.to_be_bytes().to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_half_long_u64_bitfield() {
|
||||||
|
let value = u32::MAX as u64;
|
||||||
|
|
||||||
|
let vec = value.to_be_bytes().to_vec();
|
||||||
|
let mut cursor = Cursor::new(vec);
|
||||||
|
|
||||||
|
let half = HalfLong::decode(&mut cursor).expect("Failed to decode half");
|
||||||
|
assert_eq!(half.value, u32::MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_half_int_u32_bitfield() {
|
||||||
|
let half = HalfInt {
|
||||||
|
_unused: 0,
|
||||||
|
value: u16::MAX,
|
||||||
|
};
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
|
||||||
|
half.encode(&mut vec).expect("Failed to encode half");
|
||||||
|
assert_eq!(vec, u16::MAX.to_be_bytes().to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_half_int_u32_bitfield() {
|
||||||
|
let value = u16::MAX as u32;
|
||||||
|
|
||||||
|
let vec = value.to_be_bytes().to_vec();
|
||||||
|
let mut cursor = Cursor::new(vec);
|
||||||
|
|
||||||
|
let half = HalfInt::decode(&mut cursor).expect("Failed to decode half");
|
||||||
|
assert_eq!(half.value, u16::MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_half_short_u8_bitfield() {
|
||||||
|
let half = HalfShort {
|
||||||
|
_unused: 0,
|
||||||
|
value: u8::MAX,
|
||||||
|
};
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
|
||||||
|
half.encode(&mut vec).expect("Failed to encode half");
|
||||||
|
assert_eq!(vec, u8::MAX.to_be_bytes().to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_half_short_u8_bitfield() {
|
||||||
|
let value = u8::MAX as u16;
|
||||||
|
let vec = value.to_be_bytes().to_vec();
|
||||||
|
let mut cursor = Cursor::new(vec);
|
||||||
|
|
||||||
|
let half = HalfShort::decode(&mut cursor).expect("Failed to decode half");
|
||||||
|
assert_eq!(half.value, u8::MAX);
|
||||||
|
}
|
||||||
|
}
|
54
protocol-derive/tests/bitfield_position.rs
Normal file
54
protocol-derive/tests/bitfield_position.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate minecraft_protocol_derive;
|
||||||
|
|
||||||
|
use minecraft_protocol::decoder::Decoder;
|
||||||
|
use minecraft_protocol::encoder::Encoder;
|
||||||
|
use minecraft_protocol::error::{DecodeError, EncodeError};
|
||||||
|
|
||||||
|
#[derive(Packet)]
|
||||||
|
pub struct Position {
|
||||||
|
#[packet(bitfield(size = 26))]
|
||||||
|
pub x: i32,
|
||||||
|
#[packet(bitfield(size = 26))]
|
||||||
|
pub z: i32,
|
||||||
|
#[packet(bitfield(size = 12))]
|
||||||
|
pub y: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::Position;
|
||||||
|
use minecraft_protocol::decoder::Decoder;
|
||||||
|
use minecraft_protocol::encoder::Encoder;
|
||||||
|
use minecraft_protocol::error::{DecodeError, EncodeError};
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_encode_position_i64_bitfield() {
|
||||||
|
let position = Position {
|
||||||
|
x: 1000,
|
||||||
|
y: 64,
|
||||||
|
z: -1000,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
|
||||||
|
position
|
||||||
|
.encode(&mut vec)
|
||||||
|
.expect("Failed to encode position");
|
||||||
|
|
||||||
|
assert_eq!(vec, 275152780755008i64.to_be_bytes().to_vec());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_decode_position_i64_bitfield() {
|
||||||
|
let value = -137164079660992i64;
|
||||||
|
let vec = value.to_be_bytes().to_vec();
|
||||||
|
let mut cursor = Cursor::new(vec);
|
||||||
|
let position = Position::decode(&mut cursor).expect("Failed to decode position");
|
||||||
|
|
||||||
|
assert_eq!(position.x, -500);
|
||||||
|
assert_eq!(position.y, 64);
|
||||||
|
assert_eq!(position.z, -1000);
|
||||||
|
}
|
||||||
|
}
|
@ -5,7 +5,7 @@ use num_traits::FromPrimitive;
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
trait Decoder {
|
pub trait Decoder {
|
||||||
type Output;
|
type Output;
|
||||||
|
|
||||||
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError>;
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError>;
|
||||||
@ -109,6 +109,14 @@ impl Decoder for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Decoder for i16 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
||||||
|
Ok(reader.read_i16::<BigEndian>()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Decoder for i32 {
|
impl Decoder for i32 {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
@ -117,6 +125,14 @@ impl Decoder for i32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Decoder for u16 {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn decode<R: Read>(reader: &mut R) -> Result<Self::Output, DecodeError> {
|
||||||
|
Ok(reader.read_u16::<BigEndian>()?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Decoder for u32 {
|
impl Decoder for u32 {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ use num_traits::ToPrimitive;
|
|||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
trait Encoder {
|
pub trait Encoder {
|
||||||
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError>;
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,12 +103,24 @@ impl Encoder for u8 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Encoder for i16 {
|
||||||
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
|
Ok(writer.write_i16::<BigEndian>(*self)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Encoder for i32 {
|
impl Encoder for i32 {
|
||||||
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
Ok(writer.write_i32::<BigEndian>(*self)?)
|
Ok(writer.write_i32::<BigEndian>(*self)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Encoder for u16 {
|
||||||
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
|
Ok(writer.write_u16::<BigEndian>(*self)?)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Encoder for u32 {
|
impl Encoder for u32 {
|
||||||
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
fn encode<W: Write>(&self, writer: &mut W) -> Result<(), EncodeError> {
|
||||||
Ok(writer.write_u32::<BigEndian>(*self)?)
|
Ok(writer.write_u32::<BigEndian>(*self)?)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user