use std::io::{Read, Write};
use mcproto_codec::{
error::{CodecError, CodecKind},
varint::{VarIntRead, VarIntWrite},
};
use crate::{Boolean, Int, TypeCodec, TypeStructCodec};
use super::{
DataComponentType, InvalidItemStack, decode_item_count, decode_item_id, read_length,
validate_item_fields, write_length,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TypeStructCodec)]
#[type_struct_codec(kind = HashedSlot)]
pub struct HashedDataComponent {
pub component_type: DataComponentType,
pub data_hash: Int,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HashedItemStack {
pub item_id: u32,
pub count: u32,
pub components_to_add: Vec<HashedDataComponent>,
pub components_to_remove: Vec<DataComponentType>,
}
impl HashedItemStack {
pub fn new(count: u32, item_id: u32) -> Result<Self, InvalidItemStack> {
if count == 0 || count > i32::MAX as u32 {
return Err(InvalidItemStack::Count(count));
}
if item_id > i32::MAX as u32 {
return Err(InvalidItemStack::ItemId(item_id));
}
Ok(Self {
item_id,
count,
components_to_add: Vec::new(),
components_to_remove: Vec::new(),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub enum HashedSlot {
#[default]
Empty,
Item(HashedItemStack),
}
impl TypeCodec for HashedSlot {
fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
let Self::Item(item) = self else {
return Boolean(false)
.encode(writer)
.map_err(|error| error.with_context(CodecKind::HashedSlot));
};
validate_item_fields(item.count, item.item_id, CodecKind::HashedSlot)?;
Boolean(true)
.encode(writer)
.map_err(|error| error.with_context(CodecKind::HashedSlot))?;
writer
.write_varint(item.item_id as i32)
.map_err(|error| error.with_context(CodecKind::HashedSlot))?;
writer
.write_varint(item.count as i32)
.map_err(|error| error.with_context(CodecKind::HashedSlot))?;
write_length(writer, item.components_to_add.len(), CodecKind::HashedSlot)?;
for component in &item.components_to_add {
component.encode(writer)?;
}
write_length(
writer,
item.components_to_remove.len(),
CodecKind::HashedSlot,
)?;
for component_type in &item.components_to_remove {
component_type
.encode(writer)
.map_err(|error| error.with_context(CodecKind::HashedSlot))?;
}
Ok(())
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
let has_item = Boolean::decode(reader)
.map_err(|error| error.with_context(CodecKind::HashedSlot))?
.0;
if !has_item {
return Ok(Self::Empty);
}
let item_id = reader
.read_varint()
.map_err(|error| error.with_context(CodecKind::HashedSlot))?;
let item_id = decode_item_id(item_id, CodecKind::HashedSlot)?;
let count = reader
.read_varint()
.map_err(|error| error.with_context(CodecKind::HashedSlot))?;
let count = decode_item_count(count, CodecKind::HashedSlot)?;
let add_count = read_length(reader, CodecKind::HashedSlot)?;
let mut components_to_add = Vec::with_capacity(add_count.min(1024));
for _ in 0..add_count {
components_to_add.push(HashedDataComponent::decode(reader)?);
}
let remove_count = read_length(reader, CodecKind::HashedSlot)?;
let mut components_to_remove = Vec::with_capacity(remove_count.min(1024));
for _ in 0..remove_count {
components_to_remove.push(
DataComponentType::decode(reader)
.map_err(|error| error.with_context(CodecKind::HashedSlot))?,
);
}
Ok(Self::Item(HashedItemStack {
item_id,
count,
components_to_add,
components_to_remove,
}))
}
}