azalea_core/registry_holder/
components.rs1use std::{any::Any, fmt::Debug, mem::ManuallyDrop, str::FromStr};
2
3use azalea_registry::builtin::{EnchantmentEffectComponentKind, SoundEvent};
4use simdnbt::{
5 DeserializeError,
6 borrow::{NbtCompound, NbtList, NbtTag},
7};
8
9use crate::registry_holder::{
10 entity_effect::EntityEffect,
11 get_in_compound,
12 value::{AttributeEffect, ValueEffect},
13};
14
15#[macro_export]
16macro_rules! define_effect_components {
17 ( $( $x:ident: $t:ty ),* $(,)? ) => {
18 #[allow(non_snake_case)]
19 pub union EffectComponentUnion {
20 $( $x: ManuallyDrop<$t>, )*
21 }
22
23 impl EffectComponentUnion {
24 pub unsafe fn drop_as(&mut self, kind: EnchantmentEffectComponentKind) {
28 match kind {
29 $( EnchantmentEffectComponentKind::$x => { unsafe { ManuallyDrop::drop(&mut self.$x) } }, )*
30 }
31 }
32
33 pub fn from_effect_nbt_tag_as(
34 kind: EnchantmentEffectComponentKind,
35 tag: EffectNbtTag,
36 ) -> Result<Self, DeserializeError> {
37 Ok(match kind {
38 $( EnchantmentEffectComponentKind::$x => {
39 Self { $x: ManuallyDrop::new(<$t>::from_effect_nbt_tag(tag)?) }
40 }, )*
41 })
42 }
43
44 pub unsafe fn clone_as(
48 &self,
49 kind: EnchantmentEffectComponentKind,
50 ) -> Self {
51 match kind {
52 $( EnchantmentEffectComponentKind::$x => {
53 Self { $x: unsafe { self.$x.clone() } }
54 }, )*
55 }
56 }
57
58 pub unsafe fn as_kind(&self, kind: EnchantmentEffectComponentKind) -> &dyn ResolvedEffectComponent {
62 match kind {
63 $( EnchantmentEffectComponentKind::$x => { unsafe { &**(&self.$x as &ManuallyDrop<dyn ResolvedEffectComponent>) } }, )*
64 }
65 }
66 }
67
68 $(
69 impl EffectComponentTrait for $t {
70 const KIND: EnchantmentEffectComponentKind = EnchantmentEffectComponentKind::$x;
71 }
72 )*
73 };
74}
75
76define_effect_components!(
77 DamageProtection: DamageProtection,
78 DamageImmunity: ConditionalEffect<DamageImmunity>,
79 Damage: Damage,
80 SmashDamagePerFallenBlock: SmashDamagePerFallenBlock,
81 Knockback: Knockback,
82 ArmorEffectiveness: ArmorEffectiveness,
83 PostAttack: PostAttack,
84 PostPiercingAttack: PostPiercingAttack,
85 HitBlock: ConditionalEntityEffect,
86 ItemDamage: ConditionalValueEffect,
87 Attributes: AttributeEffect,
88 EquipmentDrops: EquipmentDrops,
89 LocationChanged: ConditionalEffect<LocationBasedEffect>,
90 Tick: Tick,
91 AmmoUse: AmmoUse,
92 ProjectilePiercing: ProjectilePiercing,
93 ProjectileSpawned: ProjectileSpawned,
94 ProjectileSpread: ProjectileSpread,
95 ProjectileCount: ProjectileCount,
96 TridentReturnAcceleration: TridentReturnAcceleration,
97 FishingTimeReduction: FishingTimeReduction,
98 FishingLuckBonus: FishingLuckBonus,
99 BlockExperience: BlockExperience,
100 MobExperience: MobExperience,
101 RepairWithXp: RepairWithXp,
102 CrossbowChargeTime: CrossbowChargeTime,
103 CrossbowChargingSounds: CrossbowChargingSounds,
104 TridentSound: TridentSound,
105 PreventEquipmentDrop: PreventEquipmentDrop,
106 PreventArmorChange: PreventArmorChange,
107 TridentSpinAttackStrength: TridentSpinAttackStrength,
108);
109
110pub trait EffectComponentTrait: Any {
113 const KIND: EnchantmentEffectComponentKind;
114}
115
116pub trait ResolvedEffectComponent: Any {}
118impl<T: EffectComponentTrait> ResolvedEffectComponent for T {}
119
120#[derive(Clone, Copy, Debug)]
129pub enum EffectNbtTag<'a, 'tape> {
130 Compound(NbtCompound<'a, 'tape>),
131 List(NbtList<'a, 'tape>),
132}
133
134impl<'a, 'tape> EffectNbtTag<'a, 'tape> {
135 pub fn compound(self, error_name: &str) -> Result<NbtCompound<'a, 'tape>, DeserializeError> {
136 if let Self::Compound(nbt) = self {
137 Ok(nbt)
138 } else {
139 Err(DeserializeError::MismatchedFieldType(error_name.to_owned()))
140 }
141 }
142 pub fn list(self, error_name: &str) -> Result<NbtList<'a, 'tape>, DeserializeError> {
143 if let Self::List(nbt) = self {
144 Ok(nbt)
145 } else {
146 Err(DeserializeError::MismatchedFieldType(error_name.to_owned()))
147 }
148 }
149}
150macro_rules! impl_from_effect_nbt_tag {
151 (<$g:tt : $generic_type:tt $(::$generic_type2:tt)* $(+ $generic_type3:tt)+> $ty:ident <$generic_name:ident>) => {
152 impl<$g: $generic_type$(::$generic_type2)* $(+ $generic_type3)+> $ty<$generic_name> {
153 fn from_effect_nbt_tag(nbt: crate::registry_holder::components::EffectNbtTag) -> Result<Self, DeserializeError> {
154 let nbt = nbt.compound(stringify!($ty))?;
155 simdnbt::Deserialize::from_compound(nbt)
156 }
157 }
158 };
159 ($ty:ident) => {
160 impl $ty {
161 pub fn from_effect_nbt_tag(nbt: crate::registry_holder::components::EffectNbtTag) -> Result<Self, DeserializeError> {
162 let nbt = nbt.compound(stringify!($ty))?;
163 simdnbt::Deserialize::from_compound(nbt)
164 }
165 }
166 };
167}
168pub(crate) use impl_from_effect_nbt_tag;
169
170#[derive(Clone, Debug)]
171pub struct ConditionalEffect<T: simdnbt::Deserialize + Debug + Clone> {
172 pub effect: T,
173 }
175#[derive(Clone, Debug)]
176pub struct TargetedConditionalEffect<T: simdnbt::Deserialize + Debug + Clone> {
177 pub effect: T,
178 }
182
183type ConditionalValueEffect = ConditionalEffect<ValueEffect>;
185type ConditionalEntityEffect = ConditionalEffect<EntityEffect>;
186
187impl<T: simdnbt::Deserialize + Debug + Clone> simdnbt::Deserialize for ConditionalEffect<T> {
188 fn from_compound(nbt: NbtCompound) -> Result<Self, DeserializeError> {
189 let effect = get_in_compound(&nbt, "effect")?;
190 Ok(Self { effect })
191 }
192}
193impl_from_effect_nbt_tag!(<T: simdnbt::Deserialize + Debug + Clone> ConditionalEffect<T>);
194
195impl<T: simdnbt::Deserialize + Debug + Clone> simdnbt::Deserialize
196 for TargetedConditionalEffect<T>
197{
198 fn from_compound(nbt: NbtCompound) -> Result<Self, DeserializeError> {
199 let effect = get_in_compound(&nbt, "effect")?;
200 Ok(Self { effect })
201 }
202}
203
204macro_rules! declare_newtype_components {
205 ( $( $struct_name:ident: $inner_type:ty ),* $(,)? ) => {
206 $(
207 #[derive(Clone, Debug, simdnbt::Deserialize)]
208 pub struct $struct_name(pub $inner_type);
209 impl_from_effect_nbt_tag!($struct_name);
210 )*
211 };
212}
213
214declare_newtype_components! {
215 DamageProtection: ConditionalValueEffect,
216 Damage: ConditionalValueEffect,
217 SmashDamagePerFallenBlock: ConditionalValueEffect,
218 Knockback: ConditionalValueEffect,
219 ArmorEffectiveness: ConditionalValueEffect,
220 PostAttack: TargetedConditionalEffect<EntityEffect>,
221 PostPiercingAttack: ConditionalEffect<EntityEffect>,
222 HitBlock: ConditionalEntityEffect,
223 ItemDamage: ConditionalValueEffect,
224 EquipmentDrops: ConditionalValueEffect,
225 Tick: ConditionalEntityEffect,
226 AmmoUse: ConditionalValueEffect,
227 ProjectilePiercing: ConditionalValueEffect,
228 ProjectileSpawned: ConditionalEntityEffect,
229 ProjectileSpread: ConditionalValueEffect,
230 ProjectileCount: ConditionalValueEffect,
231 TridentReturnAcceleration: ConditionalValueEffect,
232 FishingTimeReduction: ConditionalValueEffect,
233 FishingLuckBonus: ConditionalValueEffect,
234 BlockExperience: ConditionalValueEffect,
235 MobExperience: ConditionalValueEffect,
236 RepairWithXp: ConditionalValueEffect,
237 CrossbowChargeTime: ValueEffect,
238 TridentSpinAttackStrength: ValueEffect,
239
240}
241
242#[derive(Clone, Debug, simdnbt::Deserialize)]
243pub struct DamageImmunity {}
244impl_from_effect_nbt_tag!(DamageImmunity);
245
246#[derive(Clone, Debug)]
247pub struct CrossbowChargingSounds(pub Vec<CrossbowChargingSound>);
248impl simdnbt::FromNbtTag for CrossbowChargingSounds {
249 fn from_nbt_tag(tag: NbtTag) -> Option<Self> {
250 simdnbt::FromNbtTag::from_nbt_tag(tag).map(Self)
251 }
252}
253impl CrossbowChargingSounds {
254 pub fn from_effect_nbt_tag(nbt: EffectNbtTag) -> Result<Self, DeserializeError> {
255 let Ok(nbt) = nbt.list("CrossbowChargingSounds") else {
256 return Ok(Self(vec![simdnbt::Deserialize::from_compound(
257 nbt.compound("CrossbowChargingSounds")?,
258 )?]));
259 };
260
261 Ok(Self(
262 nbt.compounds()
263 .ok_or_else(|| {
264 DeserializeError::MismatchedFieldType("CrossbowChargingSounds".to_owned())
265 })?
266 .into_iter()
267 .map(|c| simdnbt::Deserialize::from_compound(c))
268 .collect::<Result<_, _>>()?,
269 ))
270 }
271}
272
273#[derive(Clone, Debug, simdnbt::Deserialize)]
274pub struct CrossbowChargingSound {
275 pub start: Option<SoundEvent>,
276 pub mid: Option<SoundEvent>,
277 pub end: Option<SoundEvent>,
278}
279
280#[derive(Clone, Debug)]
281pub struct TridentSound(pub Vec<SoundEvent>);
282impl simdnbt::FromNbtTag for TridentSound {
283 fn from_nbt_tag(tag: NbtTag) -> Option<Self> {
284 let sounds = tag.list()?.strings()?;
285
286 sounds
287 .iter()
288 .map(|s| SoundEvent::from_str(&s.to_str()).ok())
289 .collect::<Option<Vec<_>>>()
290 .map(Self)
291 }
292}
293impl TridentSound {
294 pub fn from_effect_nbt_tag(nbt: EffectNbtTag) -> Result<Self, DeserializeError> {
295 let sounds = nbt
296 .list("TridentSound")?
297 .strings()
298 .ok_or_else(|| DeserializeError::MismatchedFieldType("TridentSound".to_owned()))?;
299
300 sounds
301 .iter()
302 .map(|s| SoundEvent::from_str(&s.to_str()).ok())
303 .collect::<Option<Vec<_>>>()
304 .ok_or_else(|| DeserializeError::MismatchedFieldType("TridentSound".to_owned()))
305 .map(Self)
306 }
307}
308
309#[derive(Clone, Debug, simdnbt::Deserialize)]
310pub struct LocationBasedEffect {
311 }
313impl_from_effect_nbt_tag!(LocationBasedEffect);
314
315#[derive(Clone, Debug, simdnbt::Deserialize)]
316pub struct PreventEquipmentDrop {}
317impl_from_effect_nbt_tag!(PreventEquipmentDrop);
318
319#[derive(Clone, Debug, simdnbt::Deserialize)]
320pub struct PreventArmorChange {}
321impl_from_effect_nbt_tag!(PreventArmorChange);