From 96f561494190338ed0186561135e1ce14cb9a177 Mon Sep 17 00:00:00 2001 From: guac420 <60993440+guac420@users.noreply.github.com> Date: Thu, 8 Sep 2022 21:39:08 -0700 Subject: [PATCH] Add support for Slot data decoding (#25) * Impl Seek for reader in Decode Trait * Add support for Slot data type * Update tests and rust fmt * Add tests for Slot Also fixed bugs I found while testing * Update slot signature * Resolved requested changes Updated decode trait signature and removed unnecessary getters/setters in `Slot` --- src/block.rs | 4 +- src/block_pos.rs | 4 +- src/entity/types.rs | 4 +- src/ident.rs | 6 +- src/protocol.rs | 71 +++++++++--------- src/protocol/byte_angle.rs | 4 +- src/protocol/packets.rs | 14 ++-- src/protocol/slot.rs | 148 +++++++++++++++++++++++++++++++++++++ src/protocol/var_int.rs | 4 +- src/protocol/var_long.rs | 4 +- src/text.rs | 4 +- 11 files changed, 209 insertions(+), 58 deletions(-) create mode 100644 src/protocol/slot.rs diff --git a/src/block.rs b/src/block.rs index fdd7942..bf056a2 100644 --- a/src/block.rs +++ b/src/block.rs @@ -3,7 +3,7 @@ #![allow(clippy::all, missing_docs)] use std::fmt::{self, Display}; -use std::io::{Read, Write}; +use std::io::Write; use std::iter::FusedIterator; use anyhow::Context; @@ -58,7 +58,7 @@ impl Encode for BlockState { } impl Decode for BlockState { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let id = VarInt::decode(r)?.0; let errmsg = "invalid block state ID"; diff --git a/src/block_pos.rs b/src/block_pos.rs index c507d55..a02e350 100644 --- a/src/block_pos.rs +++ b/src/block_pos.rs @@ -1,4 +1,4 @@ -use std::io::{Read, Write}; +use std::io::Write; use anyhow::bail; use vek::Vec3; @@ -38,7 +38,7 @@ impl Encode for BlockPos { } impl Decode for BlockPos { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { // Use arithmetic right shift to determine sign. let val = i64::decode(r)?; let x = val >> 38; diff --git a/src/entity/types.rs b/src/entity/types.rs index f4593a4..ff64592 100644 --- a/src/entity/types.rs +++ b/src/entity/types.rs @@ -1,6 +1,6 @@ //! Primitive types used in getters and setters on entities. -use std::io::{Read, Write}; +use std::io::Write; use crate::protocol::{Decode, Encode, VarInt}; @@ -30,7 +30,7 @@ impl Encode for OptionalInt { } impl Decode for OptionalInt { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(Self(VarInt::decode(r)?.0 as u32)) } } diff --git a/src/ident.rs b/src/ident.rs index 38557fb..1cefa72 100644 --- a/src/ident.rs +++ b/src/ident.rs @@ -1,7 +1,9 @@ //! Namespaced identifiers. +//! +//! use std::borrow::Cow; -use std::io::{Read, Write}; +use std::io::Write; use std::str::FromStr; use ascii::{AsAsciiStr, AsciiChar, AsciiStr, IntoAsciiString}; @@ -208,7 +210,7 @@ impl Encode for Ident { } impl Decode for Ident { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let string = BoundedString::<0, 32767>::decode(r)?.0; Ok(Ident::new(string)?) } diff --git a/src/protocol.rs b/src/protocol.rs index 2b52237..7bc57a7 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -23,6 +23,7 @@ use crate::nbt; mod byte_angle; pub mod codec; pub mod packets; +pub mod slot; mod var_int; mod var_long; @@ -35,7 +36,7 @@ pub trait Encode { /// Types that can be read from the Minecraft protocol. pub trait Decode: Sized { - fn decode(r: &mut impl Read) -> anyhow::Result; + fn decode(r: &mut &[u8]) -> anyhow::Result; } /// The maximum number of bytes in a single packet. @@ -48,7 +49,7 @@ impl Encode for () { } impl Decode for () { - fn decode(_r: &mut impl Read) -> anyhow::Result { + fn decode(_r: &mut &[u8]) -> anyhow::Result { Ok(()) } } @@ -61,7 +62,7 @@ impl Encode for (T, U) { } impl Decode for (T, U) { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok((T::decode(r)?, U::decode(r)?)) } } @@ -80,7 +81,7 @@ impl Encode for bool { } impl Decode for bool { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let n = r.read_u8()?; ensure!(n < 2, "boolean is not 0 or 1"); Ok(n == 1) @@ -95,7 +96,7 @@ impl Encode for u8 { } impl Decode for u8 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(r.read_u8()?) } } @@ -108,7 +109,7 @@ impl Encode for i8 { } impl Decode for i8 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(r.read_i8()?) } } @@ -121,7 +122,7 @@ impl Encode for u16 { } impl Decode for u16 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(r.read_u16::()?) } } @@ -134,7 +135,7 @@ impl Encode for i16 { } impl Decode for i16 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(r.read_i16::()?) } } @@ -147,7 +148,7 @@ impl Encode for u32 { } impl Decode for u32 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(r.read_u32::()?) } } @@ -160,7 +161,7 @@ impl Encode for i32 { } impl Decode for i32 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(r.read_i32::()?) } } @@ -173,7 +174,7 @@ impl Encode for u64 { } impl Decode for u64 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(r.read_u64::()?) } } @@ -186,7 +187,7 @@ impl Encode for i64 { } impl Decode for i64 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(r.read_i64::()?) } } @@ -203,7 +204,7 @@ impl Encode for f32 { } } impl Decode for f32 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let f = r.read_f32::()?; ensure!(f.is_finite(), "attempt to decode non-finite f32 ({f})"); Ok(f) @@ -223,7 +224,7 @@ impl Encode for f64 { } impl Decode for f64 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let f = r.read_f64::()?; ensure!(f.is_finite(), "attempt to decode non-finite f64 ({f})"); Ok(f) @@ -243,7 +244,7 @@ impl Encode for Option { } impl Decode for Option { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { if bool::decode(r)? { Ok(Some(T::decode(r)?)) } else { @@ -259,7 +260,7 @@ impl Encode for Box { } impl Decode for Box { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(Box::new(T::decode(r)?)) } } @@ -271,7 +272,7 @@ impl Encode for Box { } impl Decode for Box { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(String::decode(r)?.into_boxed_str()) } } @@ -319,7 +320,7 @@ impl Decode for BoundedInt where T: Decode + Copy + Into, { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let res = T::decode(r)?; let val = res.into(); @@ -339,7 +340,7 @@ impl Encode for String { } impl Decode for String { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { decode_string_bounded(0, 32767, r) } } @@ -370,7 +371,7 @@ impl Encode for BoundedString { } impl Decode for BoundedString { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { decode_string_bounded(MIN, MAX, r).map(Self) } } @@ -388,7 +389,7 @@ impl Encode for Vec { } impl Decode for Vec { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { decode_array_bounded(0, usize::MAX, r) } } @@ -400,7 +401,7 @@ impl Encode for Box<[T]> { } impl Decode for Box<[T]> { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { decode_array_bounded(0, usize::MAX, r).map(|v| v.into_boxed_slice()) } } @@ -412,7 +413,7 @@ impl Encode for [T; N] { } impl Decode for [T; N] { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let mut elems = ArrayVec::new(); for _ in 0..N { elems.push(T::decode(r)?); @@ -448,19 +449,19 @@ impl Encode for Vec4 { } impl Decode for Vec2 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(Vec2::new(T::decode(r)?, T::decode(r)?)) } } impl Decode for Vec3 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(Vec3::new(T::decode(r)?, T::decode(r)?, T::decode(r)?)) } } impl Decode for Vec4 { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(Vec4::new( T::decode(r)?, T::decode(r)?, @@ -484,7 +485,7 @@ impl Encode for BoundedArray Decode for BoundedArray { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { decode_array_bounded(MIN, MAX, r).map(Self) } } @@ -503,7 +504,7 @@ impl Encode for Uuid { } impl Decode for Uuid { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(Uuid::from_u128(r.read_u128::()?)) } } @@ -515,7 +516,7 @@ impl Encode for nbt::Compound { } impl Decode for nbt::Compound { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(nbt::binary::from_reader(r)?) } } @@ -532,7 +533,7 @@ impl Encode for NbtBridge { } impl Decode for NbtBridge { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { Ok(Self(nbt::binary::from_reader(r)?)) } } @@ -544,7 +545,7 @@ impl Encode for BitVec { } impl Decode for BitVec { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { BitVec::try_from_vec(Vec::decode(r)?) .map_err(|_| anyhow!("Array is too long for bit vector")) } @@ -557,7 +558,7 @@ impl Encode for BitBox { } impl Decode for BitBox { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { BitVec::decode(r).map(|v| v.into_boxed_bitslice()) } } @@ -569,7 +570,7 @@ impl Decode for BitBox { pub struct RawBytes(pub Vec); impl Decode for RawBytes { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let mut buf = Vec::new(); r.read_to_end(&mut buf)?; Ok(RawBytes(buf)) @@ -645,7 +646,7 @@ pub(crate) fn encode_string_bounded( pub(crate) fn decode_string_bounded( min: usize, max: usize, - r: &mut impl Read, + r: &mut &[u8], ) -> anyhow::Result { assert!(min <= max); @@ -665,7 +666,7 @@ pub(crate) fn decode_string_bounded( pub(crate) fn decode_array_bounded( min: usize, max: usize, - r: &mut impl Read, + r: &mut &[u8], ) -> anyhow::Result> { assert!(min <= max); diff --git a/src/protocol/byte_angle.rs b/src/protocol/byte_angle.rs index 8fd1307..8f62208 100644 --- a/src/protocol/byte_angle.rs +++ b/src/protocol/byte_angle.rs @@ -1,4 +1,4 @@ -use std::io::{Read, Write}; +use std::io::Write; use crate::protocol::{Decode, Encode}; @@ -23,7 +23,7 @@ impl Encode for ByteAngle { } impl Decode for ByteAngle { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { u8::decode(r).map(ByteAngle) } } diff --git a/src/protocol/packets.rs b/src/protocol/packets.rs index 78166be..0ad9931 100644 --- a/src/protocol/packets.rs +++ b/src/protocol/packets.rs @@ -5,7 +5,7 @@ #![macro_use] use std::fmt; -use std::io::{Read, Write}; +use std::io::Write; use anyhow::{bail, ensure, Context}; use bitvec::prelude::BitVec; @@ -42,7 +42,7 @@ pub trait EncodePacket: fmt::Debug { /// the body of the packet. pub trait DecodePacket: Sized + fmt::Debug { /// Reads a packet from the Minecraft protocol, including its packet ID. - fn decode_packet(r: &mut impl Read) -> anyhow::Result; + fn decode_packet(r: &mut &[u8]) -> anyhow::Result; } /// Defines a struct which implements [`Encode`] and [`Decode`]. @@ -79,7 +79,7 @@ macro_rules! def_struct { } impl Decode for $name { - fn decode(_r: &mut impl Read) -> anyhow::Result { + fn decode(_r: &mut &[u8]) -> anyhow::Result { $( let $field: $typ = Decode::decode(_r) .context(concat!("failed to read field `", stringify!($field), "` from struct `", stringify!($name), "`"))?; @@ -149,7 +149,7 @@ macro_rules! def_enum { } impl Decode for $name { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let tag_ctx = concat!("failed to read enum tag for `", stringify!($name), "`"); let tag = <$tag_ty>::decode(r).context(tag_ctx)?.into(); match tag { @@ -261,7 +261,7 @@ macro_rules! def_bitfield { } impl Decode for $name { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { <$inner_ty>::decode(r).map(Self) } } @@ -300,7 +300,7 @@ macro_rules! def_packet_group { } impl DecodePacket for $packet { - fn decode_packet(r: &mut impl Read) -> anyhow::Result { + fn decode_packet(r: &mut &[u8]) -> anyhow::Result { let packet_id = VarInt::decode(r).context("failed to read packet ID")?.0; ensure!( @@ -314,7 +314,7 @@ macro_rules! def_packet_group { )* impl DecodePacket for $group_name { - fn decode_packet(r: &mut impl Read) -> anyhow::Result { + fn decode_packet(r: &mut &[u8]) -> anyhow::Result { let packet_id = VarInt::decode(r) .context(concat!("failed to read ", stringify!($group_name), " packet ID"))?.0; diff --git a/src/protocol/slot.rs b/src/protocol/slot.rs new file mode 100644 index 0000000..0c515c5 --- /dev/null +++ b/src/protocol/slot.rs @@ -0,0 +1,148 @@ +use byteorder::ReadBytesExt; +use std::io::Write; + +use crate::nbt::Compound; +use crate::protocol::{Decode, Encode, VarInt}; + +/// Represents a slot in an inventory. +#[derive(Clone, Default, Debug)] +pub enum Slot { + #[default] + Empty, + Present { + item_id: VarInt, + item_count: u8, + nbt: Option, + }, +} + +impl Encode for Slot { + fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> { + match self { + Slot::Empty => false.encode(w), + Slot::Present { + item_id, + item_count, + nbt, + } => { + true.encode(w)?; + item_id.encode(w)?; + item_count.encode(w)?; + match &nbt { + Some(n) => n.encode(w), + None => 0u8.encode(w), + } + } + } + } +} + +impl Decode for Slot { + fn decode(r: &mut &[u8]) -> anyhow::Result { + let present = bool::decode(r)?; + if !present { + return Ok(Slot::Empty); + } + Ok(Slot::Present { + item_id: VarInt::decode(r)?, + item_count: u8::decode(r)?, + nbt: if r.get(0) == Some(&0) { + r.read_u8()?; + None + } else { + Some(Compound::decode(r)?) + }, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use serde_nbt::Value; + + #[test] + fn slot_with_nbt() { + let mut buf: Vec = Vec::new(); + + // Example nbt blob + // https://wiki.vg/Slot_Data + let mut nbt = Compound::new(); + { + let mut enchant = Compound::new(); + enchant.insert("id".to_string(), Value::Short(1)); + enchant.insert("lvl".to_string(), Value::Short(1)); + + let enchant_list = vec![enchant]; + nbt.insert( + "StoredEnchantments".to_string(), + Value::List(enchant_list.into()), + ); + nbt.insert("Unbreakable".to_string(), Value::Int(1)); + } + + Slot::Present { + item_id: VarInt(1), + item_count: 1, + nbt: Some(nbt), + } + .encode(&mut buf) + .unwrap(); + + let mut slice = buf.as_slice(); + let (item_id, item_count, nbt) = match Slot::decode(&mut slice).unwrap() { + Slot::Empty => { + panic!("Slot should be present") + } + Slot::Present { + item_id, + item_count, + nbt, + } => (item_id, item_count, nbt), + }; + assert_eq!(1, item_id.0); + assert_eq!(1, item_count); + assert_eq!(&Value::Int(1), nbt.unwrap().get("Unbreakable").unwrap()); + + assert!(slice.is_empty()); + } + + #[test] + fn slot_no_nbt() { + let mut buf: Vec = Vec::new(); + + Slot::Present { + item_id: VarInt(1), + item_count: 1, + nbt: None, + } + .encode(&mut buf) + .unwrap(); + + let mut slice = buf.as_slice(); + let nbt = match Slot::decode(&mut slice).unwrap() { + Slot::Empty => { + panic!("Slot should be present") + } + Slot::Present { nbt, .. } => nbt, + }; + + assert_eq!(None, nbt); + + assert!(slice.is_empty()); + } + + #[test] + fn empty_slot() { + let mut buf: Vec = Vec::new(); + + Slot::Empty.encode(&mut buf).unwrap(); + + let mut slice = buf.as_slice(); + if let Slot::Present { .. } = Slot::decode(&mut slice).unwrap() { + panic!("Slot should be empty") + }; + + assert!(slice.is_empty()); + } +} diff --git a/src/protocol/var_int.rs b/src/protocol/var_int.rs index f5588d5..e1aea02 100644 --- a/src/protocol/var_int.rs +++ b/src/protocol/var_int.rs @@ -1,4 +1,4 @@ -use std::io::{Read, Write}; +use std::io::Write; use anyhow::bail; use byteorder::{ReadBytesExt, WriteBytesExt}; @@ -47,7 +47,7 @@ impl Encode for VarInt { } impl Decode for VarInt { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let mut val = 0; for i in 0..Self::MAX_SIZE { let byte = r.read_u8()?; diff --git a/src/protocol/var_long.rs b/src/protocol/var_long.rs index 85ad906..ffe2d2a 100644 --- a/src/protocol/var_long.rs +++ b/src/protocol/var_long.rs @@ -1,4 +1,4 @@ -use std::io::{Read, Write}; +use std::io::Write; use anyhow::bail; use byteorder::{ReadBytesExt, WriteBytesExt}; @@ -30,7 +30,7 @@ impl Encode for VarLong { } impl Decode for VarLong { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let mut val = 0; for i in 0..Self::MAX_SIZE { let byte = r.read_u8()?; diff --git a/src/text.rs b/src/text.rs index 6cff249..78d736e 100644 --- a/src/text.rs +++ b/src/text.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use std::fmt; -use std::io::{Read, Write}; +use std::io::Write; use serde::de::Visitor; use serde::{Deserialize, Deserializer, Serialize, Serializer}; @@ -455,7 +455,7 @@ impl Encode for Text { } impl Decode for Text { - fn decode(r: &mut impl Read) -> anyhow::Result { + fn decode(r: &mut &[u8]) -> anyhow::Result { let string = BoundedString::<0, 262144>::decode(r)?; Ok(serde_json::from_str(&string.0)?) }