use crate::data_component_impl::{ DataComponentImpl, default_impl, get_f32_hash, get_i32_hash, get_str_hash, }; use crc_fast::CrcAlgorithm::Crc32Iscsi; use crc_fast::Digest; use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct DyeImpl; impl DataComponentImpl for DyeImpl { default_impl!(Dye); } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct DyedColorImpl { pub rgb: i32, } impl DyedColorImpl { pub fn read_data(data: &NbtTag) -> Option { data.extract_int().map(|rgb| Self { rgb }) } } impl DataComponentImpl for DyedColorImpl { fn write_data(&self) -> NbtTag { NbtTag::Int(self.rgb) } fn get_hash(&self) -> i32 { get_i32_hash(self.rgb) as i32 } default_impl!(DyedColor); } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct MapColorImpl; impl DataComponentImpl for MapColorImpl { default_impl!(MapColor); } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct MapIdImpl { pub id: i32, } impl MapIdImpl { pub fn read_data(data: &NbtTag) -> Option { data.extract_int().map(|id| Self { id }) } } impl DataComponentImpl for MapIdImpl { fn write_data(&self) -> NbtTag { NbtTag::Int(self.id) } fn get_hash(&self) -> i32 { get_i32_hash(self.id) as i32 } default_impl!(MapId); } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct MapDecorationsImpl; impl DataComponentImpl for MapDecorationsImpl { default_impl!(MapDecorations); } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct MapPostProcessingImpl; impl DataComponentImpl for MapPostProcessingImpl { default_impl!(MapPostProcessing); } #[derive(Clone, Debug, PartialEq)] pub struct ChargedProjectilesImpl { pub projectiles: Vec, } impl ChargedProjectilesImpl { pub fn read_data(data: &NbtTag) -> Option { let list = data.extract_list()?; let mut projectiles = Vec::new(); for item in list { projectiles.push(item.extract_compound()?.clone()); } Some(Self { projectiles }) } } impl DataComponentImpl for ChargedProjectilesImpl { fn write_data(&self) -> NbtTag { let mut list = Vec::new(); for item in &self.projectiles { list.push(NbtTag::Compound(item.clone())); } NbtTag::List(list) } fn get_hash(&self) -> i32 { 0 } default_impl!(ChargedProjectiles); } #[derive(Clone)] pub struct BundleContentsImpl { pub items: Vec, } impl PartialEq for BundleContentsImpl { fn eq(&self, _other: &Self) -> bool { false } } impl Eq for BundleContentsImpl {} impl std::fmt::Debug for BundleContentsImpl { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "BundleContentsImpl") } } impl BundleContentsImpl { pub fn read_data(tag: &NbtTag) -> Option { let mut items = Vec::new(); if let NbtTag::List(l) = tag { for item_tag in l { if let NbtTag::Compound(c) = item_tag && let Some(stack) = crate::item_stack::ItemStack::read_item_stack(c) { items.push(stack); } } } Some(Self { items }) } pub fn get_weight(&self) -> u32 { self.items .iter() .map(|item| item.item_count as u32 * (64 / item.get_max_stack_size() as u32).max(1)) .sum() } pub fn try_insert(&mut self, stack: &mut crate::item_stack::ItemStack) -> bool { if stack.is_empty() || stack.get_data_component::().is_some() { return false; } let weight_per_item = (64 / stack.get_max_stack_size() as u32).max(1); let mut inserted_anything = false; while stack.item_count > 0 && self.get_weight() + weight_per_item <= 64 { if let Some(top) = self.items.first_mut() && crate::item_stack::ItemStack::are_items_and_components_equal(top, stack) && top.item_count < top.get_max_stack_size() { top.item_count += 1; stack.item_count -= 1; inserted_anything = true; continue; } self.items.insert(0, stack.copy_with_count(1)); stack.item_count -= 1; inserted_anything = true; } inserted_anything } pub fn try_extract(&mut self) -> Option { if self.items.is_empty() { None } else { Some(self.items.remove(0)) } } } impl DataComponentImpl for BundleContentsImpl { default_impl!(BundleContents); } /// The dimension and block position a lodestone compass points to. #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct LodestoneTarget { pub dimension: String, pub x: i32, pub y: i32, pub z: i32, } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct LodestoneTrackerImpl { pub target: Option, pub tracked: bool, } impl LodestoneTrackerImpl { pub fn read_data(data: &NbtTag) -> Option { let compound = data.extract_compound()?; let tracked = compound.get_bool("tracked").unwrap_or(true); let target = compound.get_compound("target").and_then(|target| { let dimension = target.get_string("dimension")?.to_string(); let pos = target.get_int_array("pos")?; if pos.len() != 3 { return None; } Some(LodestoneTarget { dimension, x: pos[0], y: pos[1], z: pos[2], }) }); Some(Self { target, tracked }) } } impl DataComponentImpl for LodestoneTrackerImpl { fn write_data(&self) -> NbtTag { let mut compound = NbtCompound::new(); if let Some(target) = &self.target { let mut target_compound = NbtCompound::new(); target_compound.put_string("dimension", target.dimension.clone()); target_compound.put("pos", NbtTag::IntArray(vec![target.x, target.y, target.z])); compound.put_compound("target", target_compound); } compound.put_bool("tracked", self.tracked); NbtTag::Compound(compound) } default_impl!(LodestoneTracker); } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum FireworkExplosionShape { SmallBall = 0, LargeBall = 1, Star = 2, Creeper = 3, Burst = 4, } impl FireworkExplosionShape { pub fn from_id(id: i32) -> Option { match id { 0 => Some(Self::SmallBall), 1 => Some(Self::LargeBall), 2 => Some(Self::Star), 3 => Some(Self::Creeper), 4 => Some(Self::Burst), _ => None, } } pub fn to_id(&self) -> i32 { *self as i32 } pub fn to_name(&self) -> &str { match self { Self::SmallBall => "small_ball", Self::LargeBall => "large_ball", Self::Star => "star", Self::Creeper => "creeper", Self::Burst => "burst", } } pub fn from_name(name: &str) -> Option { match name { "small_ball" => Some(Self::SmallBall), "large_ball" => Some(Self::LargeBall), "star" => Some(Self::Star), "creeper" => Some(Self::Creeper), "burst" => Some(Self::Burst), _ => None, } } } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct FireworkExplosionImpl { pub shape: FireworkExplosionShape, pub colors: Vec, pub fade_colors: Vec, pub has_trail: bool, pub has_twinkle: bool, } impl FireworkExplosionImpl { pub fn new( shape: FireworkExplosionShape, colors: Vec, fade_colors: Vec, has_trail: bool, has_twinkle: bool, ) -> Self { Self { shape, colors, fade_colors, has_trail, has_twinkle, } } pub fn read_data(tag: &NbtTag) -> Option { let compound = tag.extract_compound()?; let shape = FireworkExplosionShape::from_name(compound.get_string("shape")?)?; let colors = compound .get_int_array("colors") .map(|v| v.to_vec()) .unwrap_or_default(); let fade_colors = compound .get_int_array("fade_colors") .map(|v| v.to_vec()) .unwrap_or_default(); let has_trail = compound.get_bool("has_trail").unwrap_or(false); let has_twinkle = compound.get_bool("has_twinkle").unwrap_or(false); Some(Self { shape, colors, fade_colors, has_trail, has_twinkle, }) } } impl DataComponentImpl for FireworkExplosionImpl { fn write_data(&self) -> NbtTag { let mut compound = NbtCompound::new(); compound.put_string("shape", self.shape.to_name().to_string()); compound.put("colors", NbtTag::IntArray(self.colors.clone())); compound.put("fade_colors", NbtTag::IntArray(self.fade_colors.clone())); compound.put_bool("has_trail", self.has_trail); compound.put_bool("has_twinkle", self.has_twinkle); NbtTag::Compound(compound) } fn get_hash(&self) -> i32 { let mut digest = Digest::new(Crc32Iscsi); digest.update(&[2u8]); digest.update(&[self.shape.to_id() as u8]); for color in &self.colors { digest.update(&get_i32_hash(*color).to_le_bytes()); } digest.update(&[3u8]); for color in &self.fade_colors { digest.update(&get_i32_hash(*color).to_le_bytes()); } digest.update(&[4u8]); digest.update(&[self.has_trail as u8]); digest.update(&[self.has_twinkle as u8]); digest.finalize() as i32 } default_impl!(FireworkExplosion); } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct FireworksImpl { pub flight_duration: i32, pub explosions: Vec, } impl FireworksImpl { pub fn new(flight_duration: i32, explosions: Vec) -> Self { Self { flight_duration, explosions, } } pub fn read_data(tag: &NbtTag) -> Option { let compound = tag.extract_compound()?; let flight_duration = compound .get_byte("flight_duration") .map(i32::from) .or_else(|| compound.get_int("flight_duration")) .unwrap_or(1); let mut explosions = Vec::new(); if let Some(list) = compound.get_list("explosions") { for item in list { if let Some(explosion) = FireworkExplosionImpl::read_data(item) { explosions.push(explosion); } } } Some(Self { flight_duration, explosions, }) } } impl DataComponentImpl for FireworksImpl { fn write_data(&self) -> NbtTag { let mut compound = NbtCompound::new(); compound.put_int("flight_duration", self.flight_duration); let explosions_list: Vec = self.explosions.iter().map(|e| e.write_data()).collect(); compound.put_list("explosions", explosions_list); NbtTag::Compound(compound) } fn get_hash(&self) -> i32 { let mut digest = Digest::new(Crc32Iscsi); digest.update(&[2u8]); digest.update(&get_i32_hash(self.flight_duration).to_le_bytes()); for explosion in &self.explosions { digest.update(&get_i32_hash(explosion.get_hash()).to_le_bytes()); } digest.update(&[3u8]); digest.finalize() as i32 } default_impl!(Fireworks); } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct ProfileProperty { pub name: String, pub value: String, pub signature: Option, } #[derive(Clone, Debug, Hash, PartialEq, Eq, Default)] pub struct ProfileImpl { pub name: Option, pub id: Option<[i32; 4]>, pub properties: Vec, pub texture: Option, pub cape: Option, pub elytra: Option, pub model: Option, } impl ProfileImpl { pub fn read_data(data: &NbtTag) -> Option { match data { NbtTag::String(name) => Some(Self { name: Some(name.to_string()), ..Default::default() }), NbtTag::Compound(compound) => { let name = compound.get_string("name").map(String::from); let id = compound.get_int_array("id").and_then(|arr| { if arr.len() == 4 { Some([arr[0], arr[1], arr[2], arr[3]]) } else { None } }); let mut properties = Vec::new(); if let Some(props_list) = compound.get_list("properties") { for prop_tag in props_list { if let Some(prop_comp) = prop_tag.extract_compound() && let (Some(prop_name), Some(prop_value)) = (prop_comp.get_string("name"), prop_comp.get_string("value")) { properties.push(ProfileProperty { name: prop_name.to_string(), value: prop_value.to_string(), signature: prop_comp.get_string("signature").map(String::from), }); } } } let texture = compound.get_string("texture").map(String::from); let cape = compound.get_string("cape").map(String::from); let elytra = compound.get_string("elytra").map(String::from); let model = compound.get_string("model").map(String::from); Some(Self { name, id, properties, texture, cape, elytra, model, }) } _ => None, } } } impl DataComponentImpl for ProfileImpl { fn write_data(&self) -> NbtTag { let mut compound = NbtCompound::new(); if let Some(name) = &self.name { compound.put_string("name", name.clone()); } if let Some(id) = &self.id { compound.put("id", NbtTag::IntArray(id.to_vec())); } if !self.properties.is_empty() { let mut props_list = Vec::new(); for prop in &self.properties { let mut prop_comp = NbtCompound::new(); prop_comp.put_string("name", prop.name.clone()); prop_comp.put_string("value", prop.value.clone()); if let Some(sig) = &prop.signature { prop_comp.put_string("signature", sig.clone()); } props_list.push(NbtTag::Compound(prop_comp)); } compound.put_list("properties", props_list); } if let Some(texture) = &self.texture { compound.put_string("texture", texture.clone()); } if let Some(cape) = &self.cape { compound.put_string("cape", cape.clone()); } if let Some(elytra) = &self.elytra { compound.put_string("elytra", elytra.clone()); } if let Some(model) = &self.model { compound.put_string("model", model.clone()); } NbtTag::Compound(compound) } fn get_hash(&self) -> i32 { let mut digest = Digest::new(Crc32Iscsi); if let Some(name) = &self.name { digest.update(&[1u8]); digest.update(&get_str_hash(name).to_le_bytes()); } if let Some(id) = &self.id { digest.update(&[2u8]); for val in id { digest.update(&get_i32_hash(*val).to_le_bytes()); } } if !self.properties.is_empty() { digest.update(&[3u8]); for prop in &self.properties { digest.update(&get_str_hash(&prop.name).to_le_bytes()); digest.update(&get_str_hash(&prop.value).to_le_bytes()); if let Some(sig) = &prop.signature { digest.update(&[1u8]); digest.update(&get_str_hash(sig).to_le_bytes()); } else { digest.update(&[0u8]); } } } if let Some(texture) = &self.texture { digest.update(&[4u8]); digest.update(&get_str_hash(texture).to_le_bytes()); } if let Some(cape) = &self.cape { digest.update(&[5u8]); digest.update(&get_str_hash(cape).to_le_bytes()); } if let Some(elytra) = &self.elytra { digest.update(&[6u8]); digest.update(&get_str_hash(elytra).to_le_bytes()); } if let Some(model) = &self.model { digest.update(&[7u8]); digest.update(&get_str_hash(model).to_le_bytes()); } digest.finalize() as i32 } default_impl!(Profile); } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct JukeboxPlayableImpl { pub song: &'static str, } impl DataComponentImpl for JukeboxPlayableImpl { default_impl!(JukeboxPlayable); } #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct RecipesImpl; impl DataComponentImpl for RecipesImpl { default_impl!(Recipes); }