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`
This commit is contained in:
guac420 2022-09-08 21:39:08 -07:00 committed by GitHub
parent 20546e2fb8
commit 96f5614941
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 209 additions and 58 deletions

View file

@ -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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
let id = VarInt::decode(r)?.0;
let errmsg = "invalid block state ID";

View file

@ -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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
// Use arithmetic right shift to determine sign.
let val = i64::decode(r)?;
let x = val >> 38;

View file

@ -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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(Self(VarInt::decode(r)?.0 as u32))
}
}

View file

@ -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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
let string = BoundedString::<0, 32767>::decode(r)?.0;
Ok(Ident::new(string)?)
}

View file

@ -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<Self>;
fn decode(r: &mut &[u8]) -> anyhow::Result<Self>;
}
/// 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<Self> {
fn decode(_r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(())
}
}
@ -61,7 +62,7 @@ impl<T: Encode, U: Encode> Encode for (T, U) {
}
impl<T: Decode, U: Decode> Decode for (T, U) {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(r.read_u8()?)
}
}
@ -108,7 +109,7 @@ impl Encode for i8 {
}
impl Decode for i8 {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(r.read_i8()?)
}
}
@ -121,7 +122,7 @@ impl Encode for u16 {
}
impl Decode for u16 {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(r.read_u16::<BigEndian>()?)
}
}
@ -134,7 +135,7 @@ impl Encode for i16 {
}
impl Decode for i16 {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(r.read_i16::<BigEndian>()?)
}
}
@ -147,7 +148,7 @@ impl Encode for u32 {
}
impl Decode for u32 {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(r.read_u32::<BigEndian>()?)
}
}
@ -160,7 +161,7 @@ impl Encode for i32 {
}
impl Decode for i32 {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(r.read_i32::<BigEndian>()?)
}
}
@ -173,7 +174,7 @@ impl Encode for u64 {
}
impl Decode for u64 {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(r.read_u64::<BigEndian>()?)
}
}
@ -186,7 +187,7 @@ impl Encode for i64 {
}
impl Decode for i64 {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(r.read_i64::<BigEndian>()?)
}
}
@ -203,7 +204,7 @@ impl Encode for f32 {
}
}
impl Decode for f32 {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
let f = r.read_f32::<BigEndian>()?;
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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
let f = r.read_f64::<BigEndian>()?;
ensure!(f.is_finite(), "attempt to decode non-finite f64 ({f})");
Ok(f)
@ -243,7 +244,7 @@ impl<T: Encode> Encode for Option<T> {
}
impl<T: Decode> Decode for Option<T> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
if bool::decode(r)? {
Ok(Some(T::decode(r)?))
} else {
@ -259,7 +260,7 @@ impl<T: Encode> Encode for Box<T> {
}
impl<T: Decode> Decode for Box<T> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(Box::new(T::decode(r)?))
}
}
@ -271,7 +272,7 @@ impl Encode for Box<str> {
}
impl Decode for Box<str> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(String::decode(r)?.into_boxed_str())
}
}
@ -319,7 +320,7 @@ impl<T, const MIN: i64, const MAX: i64> Decode for BoundedInt<T, MIN, MAX>
where
T: Decode + Copy + Into<i64>,
{
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
decode_string_bounded(0, 32767, r)
}
}
@ -370,7 +371,7 @@ impl<const MIN: usize, const MAX: usize> Encode for BoundedString<MIN, MAX> {
}
impl<const MIN: usize, const MAX: usize> Decode for BoundedString<MIN, MAX> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
decode_string_bounded(MIN, MAX, r).map(Self)
}
}
@ -388,7 +389,7 @@ impl<T: Encode> Encode for Vec<T> {
}
impl<T: Decode> Decode for Vec<T> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
decode_array_bounded(0, usize::MAX, r)
}
}
@ -400,7 +401,7 @@ impl<T: Encode> Encode for Box<[T]> {
}
impl<T: Decode> Decode for Box<[T]> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
decode_array_bounded(0, usize::MAX, r).map(|v| v.into_boxed_slice())
}
}
@ -412,7 +413,7 @@ impl<T: Encode, const N: usize> Encode for [T; N] {
}
impl<T: Decode, const N: usize> Decode for [T; N] {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
let mut elems = ArrayVec::new();
for _ in 0..N {
elems.push(T::decode(r)?);
@ -448,19 +449,19 @@ impl<T: Encode> Encode for Vec4<T> {
}
impl<T: Decode> Decode for Vec2<T> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(Vec2::new(T::decode(r)?, T::decode(r)?))
}
}
impl<T: Decode> Decode for Vec3<T> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(Vec3::new(T::decode(r)?, T::decode(r)?, T::decode(r)?))
}
}
impl<T: Decode> Decode for Vec4<T> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(Vec4::new(
T::decode(r)?,
T::decode(r)?,
@ -484,7 +485,7 @@ impl<T: Encode, const MIN: usize, const MAX: usize> Encode for BoundedArray<T, M
}
impl<T: Decode, const MIN: usize, const MAX: usize> Decode for BoundedArray<T, MIN, MAX> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(Uuid::from_u128(r.read_u128::<BigEndian>()?))
}
}
@ -515,7 +516,7 @@ impl Encode for nbt::Compound {
}
impl Decode for nbt::Compound {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(nbt::binary::from_reader(r)?)
}
}
@ -532,7 +533,7 @@ impl<T: Serialize> Encode for NbtBridge<T> {
}
impl<T: DeserializeOwned> Decode for NbtBridge<T> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
Ok(Self(nbt::binary::from_reader(r)?))
}
}
@ -544,7 +545,7 @@ impl Encode for BitVec<u64> {
}
impl Decode for BitVec<u64> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
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<u64> {
}
impl Decode for BitBox<u64> {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
BitVec::decode(r).map(|v| v.into_boxed_bitslice())
}
}
@ -569,7 +570,7 @@ impl Decode for BitBox<u64> {
pub struct RawBytes(pub Vec<u8>);
impl Decode for RawBytes {
fn decode(r: &mut impl Read) -> anyhow::Result<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
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<String> {
assert!(min <= max);
@ -665,7 +666,7 @@ pub(crate) fn decode_string_bounded(
pub(crate) fn decode_array_bounded<T: Decode>(
min: usize,
max: usize,
r: &mut impl Read,
r: &mut &[u8],
) -> anyhow::Result<Vec<T>> {
assert!(min <= max);

View file

@ -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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
u8::decode(r).map(ByteAngle)
}
}

View file

@ -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<Self>;
fn decode_packet(r: &mut &[u8]) -> anyhow::Result<Self>;
}
/// 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<Self> {
fn decode(_r: &mut &[u8]) -> anyhow::Result<Self> {
$(
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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
<$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<Self> {
fn decode_packet(r: &mut &[u8]) -> anyhow::Result<Self> {
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<Self> {
fn decode_packet(r: &mut &[u8]) -> anyhow::Result<Self> {
let packet_id = VarInt::decode(r)
.context(concat!("failed to read ", stringify!($group_name), " packet ID"))?.0;

148
src/protocol/slot.rs Normal file
View file

@ -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<Compound>,
},
}
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<Self> {
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<u8> = 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<u8> = 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<u8> = 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());
}
}

View file

@ -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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
let mut val = 0;
for i in 0..Self::MAX_SIZE {
let byte = r.read_u8()?;

View file

@ -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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
let mut val = 0;
for i in 0..Self::MAX_SIZE {
let byte = r.read_u8()?;

View file

@ -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<Self> {
fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
let string = BoundedString::<0, 262144>::decode(r)?;
Ok(serde_json::from_str(&string.0)?)
}