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 {
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());
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 {
enchantment: Cow::from(enc),
@ -597,10 +604,17 @@ pub struct StoredEnchantmentsImpl {
}
impl StoredEnchantmentsImpl {
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());
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 {
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 {
value.as_any().downcast_ref::<T>().unwrap_or_else(|| {
panic!(
"you are trying to cast {} to {}",
"you are trying to cast {} ({}) to {} ({})",
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]
pub fn get_mut<T: DataComponentImpl + 'static>(value: &mut dyn DataComponentImpl) -> &mut T {
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(|| {
panic!(
"you are trying to cast {name} to {}",
T::get_enum().to_name()
"you are trying to cast {name} ({val_type}) to {} ({})",
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 {
($t: ident) => {
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]
fn get_enum() -> crate::data_component::DataComponent
@ -745,4 +753,33 @@ mod tests {
);
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]
#[allow(clippy::too_many_lines)]
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:max_stack_size" => Some(Self::MaxStackSize),
"minecraft:max_damage" => Some(Self::MaxDamage),

View File

@ -984,7 +984,12 @@ impl Enchantment {
},
};
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:bane_of_arthropods" => Some(&Self::BANE_OF_ARTHROPODS),
"minecraft:binding_curse" => Some(&Self::BINDING_CURSE),

View File

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