fix: data component try_from_name

This commit is contained in:
Alexander Medvedev 2026-08-08 16:12:56 +02:00
parent ee6e0474ff
commit e5047bd4c9
6 changed files with 74 additions and 13 deletions

View File

@ -54,10 +54,17 @@ pub struct EnchantmentsImpl {
} }
impl EnchantmentsImpl { impl EnchantmentsImpl {
pub fn read_data(data: &NbtTag) -> Option<Self> { pub fn read_data(data: &NbtTag) -> Option<Self> {
let data = &data.extract_compound()?.child_tags; let compound = data.extract_compound()?;
let data = if let Some(NbtTag::Compound(levels)) = compound.child_tags.get("levels") {
&levels.child_tags
} else {
&compound.child_tags
};
let mut enc = Vec::with_capacity(data.len()); let mut enc = Vec::with_capacity(data.len());
for (name, level) in data { for (name, level) in data {
enc.push((Enchantment::from_name(name.as_ref())?, level.extract_int()?)); let enchantment = Enchantment::from_name(name.as_ref())
.or_else(|| Enchantment::from_name(&format!("minecraft:{name}")))?;
enc.push((enchantment, level.extract_int()?));
} }
Some(Self { Some(Self {
enchantment: Cow::from(enc), enchantment: Cow::from(enc),
@ -597,10 +604,17 @@ pub struct StoredEnchantmentsImpl {
} }
impl StoredEnchantmentsImpl { impl StoredEnchantmentsImpl {
pub fn read_data(data: &NbtTag) -> Option<Self> { pub fn read_data(data: &NbtTag) -> Option<Self> {
let data = &data.extract_compound()?.child_tags; let compound = data.extract_compound()?;
let data = if let Some(NbtTag::Compound(levels)) = compound.child_tags.get("levels") {
&levels.child_tags
} else {
&compound.child_tags
};
let mut enc = Vec::with_capacity(data.len()); let mut enc = Vec::with_capacity(data.len());
for (name, level) in data { for (name, level) in data {
enc.push((Enchantment::from_name(name.as_ref())?, level.extract_int()?)); let enchantment = Enchantment::from_name(name.as_ref())
.or_else(|| Enchantment::from_name(&format!("minecraft:{name}")))?;
enc.push((enchantment, level.extract_int()?));
} }
Some(Self { Some(Self {
enchantment: Cow::from(enc), enchantment: Cow::from(enc),

View File

@ -42,9 +42,11 @@ impl Clone for Box<dyn DataComponentImpl> {
pub fn get<T: DataComponentImpl + 'static>(value: &dyn DataComponentImpl) -> &T { pub fn get<T: DataComponentImpl + 'static>(value: &dyn DataComponentImpl) -> &T {
value.as_any().downcast_ref::<T>().unwrap_or_else(|| { value.as_any().downcast_ref::<T>().unwrap_or_else(|| {
panic!( panic!(
"you are trying to cast {} to {}", "you are trying to cast {} ({}) to {} ({})",
value.get_self_enum().to_name(), value.get_self_enum().to_name(),
T::get_enum().to_name() std::any::type_name_of_val(value),
T::get_enum().to_name(),
std::any::type_name::<T>()
) )
}) })
} }
@ -52,10 +54,12 @@ pub fn get<T: DataComponentImpl + 'static>(value: &dyn DataComponentImpl) -> &T
#[inline] #[inline]
pub fn get_mut<T: DataComponentImpl + 'static>(value: &mut dyn DataComponentImpl) -> &mut T { pub fn get_mut<T: DataComponentImpl + 'static>(value: &mut dyn DataComponentImpl) -> &mut T {
let name = value.get_self_enum().to_name(); let name = value.get_self_enum().to_name();
let val_type = std::any::type_name_of_val(value);
value.as_mut_any().downcast_mut::<T>().unwrap_or_else(|| { value.as_mut_any().downcast_mut::<T>().unwrap_or_else(|| {
panic!( panic!(
"you are trying to cast {name} to {}", "you are trying to cast {name} ({val_type}) to {} ({})",
T::get_enum().to_name() T::get_enum().to_name(),
std::any::type_name::<T>()
) )
}) })
} }
@ -63,7 +67,11 @@ pub fn get_mut<T: DataComponentImpl + 'static>(value: &mut dyn DataComponentImpl
macro_rules! default_impl { macro_rules! default_impl {
($t: ident) => { ($t: ident) => {
fn equal(&self, other: &dyn crate::data_component_impl::DataComponentImpl) -> bool { fn equal(&self, other: &dyn crate::data_component_impl::DataComponentImpl) -> bool {
self == crate::data_component_impl::get::<Self>(other) if let Some(other) = other.as_any().downcast_ref::<Self>() {
self == other
} else {
false
}
} }
#[inline] #[inline]
fn get_enum() -> crate::data_component::DataComponent fn get_enum() -> crate::data_component::DataComponent
@ -745,4 +753,33 @@ mod tests {
); );
assert_round_trip(LockImpl { predicate }, LockImpl::read_data); assert_round_trip(LockImpl { predicate }, LockImpl::read_data);
} }
#[test]
fn equal_with_different_types_returns_false() {
let enc = EnchantmentsImpl {
enchantment: Cow::Borrowed(&[(&crate::Enchantment::SHARPNESS, 2)]),
};
let max_stack = MaxStackSizeImpl { size: 64 };
assert!(!enc.equal(&max_stack));
}
#[test]
fn enchantments_read_data_formats() {
let mut direct = NbtCompound::new();
direct.put_int("sharpness", 2);
let enc1 = EnchantmentsImpl::read_data(&NbtTag::Compound(direct)).unwrap();
assert!(enc1.enchantment[0].0 == &crate::Enchantment::SHARPNESS);
assert_eq!(enc1.enchantment[0].1, 2);
let mut levels = NbtCompound::new();
levels.put_int("minecraft:sharpness", 2);
let mut wrapped = NbtCompound::new();
wrapped
.child_tags
.insert("levels".into(), NbtTag::Compound(levels));
let enc2 = EnchantmentsImpl::read_data(&NbtTag::Compound(wrapped)).unwrap();
assert_eq!(enc2.enchantment.len(), 1);
assert!(enc2.enchantment[0].0 == &crate::Enchantment::SHARPNESS);
assert_eq!(enc2.enchantment[0].1, 2);
}
} }

View File

@ -240,7 +240,12 @@ impl DataComponent {
#[must_use] #[must_use]
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
pub fn try_from_name(name: &str) -> Option<Self> { pub fn try_from_name(name: &str) -> Option<Self> {
match name { let name = if name.contains(':') {
std::borrow::Cow::Borrowed(name)
} else {
std::borrow::Cow::Owned(format!("minecraft:{name}"))
};
match name.as_ref() {
"minecraft:custom_data" => Some(Self::CustomData), "minecraft:custom_data" => Some(Self::CustomData),
"minecraft:max_stack_size" => Some(Self::MaxStackSize), "minecraft:max_stack_size" => Some(Self::MaxStackSize),
"minecraft:max_damage" => Some(Self::MaxDamage), "minecraft:max_damage" => Some(Self::MaxDamage),

View File

@ -984,7 +984,12 @@ impl Enchantment {
}, },
}; };
pub fn from_name(name: &str) -> Option<&'static Self> { pub fn from_name(name: &str) -> Option<&'static Self> {
match name { let name = if name.contains(':') {
std::borrow::Cow::Borrowed(name)
} else {
std::borrow::Cow::Owned(format!("minecraft:{name}"))
};
match name.as_ref() {
"minecraft:aqua_affinity" => Some(&Self::AQUA_AFFINITY), "minecraft:aqua_affinity" => Some(&Self::AQUA_AFFINITY),
"minecraft:bane_of_arthropods" => Some(&Self::BANE_OF_ARTHROPODS), "minecraft:bane_of_arthropods" => Some(&Self::BANE_OF_ARTHROPODS),
"minecraft:binding_curse" => Some(&Self::BINDING_CURSE), "minecraft:binding_curse" => Some(&Self::BINDING_CURSE),

View File

@ -36,7 +36,7 @@ pub fn build() -> TokenStream {
// TODO use phf // TODO use phf
name_to_enum.extend(quote! { name_to_enum.extend(quote! {
#raw_name => Some(Self::#pascal_case), #raw_name | #strip_name => Some(Self::#pascal_case),
}); });
// Enum -> &str // Enum -> &str

View File

@ -187,7 +187,7 @@ pub fn build() -> TokenStream {
}]); }]);
} }
name_to_type.extend(quote! { #name => Some(&Self::#format_name), }); name_to_type.extend(quote! { #name | #raw_name => Some(&Self::#format_name), });
id_to_type.extend(quote! { #id => Some(&Self::#format_name), }); id_to_type.extend(quote! { #id => Some(&Self::#format_name), });
} }