Pumpkin/pumpkin-protocol/src/codec/data_component.rs
Alexander Medvedev 65fe9f0af0 feat: add bundles
2026-06-22 16:59:31 +02:00

1046 lines
38 KiB
Rust

use std::borrow::Cow;
use crate::codec::var_int::VarInt;
use pumpkin_data::Enchantment;
use pumpkin_data::data_component::DataComponent;
use pumpkin_data::data_component_impl::{
BundleContentsImpl, ConsumableImpl, ConsumeAnimation, ConsumeEffect, CustomNameImpl,
DamageImpl, DataComponentImpl, EnchantmentsImpl, EquipmentSlot, EquippableImpl,
FireworkExplosionImpl, FireworkExplosionShape, FireworksImpl, IDSet, IDSetContent, IdOr,
ItemModelImpl, MapIdImpl, MaxStackSizeImpl, PotionContentsImpl, SoundEvent,
StatusEffectInstance, StoredEnchantmentsImpl, UnbreakableImpl, UseCooldownImpl, get,
};
use pumpkin_data::effect::StatusEffect;
use pumpkin_data::entity::EntityType;
use pumpkin_data::sound::Sound;
use serde::de;
use serde::de::SeqAccess;
use serde::ser::SerializeStruct;
const MAX_STATUS_EFFECTS: usize = 128;
#[must_use]
pub fn data_to_proto_sound(id_or: &IdOr<SoundEvent>) -> crate::IdOr<crate::SoundEvent> {
match id_or {
IdOr::Id(id) => crate::IdOr::Id(*id as u16),
IdOr::Value(sound) => crate::IdOr::Value(crate::SoundEvent {
sound_name: sound.sound_name.clone(),
range: sound.range,
}),
}
}
#[must_use]
pub fn proto_to_data_sound(id_or: &crate::IdOr<crate::SoundEvent>) -> Option<IdOr<SoundEvent>> {
match id_or {
crate::IdOr::Id(id) => {
let name = Sound::NAMES.get(*id as usize)?;
Some(IdOr::Id(Sound::from_name(name)?))
}
crate::IdOr::Value(sound) => Some(IdOr::Value(SoundEvent {
sound_name: sound.sound_name.clone(),
range: sound.range,
})),
}
}
fn deserialize_idset<'a, A: SeqAccess<'a>, T: IDSetContent>(
seq: &mut A,
) -> Result<IDSet<T>, A::Error> {
let id_type = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No type/len VarInt in IDSet"))?
.0;
match id_type.cmp(&0) {
std::cmp::Ordering::Equal => {
let tag = seq
.next_element::<String>()?
.ok_or(de::Error::custom("No tag name in IDSet"))?;
Ok(IDSet::Tag(Cow::Owned(tag)))
}
std::cmp::Ordering::Greater => {
let len = id_type - 1;
let mut content_vec = Vec::with_capacity(len as usize);
for _ in 0..len {
let varint_id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("Missing registry id VarInt in IDSet"))?
.0;
let elmt = T::from_id(varint_id as u16)
.ok_or(de::Error::custom("Invalid registry id VarInt in IDSet"))?;
content_vec.push(elmt);
}
Ok(IDSet::IDs(Cow::Owned(content_vec)))
}
std::cmp::Ordering::Less => {
Result::Err(de::Error::custom("Negative type/len VarInt in IDSet"))
}
}
}
fn serialize_idset<T: SerializeStruct, C: IDSetContent>(
idset: &IDSet<C>,
seq: &mut T,
) -> Result<(), T::Error> {
match idset {
IDSet::Tag(tag) => {
seq.serialize_field::<VarInt>("", &VarInt(0))?;
seq.serialize_field::<str>("", tag)
}
IDSet::IDs(elements) => {
seq.serialize_field::<VarInt>("", &VarInt(elements.len() as i32 + 1))?;
for elmt in elements.iter() {
seq.serialize_field::<VarInt>("", &VarInt(elmt.registry_id() as i32))?;
}
Ok(())
}
}
}
fn deserialize_status_effects<'a, A: SeqAccess<'a>>(
seq: &mut A,
) -> Result<Vec<StatusEffectInstance>, A::Error> {
let effects_len = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No effects_len VarInt!"))?
.0 as usize;
if effects_len > MAX_STATUS_EFFECTS {
return Err(de::Error::custom("Too many status effects"));
}
let mut custom_effects = Vec::with_capacity(effects_len);
for _ in 0..effects_len {
let effect_registry_id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No effect_id VarInt!"))?
.0;
let effect_name = StatusEffect::from_id(effect_registry_id as u16)
.ok_or(de::Error::custom("Invalid effect_id!"))?
.minecraft_name;
let effect_id = Cow::Borrowed(effect_name);
// Effect parameters
let amplifier = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No amplifier VarInt!"))?
.0;
let duration = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No duration VarInt!"))?
.0;
let ambient = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No ambient bool!"))?;
let show_particles = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No show_particles bool!"))?;
let show_icon = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No show_icon bool!"))?;
// Hidden effect (optional, recursive) - we skip it for now
let has_hidden = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No has_hidden bool!"))?;
if has_hidden {
// Skip hidden effect parameters recursively
skip_effect_parameters(seq)?;
}
custom_effects.push(StatusEffectInstance {
effect_id,
amplifier,
duration,
ambient,
show_particles,
show_icon,
});
}
Ok(custom_effects)
}
fn serialize_status_effects<T: SerializeStruct>(
effects: &Vec<StatusEffectInstance>,
seq: &mut T,
) -> Result<(), T::Error> {
seq.serialize_field::<VarInt>("", &VarInt(effects.len() as i32))?;
for effect in effects {
let effect_id = StatusEffect::from_minecraft_name(&effect.effect_id)
.ok_or_else(|| {
serde::ser::Error::custom(format!("Invalid status effect: {}", effect.effect_id))
})?
.registry_id();
seq.serialize_field::<VarInt>("", &VarInt(effect_id as i32))?;
// Effect parameters
seq.serialize_field::<VarInt>("", &VarInt::from(effect.amplifier))?;
seq.serialize_field::<VarInt>("", &VarInt::from(effect.duration))?;
seq.serialize_field::<bool>("", &effect.ambient)?;
seq.serialize_field::<bool>("", &effect.show_particles)?;
seq.serialize_field::<bool>("", &effect.show_icon)?;
// No hidden effect for now
seq.serialize_field::<bool>("", &false)?;
}
Ok(())
}
fn deserialize_consume_effect<'a, A: SeqAccess<'a>>(
seq: &mut A,
) -> Result<ConsumeEffect, A::Error> {
let effect_type = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No type VarInt in ConsumeEffect"))?
.0;
match effect_type {
0 => {
let probability = seq
.next_element::<f32>()?
.ok_or(de::Error::custom("No probability float in ConsumeEffect"))?;
Ok(ConsumeEffect::ApplyEffects((
Cow::Owned(deserialize_status_effects(seq)?),
probability,
)))
}
1 => {
let idset = deserialize_idset(seq)?;
Ok(ConsumeEffect::RemoveEffects(idset))
}
2 => Ok(ConsumeEffect::ClearAllEffects),
3 => {
let diameter = seq
.next_element::<f32>()?
.ok_or(de::Error::custom("No diameter float in ConsumeEffect"))?;
Ok(ConsumeEffect::TeleportRandomly(diameter))
}
4 => {
let proto_sound_event = seq
.next_element::<crate::IdOr<crate::SoundEvent>>()?
.ok_or(de::Error::custom(
"No sound IdOr<SoundEvent> in ConsumeEffect",
))?;
Ok(ConsumeEffect::PlaySound(
proto_to_data_sound(&proto_sound_event)
.ok_or(de::Error::custom("Invalid sound in ConsumeEffect"))?,
))
}
_ => Err(de::Error::custom("Invalid effect_type in ConsumeEffect")),
}
}
fn serialize_consume_effect<T: SerializeStruct>(
consume_effect: &ConsumeEffect,
seq: &mut T,
) -> Result<(), T::Error> {
seq.serialize_field::<VarInt>("", &VarInt(consume_effect.registry_id() as i32))?;
match consume_effect {
ConsumeEffect::ApplyEffects((effects, probability)) => {
serialize_status_effects(&effects.to_vec(), seq)?;
seq.serialize_field::<f32>("", probability)?;
}
ConsumeEffect::RemoveEffects(idset) => serialize_idset(idset, seq)?,
ConsumeEffect::ClearAllEffects => (),
ConsumeEffect::TeleportRandomly(diameter) => seq.serialize_field::<f32>("", diameter)?,
ConsumeEffect::PlaySound(id_or) => {
seq.serialize_field::<crate::IdOr<crate::SoundEvent>>("", &data_to_proto_sound(id_or))?;
}
}
Ok(())
}
trait DataComponentCodec<Impl: DataComponentImpl> {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error>;
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Impl, A::Error>;
}
impl DataComponentCodec<Self> for MaxStackSizeImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
seq.serialize_field::<VarInt>("", &VarInt::from(self.size))
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
let size = u8::try_from(
seq.next_element::<VarInt>()?
.ok_or(de::Error::custom("No MaxStackSize VarInt!"))?
.0,
)
.map_err(|_| de::Error::custom("No MaxStackSize VarInt!"))?;
Ok(Self { size })
}
}
impl DataComponentCodec<Self> for DamageImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
seq.serialize_field::<VarInt>("", &VarInt::from(self.damage))
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
let damage = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No damage VarInt!"))?
.0;
Ok(Self { damage })
}
}
impl DataComponentCodec<Self> for EnchantmentsImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
seq.serialize_field::<VarInt>("", &VarInt::from(self.enchantment.len() as i32))?;
for (enc, level) in self.enchantment.iter() {
seq.serialize_field::<VarInt>("", &VarInt::from(enc.id))?;
seq.serialize_field::<VarInt>("", &VarInt::from(*level))?;
}
Ok(())
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
const MAX_ENCHANTMENTS: usize = 256;
let len = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No EnchantmentsImpl len VarInt!"))?
.0 as usize;
if len > MAX_ENCHANTMENTS {
return Err(de::Error::custom("Too many enchantments"));
}
let mut enc = Vec::with_capacity(len);
for _ in 0..len {
let id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No EnchantmentsImpl id VarInt!"))?
.0 as u8;
let level = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No EnchantmentsImpl level VarInt!"))?
.0;
enc.push((
Enchantment::from_id(id).ok_or(de::Error::custom(
"EnchantmentsImpl Enchantment VarInt Incorrect!",
))?,
level,
));
}
Ok(Self {
enchantment: Cow::from(enc),
})
}
}
impl DataComponentCodec<Self> for UnbreakableImpl {
fn serialize<T: SerializeStruct>(&self, _seq: &mut T) -> Result<(), T::Error> {
Ok(())
}
fn deserialize<'a, A: SeqAccess<'a>>(_seq: &mut A) -> Result<Self, A::Error> {
Ok(Self)
}
}
impl DataComponentCodec<Self> for ItemModelImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
seq.serialize_field::<String>("", &self.id)
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
let id = seq
.next_element::<String>()?
.ok_or(de::Error::custom("No ItemModelImpl id string!"))?;
Ok(Self { id })
}
}
impl DataComponentCodec<Self> for CustomNameImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
seq.serialize_field::<String>("", &self.name)
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
let name = seq
.next_element::<String>()?
.ok_or(de::Error::custom("No CustomNameImpl name string!"))?;
Ok(Self { name })
}
}
impl DataComponentCodec<Self> for ConsumableImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
seq.serialize_field::<f32>("", &self.consume_seconds)?;
seq.serialize_field::<VarInt>("", &VarInt(self.animation.clone() as i32))?;
seq.serialize_field::<crate::IdOr<crate::SoundEvent>>(
"",
&data_to_proto_sound(&self.sound_event),
)?;
seq.serialize_field::<bool>("", &self.consume_particles)?;
seq.serialize_field::<VarInt>("", &VarInt(self.effects.len() as i32))?;
for effect in self.effects.iter() {
serialize_consume_effect(effect, seq)?;
}
Ok(())
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
let consume_seconds = seq.next_element::<f32>()?.ok_or(de::Error::custom(
"No ConsumableImpl consume_seconds float!",
))?;
let animation_id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No ConsumableImpl animation VarInt!"))?;
let animation: ConsumeAnimation = animation_id
.0
.try_into()
.map_err(|()| de::Error::custom("Invalid ConsumableImpl animation id!"))?;
let proto_sound_event = seq
.next_element::<crate::IdOr<crate::SoundEvent>>()?
.ok_or(de::Error::custom(
"No ConsumableImpl sound_event IdOr<SoundEvent>!",
))?;
let consume_particles = seq.next_element::<bool>()?.ok_or(de::Error::custom(
"No ConsumableImpl consume_particles bool!",
))?;
let sound_event = proto_to_data_sound(&proto_sound_event)
.ok_or(de::Error::custom("Invalid sound in ConsumableImpl"))?;
let effects_len = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No array_len VarInt in ConsumableImpl"))?
.0;
let mut effects_vec = Vec::with_capacity(effects_len as usize);
for _ in 0..effects_len {
effects_vec.push(deserialize_consume_effect(seq)?);
}
let effects: Cow<'static, [ConsumeEffect]> = Cow::Owned(effects_vec);
Ok(Self {
consume_seconds,
animation,
sound_event,
consume_particles,
effects,
})
}
}
impl DataComponentCodec<Self> for EquippableImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
seq.serialize_field::<VarInt>("", &VarInt(self.slot.get_slot_index()))?;
seq.serialize_field::<crate::IdOr<crate::SoundEvent>>(
"",
&data_to_proto_sound(&self.equip_sound),
)?;
seq.serialize_field::<Option<Cow<'static, str>>>("", &self.asset_id)?;
seq.serialize_field::<Option<Cow<'static, str>>>("", &self.camera_overlay)?;
seq.serialize_field::<bool>("", &self.allowed_entities.is_some())?;
if let Some(allowed) = &self.allowed_entities {
serialize_idset(allowed, seq)?;
}
seq.serialize_field::<bool>("", &self.dispensable)?;
seq.serialize_field::<bool>("", &self.swappable)?;
seq.serialize_field::<bool>("", &self.damage_on_hurt)?;
seq.serialize_field::<bool>("", &self.equip_on_interact)?;
seq.serialize_field::<bool>("", &self.can_be_sheared)?;
seq.serialize_field::<crate::IdOr<crate::SoundEvent>>(
"",
&data_to_proto_sound(&self.shearing_sound),
)
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
let slot_index = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No EquippableImpl slot VarInt!"))?
.0;
let slot = EquipmentSlot::from_slot_index(slot_index).ok_or(de::Error::custom(format!(
"Invalid equipment slot index {slot_index}"
)))?;
let equip_sound = proto_to_data_sound(
&seq.next_element::<crate::IdOr<crate::SoundEvent>>()?
.ok_or(de::Error::custom(
"No EquippableImpl equip_sound IdOr<SoundEvent>!",
))?,
)
.ok_or(de::Error::custom("Invalid sound in EquippableImpl"))?;
let asset_id =
seq.next_element::<Option<Cow<'static, str>>>()?
.ok_or(de::Error::custom(
"No EquippableImpl asset_id optional string!",
))?;
let camera_overlay =
seq.next_element::<Option<Cow<'static, str>>>()?
.ok_or(de::Error::custom(
"No EquippableImpl camera_overlay optional string!",
))?;
let has_allowed_entities = seq.next_element::<bool>()?.ok_or(de::Error::custom(
"No EquippableImpl allowed_entities presence bool!",
))?;
let allowed_entities: Option<IDSet<EntityType>> = if has_allowed_entities {
Some(deserialize_idset(seq)?)
} else {
None
};
let dispensable = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No EquippableImpl dispensable bool!"))?;
let swappable = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No EquippableImpl swappable bool!"))?;
let damage_on_hurt = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No EquippableImpl damage_on_hurt bool!"))?;
let equip_on_interact = seq.next_element::<bool>()?.ok_or(de::Error::custom(
"No EquippableImpl equip_on_interact bool!",
))?;
let can_be_sheared = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No EquippableImpl can_be_shared bool!"))?;
let shearing_sound = proto_to_data_sound(
&seq.next_element::<crate::IdOr<crate::SoundEvent>>()?
.ok_or(de::Error::custom(
"No EquippableImpl shearing_sound IdOr<SoundEvent>!",
))?,
)
.ok_or(de::Error::custom(
"Invalid shearing sound in EquippableImpl",
))?;
Ok(Self {
slot,
equip_sound,
asset_id,
camera_overlay,
allowed_entities,
dispensable,
swappable,
damage_on_hurt,
equip_on_interact,
can_be_sheared,
shearing_sound,
})
}
}
impl DataComponentCodec<Self> for PotionContentsImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
// Potion ID (optional)
if let Some(potion_id) = self.potion_id {
seq.serialize_field::<bool>("", &true)?;
seq.serialize_field::<VarInt>("", &VarInt::from(potion_id))?;
} else {
seq.serialize_field::<bool>("", &false)?;
}
// Custom color (optional)
if let Some(color) = self.custom_color {
seq.serialize_field::<bool>("", &true)?;
seq.serialize_field::<i32>("", &color)?;
} else {
seq.serialize_field::<bool>("", &false)?;
}
// Custom effects list
serialize_status_effects(&self.custom_effects, seq)?;
// Custom name (optional)
if let Some(name) = &self.custom_name {
seq.serialize_field::<bool>("", &true)?;
seq.serialize_field::<&str>("", &name.as_str())?;
} else {
seq.serialize_field::<bool>("", &false)?;
}
Ok(())
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
// Potion ID (optional)
let has_potion = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No PotionContents has_potion bool!"))?;
let potion_id = has_potion
.then(|| {
seq.next_element::<VarInt>()?
.ok_or(de::Error::custom("No PotionContents potion_id VarInt!"))
.map(|value| value.0)
})
.transpose()?;
// Custom color (optional)
let has_color = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No PotionContents has_color bool!"))?;
let custom_color = has_color
.then(|| {
seq.next_element::<i32>()?
.ok_or(de::Error::custom("No PotionContents custom_color i32!"))
})
.transpose()?;
// Custom effects list
let custom_effects = deserialize_status_effects(seq)?;
// Custom name (optional)
let has_name = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No PotionContents has_name bool!"))?;
let custom_name = has_name
.then(|| {
seq.next_element::<String>()?
.ok_or(de::Error::custom("No PotionContents custom_name String!"))
})
.transpose()?;
Ok(Self {
potion_id,
custom_color,
custom_effects,
custom_name,
})
}
}
/// Helper to skip hidden effect parameters recursively
fn skip_effect_parameters<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<(), A::Error> {
// amplifier
seq.next_element::<VarInt>()?
.ok_or(de::Error::custom("No hidden amplifier VarInt!"))?;
// duration
seq.next_element::<VarInt>()?
.ok_or(de::Error::custom("No hidden duration VarInt!"))?;
// ambient
seq.next_element::<bool>()?
.ok_or(de::Error::custom("No hidden ambient bool!"))?;
// show_particles
seq.next_element::<bool>()?
.ok_or(de::Error::custom("No hidden show_particles bool!"))?;
// show_icon
seq.next_element::<bool>()?
.ok_or(de::Error::custom("No hidden show_icon bool!"))?;
// has_hidden (recursive)
let has_hidden = seq
.next_element::<bool>()?
.ok_or(de::Error::custom("No hidden has_hidden bool!"))?;
if has_hidden {
skip_effect_parameters(seq)?;
}
Ok(())
}
impl DataComponentCodec<Self> for FireworkExplosionImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
// Shape (VarInt enum)
seq.serialize_field::<VarInt>("", &VarInt::from(self.shape.to_id()))?;
// Colors list
seq.serialize_field::<VarInt>("", &VarInt::from(self.colors.len() as i32))?;
for color in &self.colors {
seq.serialize_field::<i32>("", color)?;
}
// Fade colors list
seq.serialize_field::<VarInt>("", &VarInt::from(self.fade_colors.len() as i32))?;
for color in &self.fade_colors {
seq.serialize_field::<i32>("", color)?;
}
// hasTrail
seq.serialize_field::<bool>("", &self.has_trail)?;
// hasTwinkle
seq.serialize_field::<bool>("", &self.has_twinkle)?;
Ok(())
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
// Needs a length cap during deserialization to prevent OOM from malicious packets
// Vanilla doesn't have any limits (Integer.MAX_VALUE is technically a limit but not enforced in practice)
const MAX_COLORS: usize = 256;
const MAX_FADE_COLORS: usize = 256;
// Shape (VarInt enum)
let shape_id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom(
"No FireworkExplosionImpl shape_id VarInt!",
))?
.0;
let shape = FireworkExplosionShape::from_id(shape_id)
.ok_or(de::Error::custom("Invalid FireworkExplosionShape id!"))?;
// Colors list
let colors_len = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom(
"No FireworkExplosionImpl colors_len VarInt!",
))?
.0 as usize;
if colors_len > MAX_COLORS {
return Err(de::Error::custom(format!(
"FireworkExplosionImpl colors_len {colors_len} exceeds maximum of {MAX_COLORS}"
)));
}
let mut colors = Vec::with_capacity(colors_len);
for _ in 0..colors_len {
let color = seq
.next_element::<i32>()?
.ok_or(de::Error::custom("No FireworkExplosionImpl color i32!"))?;
colors.push(color);
}
// Fade colors list
let fade_colors_len = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom(
"No FireworkExplosionImpl fade_colors_len VarInt!",
))?
.0 as usize;
if fade_colors_len > MAX_FADE_COLORS {
return Err(de::Error::custom(format!(
"FireworkExplosionImpl fade_colors_len {fade_colors_len} exceeds maximum of {MAX_FADE_COLORS}"
)));
}
let mut fade_colors = Vec::with_capacity(fade_colors_len);
for _ in 0..fade_colors_len {
let color = seq.next_element::<i32>()?.ok_or(de::Error::custom(
"No FireworkExplosionImpl fade_color i32!",
))?;
fade_colors.push(color);
}
// hasTrail
let has_trail = seq.next_element::<bool>()?.ok_or(de::Error::custom(
"No FireworkExplosionImpl has_trail bool!",
))?;
// hasTwinkle
let has_twinkle = seq.next_element::<bool>()?.ok_or(de::Error::custom(
"No FireworkExplosionImpl has_twinkle bool!",
))?;
Ok(Self::new(
shape,
colors,
fade_colors,
has_trail,
has_twinkle,
))
}
}
impl DataComponentCodec<Self> for FireworksImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
// Flight duration (VarInt)
seq.serialize_field::<VarInt>("", &VarInt::from(self.flight_duration))?;
// Explosions list
seq.serialize_field::<VarInt>("", &VarInt::from(self.explosions.len() as i32))?;
for explosion in &self.explosions {
explosion.serialize(seq)?;
}
Ok(())
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
// Needs a length cap during deserialization to prevent OOM from malicious packets
// Vanilla doesn't have any limits
const MAX_EXPLOSIONS: usize = 256;
// Vanilla restricts to 0-255 (UNSIGNED_BYTE in data component codec) (do not trust client NBT to limit it)
const MAX_FLIGHT_DURATION: i32 = 255;
// Flight duration
let flight_duration = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom(
"No FireworksImpl flight_duration VarInt!",
))?
.0;
if !(0..=MAX_FLIGHT_DURATION).contains(&flight_duration) {
return Err(de::Error::custom(format!(
"FireworksImpl flight_duration {flight_duration} is out of bounds (0-{MAX_FLIGHT_DURATION})"
)));
}
// Explosions list
let explosions_len = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No FireworksImpl explosions_len VarInt!"))?
.0 as usize;
if explosions_len > MAX_EXPLOSIONS {
return Err(de::Error::custom(format!(
"FireworksImpl explosions_len {explosions_len} exceeds maximum of {MAX_EXPLOSIONS}"
)));
}
let mut explosions = Vec::with_capacity(explosions_len);
for _ in 0..explosions_len {
// Recursively deserialize each explosion
let explosion = FireworkExplosionImpl::deserialize(seq)?;
explosions.push(explosion);
}
Ok(Self::new(flight_duration, explosions))
}
}
impl DataComponentCodec<Self> for StoredEnchantmentsImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
seq.serialize_field::<VarInt>("", &VarInt::from(self.enchantment.len() as i32))?;
for (enc, level) in self.enchantment.iter() {
seq.serialize_field::<VarInt>("", &VarInt::from(enc.id))?;
seq.serialize_field::<VarInt>("", &VarInt::from(*level))?;
}
Ok(())
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
const MAX_ENCHANTMENTS: usize = 256;
let len = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No StoredEnchantmentsImpl len VarInt!"))?
.0 as usize;
if len > MAX_ENCHANTMENTS {
return Err(de::Error::custom("Too many enchantments"));
}
let mut stored_enchantments = Vec::with_capacity(len);
for _ in 0..len {
let id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No StoredEnchantmentsImpl id VarInt!"))?
.0 as u8;
let level = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No StoredEnchantmentsImpl level VarInt!"))?
.0;
stored_enchantments.push((
Enchantment::from_id(id).ok_or(de::Error::custom(
"StoredEnchantmentsImpl Enchantment VarInt Incorrect!",
))?,
level,
));
}
Ok(Self {
enchantment: Cow::from(stored_enchantments),
})
}
}
pub fn deserialize<'a, A: SeqAccess<'a>>(
id: DataComponent,
seq: &mut A,
) -> Result<Box<dyn DataComponentImpl>, A::Error> {
match id {
DataComponent::MaxStackSize => Ok(MaxStackSizeImpl::deserialize(seq)?.to_dyn()),
DataComponent::Enchantments => Ok(EnchantmentsImpl::deserialize(seq)?.to_dyn()),
DataComponent::Damage => Ok(DamageImpl::deserialize(seq)?.to_dyn()),
DataComponent::Unbreakable => Ok(UnbreakableImpl::deserialize(seq)?.to_dyn()),
DataComponent::PotionContents => Ok(PotionContentsImpl::deserialize(seq)?.to_dyn()),
DataComponent::FireworkExplosion => Ok(FireworkExplosionImpl::deserialize(seq)?.to_dyn()),
DataComponent::Fireworks => Ok(FireworksImpl::deserialize(seq)?.to_dyn()),
DataComponent::ItemModel => Ok(ItemModelImpl::deserialize(seq)?.to_dyn()),
DataComponent::CustomName => Ok(CustomNameImpl::deserialize(seq)?.to_dyn()),
DataComponent::Consumable => Ok(ConsumableImpl::deserialize(seq)?.to_dyn()),
DataComponent::Equippable => Ok(EquippableImpl::deserialize(seq)?.to_dyn()),
DataComponent::StoredEnchantments => Ok(StoredEnchantmentsImpl::deserialize(seq)?.to_dyn()),
DataComponent::UseCooldown => Ok(UseCooldownImpl::deserialize(seq)?.to_dyn()),
DataComponent::MapId => Ok(MapIdImpl::deserialize(seq)?.to_dyn()),
DataComponent::BundleContents => Ok(BundleContentsImpl::deserialize(seq)?.to_dyn()),
_ => Err(serde::de::Error::custom(format!("{id:?} (TODO)"))),
}
}
pub fn serialize<T: SerializeStruct>(
id: DataComponent,
value: &dyn DataComponentImpl,
seq: &mut T,
) -> Result<(), T::Error> {
match id {
DataComponent::MaxStackSize => get::<MaxStackSizeImpl>(value).serialize(seq),
DataComponent::Enchantments => get::<EnchantmentsImpl>(value).serialize(seq),
DataComponent::Damage => get::<DamageImpl>(value).serialize(seq),
DataComponent::Unbreakable => get::<UnbreakableImpl>(value).serialize(seq),
DataComponent::PotionContents => get::<PotionContentsImpl>(value).serialize(seq),
DataComponent::FireworkExplosion => get::<FireworkExplosionImpl>(value).serialize(seq),
DataComponent::Fireworks => get::<FireworksImpl>(value).serialize(seq),
DataComponent::ItemModel => get::<ItemModelImpl>(value).serialize(seq),
DataComponent::CustomName => get::<CustomNameImpl>(value).serialize(seq),
DataComponent::Consumable => get::<ConsumableImpl>(value).serialize(seq),
DataComponent::Equippable => get::<EquippableImpl>(value).serialize(seq),
DataComponent::StoredEnchantments => get::<StoredEnchantmentsImpl>(value).serialize(seq),
DataComponent::UseCooldown => get::<UseCooldownImpl>(value).serialize(seq),
DataComponent::MapId => get::<MapIdImpl>(value).serialize(seq),
DataComponent::BundleContents => get::<BundleContentsImpl>(value).serialize(seq),
_ => Err(serde::ser::Error::custom(format!(
"{} not yet implemented",
id.to_name()
))),
}
}
impl DataComponentCodec<Self> for MapIdImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
seq.serialize_field::<VarInt>("", &VarInt::from(self.id))
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
let id = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No MapId VarInt!"))?
.0;
Ok(Self { id })
}
}
impl DataComponentCodec<Self> for UseCooldownImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
seq.serialize_field::<f32>("", &self.seconds)?;
seq.serialize_field::<Option<String>>("", &self.cooldown_group)
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
let seconds = seq
.next_element::<f32>()?
.ok_or(de::Error::custom("No UseCooldownImpl seconds float!"))?;
let cooldown_group = seq
.next_element::<Option<String>>()?
.ok_or(de::Error::custom(
"No UseCooldownImpl cooldown_group optional string!",
))?;
Ok(Self {
seconds,
cooldown_group,
})
}
}
fn deserialize_item_stack_template<'a, A: SeqAccess<'a>>(
seq: &mut A,
) -> Result<pumpkin_data::item_stack::ItemStack, A::Error> {
let item_id = seq
.next_element::<VarInt>()?
.ok_or_else(|| de::Error::custom("Missing item_id in ItemStackTemplate"))?
.0 as u16;
let count = seq
.next_element::<VarInt>()?
.ok_or_else(|| de::Error::custom("Missing count in ItemStackTemplate"))?
.0 as u8;
let num_to_add = seq.next_element::<VarInt>()?.map_or(0, |v| v.0);
let num_to_remove = seq.next_element::<VarInt>()?.map_or(0, |v| v.0);
if num_to_add < 0 || num_to_remove < 0 {
return Err(de::Error::custom("Negative component count"));
}
const MAX_COMPONENTS: i32 = 256;
let total_components = num_to_add
.checked_add(num_to_remove)
.ok_or_else(|| de::Error::custom("Component count overflow"))?;
if total_components > MAX_COMPONENTS {
return Err(de::Error::custom(
"Too many components in ItemStackTemplate patch",
));
}
let mut patch = Vec::with_capacity((num_to_add + num_to_remove) as usize);
for _ in 0..num_to_add {
let id_val = seq
.next_element::<VarInt>()?
.ok_or_else(|| de::Error::custom("Missing component ID"))?
.0;
let id = DataComponent::try_from_id(id_val as u8)
.ok_or_else(|| de::Error::custom(format!("Unknown component ID: {id_val}")))?;
let _byte_len = seq
.next_element::<VarInt>()?
.ok_or_else(|| de::Error::custom("No data len VarInt!"))?;
let component_impl = deserialize(id, seq)?;
patch.push((id, Some(component_impl)));
}
for _ in 0..num_to_remove {
let id_val = seq
.next_element::<VarInt>()?
.ok_or_else(|| de::Error::custom("Missing remove component ID"))?
.0;
let id = DataComponent::try_from_id(id_val as u8)
.ok_or_else(|| de::Error::custom("Unknown component ID"))?;
patch.push((id, None));
}
Ok(pumpkin_data::item_stack::ItemStack::new_with_component(
count,
pumpkin_data::item::Item::from_id(item_id).unwrap_or(&pumpkin_data::item::Item::AIR),
patch,
))
}
fn serialize_item_stack_template<T: SerializeStruct>(
stack: &pumpkin_data::item_stack::ItemStack,
seq: &mut T,
) -> Result<(), T::Error> {
seq.serialize_field::<VarInt>("", &VarInt::from(stack.item.id))?;
seq.serialize_field::<VarInt>("", &VarInt::from(stack.item_count))?;
let mut to_add = 0u8;
let mut to_remove = 0u8;
for (_id, data) in &stack.patch {
if data.is_none() {
to_remove += 1;
} else {
to_add += 1;
}
}
seq.serialize_field::<VarInt>("", &VarInt::from(to_add))?;
seq.serialize_field::<VarInt>("", &VarInt::from(to_remove))?;
for (id, data) in &stack.patch {
if let Some(data) = data {
seq.serialize_field::<VarInt>("", &VarInt::from(id.to_id()))?;
serialize(*id, data.as_ref(), seq)?;
}
}
for (id, data) in &stack.patch {
if data.is_none() {
seq.serialize_field::<VarInt>("", &VarInt::from(id.to_id()))?;
}
}
Ok(())
}
impl DataComponentCodec<Self> for BundleContentsImpl {
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
seq.serialize_field::<VarInt>("", &VarInt::from(self.items.len() as i32))?;
for item in &self.items {
serialize_item_stack_template(item, seq)?;
}
Ok(())
}
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
const MAX_BUNDLE_ITEMS: usize = 64;
let len = seq
.next_element::<VarInt>()?
.ok_or(de::Error::custom("No BundleContentsImpl len VarInt!"))?
.0 as usize;
if len > MAX_BUNDLE_ITEMS {
return Err(de::Error::custom("Too many items in BundleContents"));
}
let mut items = Vec::with_capacity(len);
for _ in 0..len {
items.push(deserialize_item_stack_template(seq)?);
}
Ok(Self { items })
}
}