feat: Implement missing data components
This commit is contained in:
parent
3c6557d6db
commit
af3376e68e
@ -74,6 +74,92 @@ pub struct ItemComponents {
|
||||
pub weapon: Option<WeaponComponent>,
|
||||
#[serde(rename = "minecraft:enchantable")]
|
||||
pub enchantable: Option<EnchantableComponent>,
|
||||
#[serde(rename = "minecraft:attack_range")]
|
||||
pub attack_range: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:banner_patterns")]
|
||||
pub banner_patterns: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:bees")]
|
||||
pub bees: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:block_state")]
|
||||
pub block_state: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:break_sound")]
|
||||
pub break_sound: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:bucket_entity_data")]
|
||||
pub bucket_entity_data: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:bundle_contents")]
|
||||
pub bundle_contents: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:charged_projectiles")]
|
||||
pub charged_projectiles: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:chicken/variant")]
|
||||
pub chicken_variant: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:container")]
|
||||
pub container: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:damage_type")]
|
||||
pub damage_type: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:debug_stick_state")]
|
||||
pub debug_stick_state: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:dye")]
|
||||
pub dye: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:enchantment_glint_override")]
|
||||
pub enchantment_glint_override: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:enchantments")]
|
||||
pub enchantments: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:entity_data")]
|
||||
pub entity_data: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:fireworks")]
|
||||
pub fireworks: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:glider")]
|
||||
pub glider: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:instrument")]
|
||||
pub instrument: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:item_model")]
|
||||
pub item_model: Option<String>,
|
||||
#[serde(rename = "minecraft:kinetic_weapon")]
|
||||
pub kinetic_weapon: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:lore")]
|
||||
pub lore: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:map_color")]
|
||||
pub map_color: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:map_decorations")]
|
||||
pub map_decorations: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:minimum_attack_charge")]
|
||||
pub minimum_attack_charge: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:ominous_bottle_amplifier")]
|
||||
pub ominous_bottle_amplifier: Option<i32>,
|
||||
#[serde(rename = "minecraft:piercing_weapon")]
|
||||
pub piercing_weapon: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:pot_decorations")]
|
||||
pub pot_decorations: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:potion_contents")]
|
||||
pub potion_contents: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:potion_duration_scale")]
|
||||
pub potion_duration_scale: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:provides_banner_patterns")]
|
||||
pub provides_banner_patterns: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:provides_trim_material")]
|
||||
pub provides_trim_material: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:rarity")]
|
||||
pub rarity: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:recipes")]
|
||||
pub recipes: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:repair_cost")]
|
||||
pub repair_cost: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:repairable")]
|
||||
pub repairable: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:stored_enchantments")]
|
||||
pub stored_enchantments: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:suspicious_stew_effects")]
|
||||
pub suspicious_stew_effects: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:swing_animation")]
|
||||
pub swing_animation: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:tooltip_display")]
|
||||
pub tooltip_display: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:use_effects")]
|
||||
pub use_effects: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:use_remainder")]
|
||||
pub use_remainder: Option<serde_json::Value>,
|
||||
#[serde(rename = "minecraft:writable_book_content")]
|
||||
pub writable_book_content: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -612,6 +698,180 @@ impl ToTokens for ItemComponents {
|
||||
let value = LitInt::new(&enchantable.value.to_string(), Span::call_site());
|
||||
tokens.extend(quote! { (Enchantable, &EnchantableImpl { value: #value }), });
|
||||
}
|
||||
|
||||
if self.attack_range.is_some() {
|
||||
tokens.extend(quote! { (AttackRange, &AttackRangeImpl), });
|
||||
}
|
||||
if self.banner_patterns.is_some() {
|
||||
tokens.extend(quote! { (BannerPatterns, &BannerPatternsImpl), });
|
||||
}
|
||||
if self.bees.is_some() {
|
||||
tokens.extend(quote! { (Bees, &BeesImpl), });
|
||||
}
|
||||
if let Some(block_state) = &self.block_state {
|
||||
let mut entries = TokenStream::new();
|
||||
if let serde_json::Value::Object(map) = block_state {
|
||||
for (k, v) in map {
|
||||
let v_str = match v {
|
||||
serde_json::Value::String(s) => s.clone(),
|
||||
_ => v.to_string(),
|
||||
};
|
||||
let k_lit = LitStr::new(k, Span::call_site());
|
||||
let v_lit = LitStr::new(&v_str, Span::call_site());
|
||||
entries.extend(quote! {
|
||||
(Cow::Borrowed(#k_lit), Cow::Borrowed(#v_lit)),
|
||||
});
|
||||
}
|
||||
}
|
||||
tokens.extend(quote! {
|
||||
(BlockState, &BlockStateImpl {
|
||||
properties: Cow::Borrowed(&[#entries])
|
||||
}),
|
||||
});
|
||||
}
|
||||
if self.break_sound.is_some() {
|
||||
tokens.extend(quote! { (BreakSound, &BreakSoundImpl), });
|
||||
}
|
||||
if self.bucket_entity_data.is_some() {
|
||||
tokens.extend(quote! { (BucketEntityData, &BucketEntityDataImpl), });
|
||||
}
|
||||
if self.bundle_contents.is_some() {
|
||||
tokens.extend(quote! { (BundleContents, &BundleContentsImpl { items: Vec::new() }), });
|
||||
}
|
||||
if self.charged_projectiles.is_some() {
|
||||
tokens.extend(quote! { (ChargedProjectiles, &ChargedProjectilesImpl { projectiles: Vec::new() }), });
|
||||
}
|
||||
if let Some(val) = &self.chicken_variant {
|
||||
let val_str = match val {
|
||||
serde_json::Value::String(s) => s.clone(),
|
||||
_ => val.to_string(),
|
||||
};
|
||||
let val_lit = LitStr::new(&val_str, Span::call_site());
|
||||
tokens.extend(quote! { (ChickenVariant, &ChickenVariantImpl { value: Cow::Borrowed(#val_lit) }), });
|
||||
}
|
||||
if self.container.is_some() {
|
||||
tokens.extend(quote! { (Container, &ContainerImpl { items: Vec::new() }), });
|
||||
}
|
||||
if self.damage_type.is_some() {
|
||||
tokens.extend(quote! { (DamageType, &DamageTypeImpl), });
|
||||
}
|
||||
if self.debug_stick_state.is_some() {
|
||||
tokens.extend(quote! { (DebugStickState, &DebugStickStateImpl), });
|
||||
}
|
||||
if self.dye.is_some() {
|
||||
tokens.extend(quote! { (Dye, &DyeImpl), });
|
||||
}
|
||||
if self.enchantment_glint_override.is_some() {
|
||||
tokens.extend(quote! { (EnchantmentGlintOverride, &EnchantmentGlintOverrideImpl), });
|
||||
}
|
||||
if self.enchantments.is_some() {
|
||||
tokens.extend(quote! { (Enchantments, &EnchantmentsImpl { enchantment: Cow::Borrowed(&[]) }), });
|
||||
}
|
||||
if self.entity_data.is_some() {
|
||||
tokens.extend(quote! { (EntityData, &EntityDataImpl), });
|
||||
}
|
||||
if let Some(fireworks) = &self.fireworks {
|
||||
let flight_duration = if let serde_json::Value::Object(map) = fireworks
|
||||
&& let Some(serde_json::Value::Number(num)) = map.get("flight_duration")
|
||||
{
|
||||
num.as_i64().unwrap_or(1) as i32
|
||||
} else {
|
||||
1
|
||||
};
|
||||
tokens.extend(quote! {
|
||||
(Fireworks, &FireworksImpl {
|
||||
flight_duration: #flight_duration,
|
||||
explosions: Vec::new(),
|
||||
}),
|
||||
});
|
||||
}
|
||||
if self.glider.is_some() {
|
||||
tokens.extend(quote! { (Glider, &GliderImpl), });
|
||||
}
|
||||
if self.instrument.is_some() {
|
||||
tokens.extend(quote! { (Instrument, &InstrumentImpl), });
|
||||
}
|
||||
if let Some(model) = &self.item_model {
|
||||
let model_lit = LitStr::new(model, Span::call_site());
|
||||
tokens.extend(quote! { (ItemModel, &ItemModelImpl { id: Cow::Borrowed(#model_lit) }), });
|
||||
}
|
||||
if self.kinetic_weapon.is_some() {
|
||||
tokens.extend(quote! { (KineticWeapon, &KineticWeaponImpl), });
|
||||
}
|
||||
if self.lore.is_some() {
|
||||
tokens.extend(quote! { (Lore, &LoreImpl), });
|
||||
}
|
||||
if self.map_color.is_some() {
|
||||
tokens.extend(quote! { (MapColor, &MapColorImpl), });
|
||||
}
|
||||
if self.map_decorations.is_some() {
|
||||
tokens.extend(quote! { (MapDecorations, &MapDecorationsImpl), });
|
||||
}
|
||||
if self.minimum_attack_charge.is_some() {
|
||||
tokens.extend(quote! { (MinimumAttackCharge, &MinimumAttackChargeImpl), });
|
||||
}
|
||||
if let Some(amp) = self.ominous_bottle_amplifier {
|
||||
let amp_lit = LitInt::new(&.to_string(), Span::call_site());
|
||||
tokens.extend(quote! { (OminousBottleAmplifier, &OminousBottleAmplifierImpl { amplifier: #amp_lit }), });
|
||||
}
|
||||
if self.piercing_weapon.is_some() {
|
||||
tokens.extend(quote! { (PiercingWeapon, &PiercingWeaponImpl), });
|
||||
}
|
||||
if self.pot_decorations.is_some() {
|
||||
tokens.extend(quote! { (PotDecorations, &PotDecorationsImpl), });
|
||||
}
|
||||
if self.potion_contents.is_some() {
|
||||
tokens.extend(quote! {
|
||||
(PotionContents, &PotionContentsImpl {
|
||||
potion_id: None,
|
||||
custom_color: None,
|
||||
custom_effects: Vec::new(),
|
||||
custom_name: None,
|
||||
}),
|
||||
});
|
||||
}
|
||||
if self.potion_duration_scale.is_some() {
|
||||
tokens.extend(quote! { (PotionDurationScale, &PotionDurationScaleImpl), });
|
||||
}
|
||||
if self.provides_banner_patterns.is_some() {
|
||||
tokens.extend(quote! { (ProvidesBannerPatterns, &ProvidesBannerPatternsImpl), });
|
||||
}
|
||||
if self.provides_trim_material.is_some() {
|
||||
tokens.extend(quote! { (ProvidesTrimMaterial, &ProvidesTrimMaterialImpl), });
|
||||
}
|
||||
if self.rarity.is_some() {
|
||||
tokens.extend(quote! { (Rarity, &RarityImpl), });
|
||||
}
|
||||
if self.recipes.is_some() {
|
||||
tokens.extend(quote! { (Recipes, &RecipesImpl), });
|
||||
}
|
||||
if self.repair_cost.is_some() {
|
||||
tokens.extend(quote! { (RepairCost, &RepairCostImpl), });
|
||||
}
|
||||
if self.repairable.is_some() {
|
||||
tokens.extend(quote! { (Repairable, &RepairableImpl), });
|
||||
}
|
||||
if self.stored_enchantments.is_some() {
|
||||
tokens.extend(quote! { (StoredEnchantments, &StoredEnchantmentsImpl { enchantment: Cow::Borrowed(&[]) }), });
|
||||
}
|
||||
if self.suspicious_stew_effects.is_some() {
|
||||
tokens.extend(quote! { (SuspiciousStewEffects, &SuspiciousStewEffectsImpl), });
|
||||
}
|
||||
if self.swing_animation.is_some() {
|
||||
tokens.extend(quote! { (SwingAnimation, &SwingAnimationImpl), });
|
||||
}
|
||||
if self.tooltip_display.is_some() {
|
||||
tokens.extend(quote! { (TooltipDisplay, &TooltipDisplayImpl), });
|
||||
}
|
||||
if self.use_effects.is_some() {
|
||||
tokens.extend(quote! { (UseEffects, &UseEffectsImpl), });
|
||||
}
|
||||
if self.use_remainder.is_some() {
|
||||
tokens.extend(quote! { (UseRemainder, &UseRemainderImpl), });
|
||||
}
|
||||
if self.writable_book_content.is_some() {
|
||||
tokens.extend(quote! { (WritableBookContent, &WritableBookContentImpl { pages: Vec::new() }), });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
265
pumpkin-data/src/data_component_impl/basic.rs
Normal file
265
pumpkin-data/src/data_component_impl/basic.rs
Normal file
@ -0,0 +1,265 @@
|
||||
use crate::data_component_impl::{DataComponentImpl, default_impl, get_i32_hash, get_str_hash};
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct CustomDataImpl {
|
||||
pub data: NbtCompound,
|
||||
}
|
||||
impl CustomDataImpl {
|
||||
#[must_use]
|
||||
pub const fn new(data: NbtCompound) -> Self {
|
||||
Self { data }
|
||||
}
|
||||
#[must_use]
|
||||
pub fn read_data(tag: &NbtTag) -> Option<Self> {
|
||||
if let NbtTag::Compound(c) = tag {
|
||||
Some(Self { data: c.clone() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for CustomDataImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
NbtTag::Compound(self.data.clone())
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
0
|
||||
}
|
||||
default_impl!(CustomData);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct MaxStackSizeImpl {
|
||||
pub size: u8,
|
||||
}
|
||||
impl MaxStackSizeImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
data.extract_int().map(|size| Self { size: size as u8 })
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for MaxStackSizeImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
NbtTag::Int(self.size as i32)
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
get_i32_hash(self.size as i32) as i32
|
||||
}
|
||||
default_impl!(MaxStackSize);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct MaxDamageImpl {
|
||||
pub max_damage: i32,
|
||||
}
|
||||
impl DataComponentImpl for MaxDamageImpl {
|
||||
default_impl!(MaxDamage);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct DamageImpl {
|
||||
pub damage: i32,
|
||||
}
|
||||
impl DamageImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
data.extract_int().map(|damage| Self { damage })
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for DamageImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
NbtTag::Int(self.damage)
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
get_i32_hash(self.damage) as i32
|
||||
}
|
||||
default_impl!(Damage);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct UnbreakableImpl;
|
||||
impl UnbreakableImpl {
|
||||
pub const fn read_data(_data: &NbtTag) -> Option<Self> {
|
||||
Some(Self)
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for UnbreakableImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
NbtTag::Compound(NbtCompound::new())
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
0
|
||||
}
|
||||
default_impl!(Unbreakable);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct CustomNameImpl {
|
||||
pub name: TextComponent,
|
||||
}
|
||||
impl CustomNameImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
data.extract_string().map(|name| Self {
|
||||
name: TextComponent::text(name.to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for CustomNameImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
NbtTag::String(self.name.clone().get_text().into())
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
get_str_hash(self.name.clone().get_text().as_str()) as i32
|
||||
}
|
||||
default_impl!(CustomName);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct ItemNameImpl {
|
||||
pub name: &'static str,
|
||||
}
|
||||
impl DataComponentImpl for ItemNameImpl {
|
||||
default_impl!(ItemName);
|
||||
}
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct ItemModelImpl {
|
||||
pub id: Cow<'static, str>,
|
||||
}
|
||||
impl ItemModelImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
data.extract_string().map(|id| Self {
|
||||
id: Cow::Owned(id.to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for ItemModelImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
NbtTag::String(self.id.clone().into_owned().into())
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
get_str_hash(self.id.as_ref()) as i32
|
||||
}
|
||||
default_impl!(ItemModel);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct LoreImpl;
|
||||
impl DataComponentImpl for LoreImpl {
|
||||
default_impl!(Lore);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct RarityImpl;
|
||||
impl DataComponentImpl for RarityImpl {
|
||||
default_impl!(Rarity);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct CustomModelDataImpl;
|
||||
impl DataComponentImpl for CustomModelDataImpl {
|
||||
default_impl!(CustomModelData);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct TooltipDisplayImpl;
|
||||
impl DataComponentImpl for TooltipDisplayImpl {
|
||||
default_impl!(TooltipDisplay);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct CreativeSlotLockImpl;
|
||||
impl DataComponentImpl for CreativeSlotLockImpl {
|
||||
default_impl!(CreativeSlotLock);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct EnchantmentGlintOverrideImpl;
|
||||
impl DataComponentImpl for EnchantmentGlintOverrideImpl {
|
||||
default_impl!(EnchantmentGlintOverride);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct TooltipStyleImpl;
|
||||
impl DataComponentImpl for TooltipStyleImpl {
|
||||
default_impl!(TooltipStyle);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct NoteBlockSoundImpl;
|
||||
impl DataComponentImpl for NoteBlockSoundImpl {
|
||||
default_impl!(NoteBlockSound);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct BaseColorImpl;
|
||||
impl DataComponentImpl for BaseColorImpl {
|
||||
default_impl!(BaseColor);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct InstrumentImpl;
|
||||
impl DataComponentImpl for InstrumentImpl {
|
||||
default_impl!(Instrument);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct ProvidesTrimMaterialImpl;
|
||||
impl DataComponentImpl for ProvidesTrimMaterialImpl {
|
||||
default_impl!(ProvidesTrimMaterial);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct ProvidesBannerPatternsImpl;
|
||||
impl DataComponentImpl for ProvidesBannerPatternsImpl {
|
||||
default_impl!(ProvidesBannerPatterns);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct BannerPatternsImpl;
|
||||
impl DataComponentImpl for BannerPatternsImpl {
|
||||
default_impl!(BannerPatterns);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct PotDecorationsImpl;
|
||||
impl DataComponentImpl for PotDecorationsImpl {
|
||||
default_impl!(PotDecorations);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct LockImpl;
|
||||
impl DataComponentImpl for LockImpl {
|
||||
default_impl!(Lock);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct BreakSoundImpl;
|
||||
impl DataComponentImpl for BreakSoundImpl {
|
||||
default_impl!(BreakSound);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct SoundEvent {
|
||||
pub sound_name: String,
|
||||
pub range: Option<f32>,
|
||||
}
|
||||
impl std::hash::Hash for SoundEvent {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.sound_name.hash(state);
|
||||
if let Some(val) = self.range {
|
||||
true.hash(state);
|
||||
unsafe { (*(&raw const val).cast::<u32>()).hash(state) };
|
||||
} else {
|
||||
false.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl SoundEvent {
|
||||
pub const fn new(sound_name: String, range: Option<f32>) -> Self {
|
||||
Self { sound_name, range }
|
||||
}
|
||||
}
|
||||
150
pumpkin-data/src/data_component_impl/block_entity.rs
Normal file
150
pumpkin-data/src/data_component_impl/block_entity.rs
Normal file
@ -0,0 +1,150 @@
|
||||
use crate::data_component_impl::{DataComponentImpl, default_impl, 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, PartialEq)]
|
||||
pub struct BlockEntityDataImpl {
|
||||
pub nbt: NbtCompound,
|
||||
}
|
||||
impl BlockEntityDataImpl {
|
||||
pub fn read_data(tag: &NbtTag) -> Option<Self> {
|
||||
if let NbtTag::Compound(c) = tag {
|
||||
Some(Self { nbt: c.clone() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for BlockEntityDataImpl {
|
||||
default_impl!(BlockEntityData);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct EntityDataImpl;
|
||||
impl DataComponentImpl for EntityDataImpl {
|
||||
default_impl!(EntityData);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct BucketEntityDataImpl;
|
||||
impl DataComponentImpl for BucketEntityDataImpl {
|
||||
default_impl!(BucketEntityData);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ContainerImpl {
|
||||
pub items: Vec<(u8, crate::item_stack::ItemStack)>,
|
||||
}
|
||||
impl PartialEq for ContainerImpl {
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
impl Eq for ContainerImpl {}
|
||||
impl std::fmt::Debug for ContainerImpl {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "ContainerImpl")
|
||||
}
|
||||
}
|
||||
impl ContainerImpl {
|
||||
pub fn read_data(tag: &NbtTag) -> Option<Self> {
|
||||
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(slot) = c.get_int("slot")
|
||||
&& let Some(item_compound) = c.get_compound("item")
|
||||
&& let Some(stack) =
|
||||
crate::item_stack::ItemStack::read_item_stack(item_compound)
|
||||
{
|
||||
items.push((slot as u8, stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Self { items })
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for ContainerImpl {
|
||||
default_impl!(Container);
|
||||
}
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BlockStateImpl {
|
||||
pub properties: Cow<'static, [(Cow<'static, str>, Cow<'static, str>)]>,
|
||||
}
|
||||
impl PartialEq for BlockStateImpl {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
let mut self_props = self.properties.to_vec();
|
||||
self_props.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
let mut other_props = other.properties.to_vec();
|
||||
other_props.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
self_props == other_props
|
||||
}
|
||||
}
|
||||
impl Eq for BlockStateImpl {}
|
||||
impl std::hash::Hash for BlockStateImpl {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
let mut props = self.properties.to_vec();
|
||||
props.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
for (k, v) in props {
|
||||
k.hash(state);
|
||||
v.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
impl BlockStateImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
let compound = data.extract_compound()?;
|
||||
let mut properties = Vec::new();
|
||||
for (key, val) in compound.child_tags.iter() {
|
||||
if let Some(s) = val.extract_string() {
|
||||
properties.push((Cow::Owned(key.to_string()), Cow::Owned(s.to_string())));
|
||||
}
|
||||
}
|
||||
Some(Self {
|
||||
properties: Cow::Owned(properties),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for BlockStateImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
let mut compound = NbtCompound::new();
|
||||
for (k, v) in self.properties.iter() {
|
||||
compound.put_string(k.as_ref(), v.to_string());
|
||||
}
|
||||
NbtTag::Compound(compound)
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
let mut props = self.properties.to_vec();
|
||||
props.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
for (k, v) in props {
|
||||
digest.update(k.as_ref().as_bytes());
|
||||
digest.update(v.as_ref().as_bytes());
|
||||
}
|
||||
digest.finalize() as i32
|
||||
}
|
||||
default_impl!(BlockState);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct BeesImpl;
|
||||
impl DataComponentImpl for BeesImpl {
|
||||
default_impl!(Bees);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct ContainerLootImpl;
|
||||
impl DataComponentImpl for ContainerLootImpl {
|
||||
default_impl!(ContainerLoot);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct SulfurCubeContentImpl;
|
||||
impl DataComponentImpl for SulfurCubeContentImpl {
|
||||
default_impl!(SulfurCubeContent);
|
||||
}
|
||||
50
pumpkin-data/src/data_component_impl/book.rs
Normal file
50
pumpkin-data/src/data_component_impl/book.rs
Normal file
@ -0,0 +1,50 @@
|
||||
use crate::data_component_impl::{DataComponentImpl, default_impl};
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct WritableBookContentImpl {
|
||||
pub pages: Vec<String>,
|
||||
}
|
||||
impl WritableBookContentImpl {
|
||||
pub fn read_data(tag: &NbtTag) -> Option<Self> {
|
||||
let mut pages = Vec::new();
|
||||
if let NbtTag::Compound(c) = tag
|
||||
&& let Some(NbtTag::List(l)) = c.get("pages")
|
||||
{
|
||||
for _ in l {
|
||||
pages.push(String::new());
|
||||
}
|
||||
}
|
||||
Some(Self { pages })
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for WritableBookContentImpl {
|
||||
default_impl!(WritableBookContent);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct WrittenBookContentImpl {
|
||||
pub pages: Vec<String>,
|
||||
}
|
||||
impl WrittenBookContentImpl {
|
||||
pub fn read_data(tag: &NbtTag) -> Option<Self> {
|
||||
let mut pages = Vec::new();
|
||||
if let NbtTag::Compound(c) = tag
|
||||
&& let Some(NbtTag::List(l)) = c.get("pages")
|
||||
{
|
||||
for _ in l {
|
||||
pages.push(String::new());
|
||||
}
|
||||
}
|
||||
Some(Self { pages })
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for WrittenBookContentImpl {
|
||||
default_impl!(WrittenBookContent);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct DebugStickStateImpl;
|
||||
impl DataComponentImpl for DebugStickStateImpl {
|
||||
default_impl!(DebugStickState);
|
||||
}
|
||||
620
pumpkin-data/src/data_component_impl/combat.rs
Normal file
620
pumpkin-data/src/data_component_impl/combat.rs
Normal file
@ -0,0 +1,620 @@
|
||||
use crate::Block;
|
||||
use crate::Enchantment;
|
||||
use crate::attributes::Attributes;
|
||||
use crate::data_component_impl::basic::SoundEvent;
|
||||
use crate::data_component_impl::{
|
||||
DataComponentImpl, EquipmentSlot, IDSet, IDSetContent, IdOr, default_impl, get_f32_hash,
|
||||
get_i32_hash, get_idor, get_idor_hash, get_idset_hash, get_str_hash, put_idor,
|
||||
};
|
||||
use crate::entity_type::EntityType;
|
||||
use crate::sound::Sound;
|
||||
use crc_fast::CrcAlgorithm::Crc32Iscsi;
|
||||
use crc_fast::Digest;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use std::borrow::Cow;
|
||||
use std::hash::Hash;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum Operation {
|
||||
AddValue,
|
||||
AddMultipliedBase,
|
||||
AddMultipliedTotal,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Modifier {
|
||||
pub r#type: &'static Attributes,
|
||||
pub id: &'static str,
|
||||
pub amount: f64,
|
||||
pub operation: Operation,
|
||||
pub slot: crate::AttributeModifierSlot,
|
||||
}
|
||||
impl Hash for Modifier {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.r#type.hash(state);
|
||||
self.id.hash(state);
|
||||
unsafe { (*(&raw const self.amount).cast::<u64>()).hash(state) };
|
||||
self.operation.hash(state);
|
||||
self.slot.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq)]
|
||||
pub struct AttributeModifiersImpl {
|
||||
pub attribute_modifiers: Cow<'static, [Modifier]>,
|
||||
}
|
||||
impl DataComponentImpl for AttributeModifiersImpl {
|
||||
default_impl!(AttributeModifiers);
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Default)]
|
||||
pub struct EnchantmentsImpl {
|
||||
pub enchantment: Cow<'static, [(&'static Enchantment, i32)]>,
|
||||
}
|
||||
impl EnchantmentsImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
let data = &data.extract_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()?));
|
||||
}
|
||||
Some(Self {
|
||||
enchantment: Cow::from(enc),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for EnchantmentsImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
let mut data = NbtCompound::new();
|
||||
for (enc, level) in self.enchantment.iter() {
|
||||
data.put_int(enc.name, *level);
|
||||
}
|
||||
NbtTag::Compound(data)
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
digest.update(&[2u8]);
|
||||
for (enc, level) in self.enchantment.iter() {
|
||||
digest.update(&get_str_hash(enc.name).to_le_bytes());
|
||||
digest.update(&get_i32_hash(*level).to_le_bytes());
|
||||
}
|
||||
digest.update(&[3u8]);
|
||||
digest.finalize() as i32
|
||||
}
|
||||
default_impl!(Enchantments);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct CanPlaceOnImpl;
|
||||
impl DataComponentImpl for CanPlaceOnImpl {
|
||||
default_impl!(CanPlaceOn);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct CanBreakImpl;
|
||||
impl DataComponentImpl for CanBreakImpl {
|
||||
default_impl!(CanBreak);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct RepairCostImpl;
|
||||
impl DataComponentImpl for RepairCostImpl {
|
||||
default_impl!(RepairCost);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct IntangibleProjectileImpl;
|
||||
impl DataComponentImpl for IntangibleProjectileImpl {
|
||||
default_impl!(IntangibleProjectile);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum DamageResistantType {
|
||||
AlwaysHurtsEnderDragons,
|
||||
AlwaysKillsArmorStands,
|
||||
AlwaysMostSignificantFall,
|
||||
AlwaysTriggersSilverfish,
|
||||
AvoidsGuardianThorns,
|
||||
BurnsArmorStands,
|
||||
BurnFromStepping,
|
||||
BypassesArmor,
|
||||
BypassesCooldown,
|
||||
BypassesEffects,
|
||||
BypassesEnchantments,
|
||||
BypassesInvulnerability,
|
||||
BypassesResistance,
|
||||
BypassesShield,
|
||||
BypassesWolfArmor,
|
||||
CanBreakArmorStands,
|
||||
DamagesHelmet,
|
||||
IgnitesArmorStands,
|
||||
Drowning,
|
||||
Explosion,
|
||||
Fall,
|
||||
Fire,
|
||||
Freezing,
|
||||
Lightning,
|
||||
PlayerAttack,
|
||||
Projectile,
|
||||
MaceSmash,
|
||||
NoAnger,
|
||||
NoImpact,
|
||||
NoKnockback,
|
||||
PanicCauses,
|
||||
PanicEnvironmentalCauses,
|
||||
WitchResistantTo,
|
||||
WitherImmuneTo,
|
||||
Generic,
|
||||
}
|
||||
|
||||
impl DamageResistantType {
|
||||
pub fn from_tag(s: &str) -> Self {
|
||||
match s {
|
||||
"#minecraft:always_hurts_ender_dragons"
|
||||
| "minecraft:always_hurts_ender_dragons"
|
||||
| "always_hurts_ender_dragons" => Self::AlwaysHurtsEnderDragons,
|
||||
"#minecraft:always_kills_armor_stands"
|
||||
| "minecraft:always_kills_armor_stands"
|
||||
| "always_kills_armor_stands" => Self::AlwaysKillsArmorStands,
|
||||
"#minecraft:always_most_significant_fall"
|
||||
| "minecraft:always_most_significant_fall"
|
||||
| "always_most_significant_fall" => Self::AlwaysMostSignificantFall,
|
||||
"#minecraft:always_triggers_silverfish"
|
||||
| "minecraft:always_triggers_silverfish"
|
||||
| "always_triggers_silverfish" => Self::AlwaysTriggersSilverfish,
|
||||
"#minecraft:avoids_guardian_thorns"
|
||||
| "minecraft:avoids_guardian_thorns"
|
||||
| "avoids_guardian_thorns" => Self::AvoidsGuardianThorns,
|
||||
"#minecraft:burns_armor_stands"
|
||||
| "minecraft:burns_armor_stands"
|
||||
| "burns_armor_stands" => Self::BurnsArmorStands,
|
||||
"#minecraft:burn_from_stepping"
|
||||
| "minecraft:burn_from_stepping"
|
||||
| "burn_from_stepping" => Self::BurnFromStepping,
|
||||
"#minecraft:bypasses_armor" | "minecraft:bypasses_armor" | "bypasses_armor" => {
|
||||
Self::BypassesArmor
|
||||
}
|
||||
"#minecraft:bypasses_cooldown"
|
||||
| "minecraft:bypasses_cooldown"
|
||||
| "bypasses_cooldown" => Self::BypassesCooldown,
|
||||
"#minecraft:bypasses_effects" | "minecraft:bypasses_effects" | "bypasses_effects" => {
|
||||
Self::BypassesEffects
|
||||
}
|
||||
"#minecraft:bypasses_enchantments"
|
||||
| "minecraft:bypasses_enchantments"
|
||||
| "bypasses_enchantments" => Self::BypassesEnchantments,
|
||||
"#minecraft:bypasses_invulnerability"
|
||||
| "minecraft:bypasses_invulnerability"
|
||||
| "bypasses_invulnerability" => Self::BypassesInvulnerability,
|
||||
"#minecraft:bypasses_resistance"
|
||||
| "minecraft:bypasses_resistance"
|
||||
| "bypasses_resistance" => Self::BypassesResistance,
|
||||
"#minecraft:bypasses_shield" | "minecraft:bypasses_shield" | "bypasses_shield" => {
|
||||
Self::BypassesShield
|
||||
}
|
||||
"#minecraft:bypasses_wolf_armor"
|
||||
| "minecraft:bypasses_wolf_armor"
|
||||
| "bypasses_wolf_armor" => Self::BypassesWolfArmor,
|
||||
"#minecraft:can_break_armor_stand"
|
||||
| "minecraft:can_break_armor_stand"
|
||||
| "can_break_armor_stand" => Self::CanBreakArmorStands,
|
||||
"#minecraft:damages_helmet" | "minecraft:damages_helmet" | "damages_helmet" => {
|
||||
Self::DamagesHelmet
|
||||
}
|
||||
"#minecraft:ignites_armor_stands"
|
||||
| "minecraft:ignites_armor_stands"
|
||||
| "ignites_armor_stands" => Self::IgnitesArmorStands,
|
||||
"#minecraft:is_drowning" | "minecraft:is_drowning" | "is_drowning" => Self::Drowning,
|
||||
"#minecraft:is_explosion" | "minecraft:is_explosion" | "is_explosion" | "explosion" => {
|
||||
Self::Explosion
|
||||
}
|
||||
"#minecraft:is_fall" | "minecraft:is_fall" | "is_fall" | "fall" => Self::Fall,
|
||||
"#minecraft:is_fire" | "minecraft:is_fire" | "is_fire" | "fire" | "in_fire"
|
||||
| "minecraft:in_fire" => Self::Fire,
|
||||
"#minecraft:is_freezing" | "minecraft:is_freezing" | "is_freezing" => Self::Freezing,
|
||||
"#minecraft:is_lightning" | "minecraft:is_lightning" | "is_lightning" => {
|
||||
Self::Lightning
|
||||
}
|
||||
"#minecraft:is_player_attack" | "minecraft:is_player_attack" | "is_player_attack" => {
|
||||
Self::PlayerAttack
|
||||
}
|
||||
"#minecraft:is_projectile" | "minecraft:is_projectile" | "is_projectile" => {
|
||||
Self::Projectile
|
||||
}
|
||||
"#minecraft:mace_smash" | "minecraft:mace_smash" | "mace_smash" => Self::MaceSmash,
|
||||
"#minecraft:no_anger" | "minecraft:no_anger" | "no_anger" => Self::NoAnger,
|
||||
"#minecraft:no_impact" | "minecraft:no_impact" | "no_impact" => Self::NoImpact,
|
||||
"#minecraft:no_knockback" | "minecraft:no_knockback" | "no_knockback" => {
|
||||
Self::NoKnockback
|
||||
}
|
||||
"#minecraft:panic_causes" | "minecraft:panic_causes" | "panic_causes" => {
|
||||
Self::PanicCauses
|
||||
}
|
||||
"#minecraft:panic_environmental_causes"
|
||||
| "minecraft:panic_environmental_causes"
|
||||
| "panic_environmental_causes" => Self::PanicEnvironmentalCauses,
|
||||
"#minecraft:witch_resistant_to"
|
||||
| "minecraft:witch_resistant_to"
|
||||
| "witch_resistant_to" => Self::WitchResistantTo,
|
||||
"#minecraft:wither_immune_to" | "minecraft:wither_immune_to" | "wither_immune_to" => {
|
||||
Self::WitherImmuneTo
|
||||
}
|
||||
_ => Self::Generic,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::AlwaysHurtsEnderDragons => "#minecraft:always_hurts_ender_dragons",
|
||||
Self::AlwaysKillsArmorStands => "#minecraft:always_kills_armor_stands",
|
||||
Self::AlwaysMostSignificantFall => "#minecraft:always_most_significant_fall",
|
||||
Self::AlwaysTriggersSilverfish => "#minecraft:always_triggers_silverfish",
|
||||
Self::AvoidsGuardianThorns => "#minecraft:avoids_guardian_thorns",
|
||||
Self::BurnsArmorStands => "#minecraft:burns_armor_stands",
|
||||
Self::BurnFromStepping => "#minecraft:burn_from_stepping",
|
||||
Self::BypassesArmor => "#minecraft:bypasses_armor",
|
||||
Self::BypassesCooldown => "#minecraft:bypasses_cooldown",
|
||||
Self::BypassesEffects => "#minecraft:bypasses_effects",
|
||||
Self::BypassesEnchantments => "#minecraft:bypasses_enchantments",
|
||||
Self::BypassesInvulnerability => "#minecraft:bypasses_invulnerability",
|
||||
Self::BypassesResistance => "#minecraft:bypasses_resistance",
|
||||
Self::BypassesShield => "#minecraft:bypasses_shield",
|
||||
Self::BypassesWolfArmor => "#minecraft:bypasses_wolf_armor",
|
||||
Self::CanBreakArmorStands => "#minecraft:can_break_armor_stand",
|
||||
Self::DamagesHelmet => "#minecraft:damages_helmet",
|
||||
Self::IgnitesArmorStands => "#minecraft:ignites_armor_stands",
|
||||
Self::Drowning => "#minecraft:is_drowning",
|
||||
Self::Explosion => "#minecraft:is_explosion",
|
||||
Self::Fall => "#minecraft:is_fall",
|
||||
Self::Fire => "#minecraft:is_fire",
|
||||
Self::Freezing => "#minecraft:is_freezing",
|
||||
Self::Lightning => "#minecraft:is_lightning",
|
||||
Self::PlayerAttack => "#minecraft:is_player_attack",
|
||||
Self::Projectile => "#minecraft:is_projectile",
|
||||
Self::MaceSmash => "#minecraft:mace_smash",
|
||||
Self::NoAnger => "#minecraft:no_anger",
|
||||
Self::NoImpact => "#minecraft:no_impact",
|
||||
Self::NoKnockback => "#minecraft:no_knockback",
|
||||
Self::PanicCauses => "#minecraft:panic_causes",
|
||||
Self::PanicEnvironmentalCauses => "#minecraft:panic_environmental_causes",
|
||||
Self::WitchResistantTo => "#minecraft:witch_resistant_to",
|
||||
Self::WitherImmuneTo => "#minecraft:wither_immune_to",
|
||||
Self::Generic => "minecraft:generic",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct DamageResistantImpl {
|
||||
pub res_type: DamageResistantType,
|
||||
}
|
||||
impl DamageResistantImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
let compound = data.extract_compound()?;
|
||||
let type_str = compound.get_string("types")?;
|
||||
Some(Self {
|
||||
res_type: DamageResistantType::from_tag(type_str),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl std::str::FromStr for DamageResistantType {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(DamageResistantType::from_tag(s))
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for DamageResistantImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
let mut compound = NbtCompound::new();
|
||||
compound.put_string("types", self.res_type.as_str().to_string());
|
||||
NbtTag::Compound(compound)
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
get_str_hash(self.res_type.as_str()) as i32
|
||||
}
|
||||
default_impl!(DamageResistant);
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct ToolRule {
|
||||
pub blocks: IDSet<Block>,
|
||||
pub speed: Option<f32>,
|
||||
pub correct_for_drops: Option<bool>,
|
||||
}
|
||||
impl Hash for ToolRule {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.blocks.hash(state);
|
||||
if let Some(val) = self.speed {
|
||||
true.hash(state);
|
||||
unsafe { (*(&raw const val).cast::<u32>()).hash(state) };
|
||||
} else {
|
||||
false.hash(state);
|
||||
}
|
||||
self.correct_for_drops.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct ToolImpl {
|
||||
pub rules: Cow<'static, [ToolRule]>,
|
||||
pub default_mining_speed: f32,
|
||||
pub damage_per_block: u32,
|
||||
pub can_destroy_blocks_in_creative: bool,
|
||||
}
|
||||
impl DataComponentImpl for ToolImpl {
|
||||
default_impl!(Tool);
|
||||
}
|
||||
impl Hash for ToolImpl {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.rules.hash(state);
|
||||
unsafe { (*(&raw const self.default_mining_speed).cast::<u32>()).hash(state) };
|
||||
self.damage_per_block.hash(state);
|
||||
self.can_destroy_blocks_in_creative.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct WeaponImpl {
|
||||
pub item_damage_per_attack: u32,
|
||||
}
|
||||
impl WeaponImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
let compound = data.extract_compound()?;
|
||||
let item_damage_per_attack = compound
|
||||
.get_int("item_damage_per_attack")
|
||||
.unwrap_or(1)
|
||||
.max(0) as u32;
|
||||
Some(Self {
|
||||
item_damage_per_attack,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for WeaponImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
let mut compound = NbtCompound::new();
|
||||
compound.put_int("item_damage_per_attack", self.item_damage_per_attack as i32);
|
||||
NbtTag::Compound(compound)
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
self.item_damage_per_attack as i32
|
||||
}
|
||||
default_impl!(Weapon);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct AttackRangeImpl;
|
||||
impl DataComponentImpl for AttackRangeImpl {
|
||||
default_impl!(AttackRange);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct EnchantableImpl {
|
||||
pub value: i32,
|
||||
}
|
||||
impl DataComponentImpl for EnchantableImpl {
|
||||
default_impl!(Enchantable);
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, PartialEq)]
|
||||
pub struct EquippableImpl {
|
||||
pub slot: &'static EquipmentSlot,
|
||||
pub equip_sound: IdOr<SoundEvent>,
|
||||
pub asset_id: Option<Cow<'static, str>>,
|
||||
pub camera_overlay: Option<Cow<'static, str>>,
|
||||
pub allowed_entities: Option<IDSet<EntityType>>,
|
||||
pub dispensable: bool,
|
||||
pub swappable: bool,
|
||||
pub damage_on_hurt: bool,
|
||||
pub equip_on_interact: bool,
|
||||
pub can_be_sheared: bool,
|
||||
pub shearing_sound: IdOr<SoundEvent>,
|
||||
}
|
||||
impl EquippableImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
let compound = data.extract_compound()?;
|
||||
let slot = EquipmentSlot::get_from_name(compound.get_string("slot")?)?;
|
||||
let asset_id = compound
|
||||
.get_string("asset_id")
|
||||
.map(|str| Cow::Owned(str.to_owned()));
|
||||
let camera_overlay = compound
|
||||
.get_string("camera_overlay")
|
||||
.map(|str| Cow::Owned(str.to_owned()));
|
||||
let dispensable = compound.get_bool("dispensable").unwrap_or(true);
|
||||
let swappable = compound.get_bool("swappable").unwrap_or(true);
|
||||
let damage_on_hurt = compound.get_bool("damage_on_hurt").unwrap_or(true);
|
||||
let equip_on_interact = compound.get_bool("equip_on_interact").unwrap_or(false);
|
||||
let can_be_sheared = compound.get_bool("can_be_sheared").unwrap_or(false);
|
||||
let equip_sound = get_idor(compound, "equip_sound", Sound::ItemArmorEquipGeneric);
|
||||
let shearing_sound = get_idor(compound, "shearing_sound_sound", Sound::ItemShearsSnip);
|
||||
let allowed_entities = if let Some(nbt) = compound.get("allowed_entities") {
|
||||
IDSet::<EntityType>::read(nbt)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Some(Self {
|
||||
slot,
|
||||
equip_sound,
|
||||
asset_id,
|
||||
camera_overlay,
|
||||
allowed_entities,
|
||||
dispensable,
|
||||
swappable,
|
||||
damage_on_hurt,
|
||||
equip_on_interact,
|
||||
can_be_sheared,
|
||||
shearing_sound,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for EquippableImpl {
|
||||
fn get_hash(&self) -> i32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
digest.update(&[16u8]);
|
||||
digest.update(&get_i32_hash(self.slot.get_slot_index()).to_le_bytes());
|
||||
digest.update(&get_idor_hash(&self.equip_sound).to_le_bytes());
|
||||
if let Some(asset) = &self.asset_id {
|
||||
digest.update(&[1u8]);
|
||||
digest.update(&get_str_hash(asset).to_le_bytes());
|
||||
}
|
||||
if let Some(overlay) = &self.camera_overlay {
|
||||
digest.update(&[2u8]);
|
||||
digest.update(&get_str_hash(overlay).to_le_bytes());
|
||||
}
|
||||
if let Some(allowed_entities) = &self.allowed_entities {
|
||||
digest.update(&[3u8]);
|
||||
digest.update(&get_idset_hash(allowed_entities).to_le_bytes());
|
||||
}
|
||||
digest.update(&[self.dispensable as u8]);
|
||||
digest.update(&[self.swappable as u8]);
|
||||
digest.update(&[self.damage_on_hurt as u8]);
|
||||
digest.update(&[self.equip_on_interact as u8]);
|
||||
digest.update(&[self.can_be_sheared as u8]);
|
||||
digest.update(&get_idor_hash(&self.shearing_sound).to_le_bytes());
|
||||
digest.finalize() as i32
|
||||
}
|
||||
fn write_data(&self) -> NbtTag {
|
||||
let mut compound = NbtCompound::new();
|
||||
compound.put_string("slot", self.slot.to_name().to_string());
|
||||
put_idor(&mut compound, "equip_sound", &self.equip_sound);
|
||||
put_idor(&mut compound, "shearing_sound", &self.shearing_sound);
|
||||
if let Some(asset_id) = &self.asset_id {
|
||||
compound.put_string("asset_id", asset_id.to_string());
|
||||
}
|
||||
if let Some(camera_overlay) = &self.camera_overlay {
|
||||
compound.put_string("camera_overlay", camera_overlay.to_string());
|
||||
}
|
||||
if let Some(allowed_entities) = &self.allowed_entities {
|
||||
allowed_entities.write(&mut compound, "allowed_entities");
|
||||
}
|
||||
compound.put_bool("dispensable", self.dispensable);
|
||||
compound.put_bool("swappable", self.swappable);
|
||||
compound.put_bool("damage_on_hurt", self.damage_on_hurt);
|
||||
compound.put_bool("equip_on_interact", self.equip_on_interact);
|
||||
compound.put_bool("can_be_sheared", self.can_be_sheared);
|
||||
NbtTag::Compound(compound)
|
||||
}
|
||||
default_impl!(Equippable);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct RepairableImpl;
|
||||
impl DataComponentImpl for RepairableImpl {
|
||||
default_impl!(Repairable);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct GliderImpl;
|
||||
impl DataComponentImpl for GliderImpl {
|
||||
default_impl!(Glider);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct DeathProtectionImpl;
|
||||
impl DataComponentImpl for DeathProtectionImpl {
|
||||
default_impl!(DeathProtection);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct BlocksAttacksImpl;
|
||||
impl DataComponentImpl for BlocksAttacksImpl {
|
||||
default_impl!(BlocksAttacks);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct PiercingWeaponImpl;
|
||||
impl DataComponentImpl for PiercingWeaponImpl {
|
||||
default_impl!(PiercingWeapon);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct KineticWeaponImpl;
|
||||
impl DataComponentImpl for KineticWeaponImpl {
|
||||
default_impl!(KineticWeapon);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct SwingAnimationImpl;
|
||||
impl DataComponentImpl for SwingAnimationImpl {
|
||||
default_impl!(SwingAnimation);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct AdditionalTradeCostImpl;
|
||||
impl DataComponentImpl for AdditionalTradeCostImpl {
|
||||
default_impl!(AdditionalTradeCost);
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
pub struct StoredEnchantmentsImpl {
|
||||
pub enchantment: Cow<'static, [(&'static Enchantment, i32)]>,
|
||||
}
|
||||
impl StoredEnchantmentsImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
let data = &data.extract_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()?));
|
||||
}
|
||||
Some(Self {
|
||||
enchantment: Cow::from(enc),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for StoredEnchantmentsImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
let mut data = NbtCompound::new();
|
||||
for (enc, level) in self.enchantment.iter() {
|
||||
data.put_int(enc.name, *level);
|
||||
}
|
||||
NbtTag::Compound(data)
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
digest.update(&[2u8]);
|
||||
for (enc, level) in self.enchantment.iter() {
|
||||
digest.update(&get_str_hash(enc.name).to_le_bytes());
|
||||
digest.update(&get_i32_hash(*level).to_le_bytes());
|
||||
}
|
||||
digest.update(&[3u8]);
|
||||
digest.finalize() as i32
|
||||
}
|
||||
default_impl!(StoredEnchantments);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct OminousBottleAmplifierImpl {
|
||||
pub amplifier: i32,
|
||||
}
|
||||
impl OminousBottleAmplifierImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
data.extract_int().map(|amplifier| Self { amplifier })
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for OminousBottleAmplifierImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
NbtTag::Int(self.amplifier)
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
get_i32_hash(self.amplifier) as i32
|
||||
}
|
||||
default_impl!(OminousBottleAmplifier);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct TrimImpl;
|
||||
impl DataComponentImpl for TrimImpl {
|
||||
default_impl!(Trim);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct MinimumAttackChargeImpl;
|
||||
impl DataComponentImpl for MinimumAttackChargeImpl {
|
||||
default_impl!(MinimumAttackCharge);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct DamageTypeImpl;
|
||||
impl DataComponentImpl for DamageTypeImpl {
|
||||
default_impl!(DamageType);
|
||||
}
|
||||
58
pumpkin-data/src/data_component_impl/entity_variant.rs
Normal file
58
pumpkin-data/src/data_component_impl/entity_variant.rs
Normal file
@ -0,0 +1,58 @@
|
||||
use crate::data_component_impl::{DataComponentImpl, default_impl};
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use std::borrow::Cow;
|
||||
|
||||
macro_rules! string_variant {
|
||||
($name:ident, $struct_name:ident) => {
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct $struct_name {
|
||||
pub value: Cow<'static, str>,
|
||||
}
|
||||
impl $struct_name {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
data.extract_string().map(|v| Self {
|
||||
value: Cow::Owned(v.to_string()),
|
||||
})
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for $struct_name {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
NbtTag::String(self.value.clone().into_owned().into())
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
crate::data_component_impl::get_str_hash(self.value.as_ref()) as i32
|
||||
}
|
||||
default_impl!($name);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
string_variant!(VillagerVariant, VillagerVariantImpl);
|
||||
string_variant!(WolfVariant, WolfVariantImpl);
|
||||
string_variant!(WolfSoundVariant, WolfSoundVariantImpl);
|
||||
string_variant!(WolfCollar, WolfCollarImpl);
|
||||
string_variant!(FoxVariant, FoxVariantImpl);
|
||||
string_variant!(SalmonSize, SalmonSizeImpl);
|
||||
string_variant!(ParrotVariant, ParrotVariantImpl);
|
||||
string_variant!(TropicalFishPattern, TropicalFishPatternImpl);
|
||||
string_variant!(TropicalFishBaseColor, TropicalFishBaseColorImpl);
|
||||
string_variant!(TropicalFishPatternColor, TropicalFishPatternColorImpl);
|
||||
string_variant!(MooshroomVariant, MooshroomVariantImpl);
|
||||
string_variant!(RabbitVariant, RabbitVariantImpl);
|
||||
string_variant!(PigVariant, PigVariantImpl);
|
||||
string_variant!(PigSoundVariant, PigSoundVariantImpl);
|
||||
string_variant!(CowVariant, CowVariantImpl);
|
||||
string_variant!(CowSoundVariant, CowSoundVariantImpl);
|
||||
string_variant!(ChickenVariant, ChickenVariantImpl);
|
||||
string_variant!(ChickenSoundVariant, ChickenSoundVariantImpl);
|
||||
string_variant!(ZombieNautilusVariant, ZombieNautilusVariantImpl);
|
||||
string_variant!(FrogVariant, FrogVariantImpl);
|
||||
string_variant!(HorseVariant, HorseVariantImpl);
|
||||
string_variant!(PaintingVariant, PaintingVariantImpl);
|
||||
string_variant!(LlamaVariant, LlamaVariantImpl);
|
||||
string_variant!(AxolotlVariant, AxolotlVariantImpl);
|
||||
string_variant!(CatVariant, CatVariantImpl);
|
||||
string_variant!(CatSoundVariant, CatSoundVariantImpl);
|
||||
string_variant!(CatCollar, CatCollarImpl);
|
||||
string_variant!(SheepColor, SheepColorImpl);
|
||||
string_variant!(ShulkerColor, ShulkerColorImpl);
|
||||
548
pumpkin-data/src/data_component_impl/food.rs
Normal file
548
pumpkin-data/src/data_component_impl/food.rs
Normal file
@ -0,0 +1,548 @@
|
||||
use crate::data_component_impl::basic::SoundEvent;
|
||||
use crate::data_component_impl::{
|
||||
DataComponentImpl, IDSet, IdOr, default_impl, get_f32_hash, get_i32_hash, get_idor,
|
||||
get_idor_hash, get_idset_hash, get_str_hash, put_idor,
|
||||
};
|
||||
use crate::effect::StatusEffect;
|
||||
use crate::sound::Sound;
|
||||
use crc_fast::CrcAlgorithm::Crc32Iscsi;
|
||||
use crc_fast::Digest;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use std::borrow::Cow;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct FoodImpl {
|
||||
pub nutrition: i32,
|
||||
pub saturation: f32,
|
||||
pub can_always_eat: bool,
|
||||
}
|
||||
impl DataComponentImpl for FoodImpl {
|
||||
default_impl!(Food);
|
||||
}
|
||||
impl Hash for FoodImpl {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.nutrition.hash(state);
|
||||
unsafe { (*(&raw const self.saturation).cast::<u32>()).hash(state) };
|
||||
self.can_always_eat.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct StatusEffectInstance {
|
||||
pub effect_id: Cow<'static, str>,
|
||||
pub amplifier: i32,
|
||||
pub duration: i32,
|
||||
pub ambient: bool,
|
||||
pub show_particles: bool,
|
||||
pub show_icon: bool,
|
||||
}
|
||||
|
||||
impl StatusEffectInstance {
|
||||
pub fn read_data(nbt: &NbtTag) -> Option<Self> {
|
||||
let compound = nbt.extract_compound()?;
|
||||
let effect_id = Cow::Owned(compound.get_string("id")?.to_string());
|
||||
let amplifier = compound.get_int("amplifier")?;
|
||||
let duration = compound.get_int("duration")?;
|
||||
let ambient = compound.get_bool("ambient")?;
|
||||
let show_particles = compound.get_bool("show_particles")?;
|
||||
let show_icon = compound.get_bool("show_icon")?;
|
||||
Some(Self {
|
||||
effect_id,
|
||||
amplifier,
|
||||
duration,
|
||||
ambient,
|
||||
show_particles,
|
||||
show_icon,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn as_nbt(&self) -> NbtTag {
|
||||
let mut compound = NbtCompound::new();
|
||||
compound.put_string("id", self.effect_id.to_string());
|
||||
compound.put_int("amplifier", self.amplifier);
|
||||
compound.put_int("duration", self.duration);
|
||||
compound.put_bool("ambient", self.ambient);
|
||||
compound.put_bool("show_particles", self.show_particles);
|
||||
compound.put_bool("show_icon", self.show_icon);
|
||||
NbtTag::Compound(compound)
|
||||
}
|
||||
|
||||
pub fn get_hash(&self) -> i32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
digest.update(&get_str_hash(self.effect_id.as_ref()).to_le_bytes());
|
||||
digest.update(&get_i32_hash(self.amplifier).to_le_bytes());
|
||||
digest.update(&get_i32_hash(self.duration).to_le_bytes());
|
||||
digest.update(&[self.ambient as u8]);
|
||||
digest.update(&[self.show_particles as u8]);
|
||||
digest.update(&[self.show_icon as u8]);
|
||||
digest.finalize() as i32
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ConsumeAnimation {
|
||||
None,
|
||||
Eat,
|
||||
Drink,
|
||||
Block,
|
||||
Bow,
|
||||
Spear,
|
||||
Crossbow,
|
||||
Spyglass,
|
||||
Horn,
|
||||
Brush,
|
||||
}
|
||||
|
||||
impl ConsumeAnimation {
|
||||
#[must_use]
|
||||
pub const fn to_str(&self) -> &'static str {
|
||||
match self {
|
||||
ConsumeAnimation::None => "none",
|
||||
ConsumeAnimation::Eat => "eat",
|
||||
ConsumeAnimation::Drink => "drink",
|
||||
ConsumeAnimation::Block => "block",
|
||||
ConsumeAnimation::Bow => "bow",
|
||||
ConsumeAnimation::Spear => "spear",
|
||||
ConsumeAnimation::Crossbow => "crossbow",
|
||||
ConsumeAnimation::Spyglass => "spyglass",
|
||||
ConsumeAnimation::Horn => "horn",
|
||||
ConsumeAnimation::Brush => "brush",
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TryFrom<i32> for ConsumeAnimation {
|
||||
type Error = ();
|
||||
fn try_from(value: i32) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
0 => Ok(Self::None),
|
||||
1 => Ok(Self::Eat),
|
||||
2 => Ok(Self::Drink),
|
||||
3 => Ok(Self::Block),
|
||||
4 => Ok(Self::Bow),
|
||||
5 => Ok(Self::Spear),
|
||||
6 => Ok(Self::Crossbow),
|
||||
7 => Ok(Self::Spyglass),
|
||||
8 => Ok(Self::Horn),
|
||||
9 => Ok(Self::Brush),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl FromStr for ConsumeAnimation {
|
||||
type Err = ();
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"none" => Ok(Self::None),
|
||||
"eat" => Ok(Self::Eat),
|
||||
"drink" => Ok(Self::Drink),
|
||||
"block" => Ok(Self::Block),
|
||||
"bow" => Ok(Self::Bow),
|
||||
"spear" => Ok(Self::Spear),
|
||||
"crossbow" => Ok(Self::Crossbow),
|
||||
"spyglass" => Ok(Self::Spyglass),
|
||||
"horn" => Ok(Self::Horn),
|
||||
"brush" => Ok(Self::Brush),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum ConsumeEffect {
|
||||
ApplyEffects((Cow<'static, [StatusEffectInstance]>, f32)),
|
||||
RemoveEffects(IDSet<StatusEffect>),
|
||||
ClearAllEffects,
|
||||
TeleportRandomly(f32),
|
||||
PlaySound(IdOr<SoundEvent>),
|
||||
}
|
||||
impl Hash for ConsumeEffect {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.to_str().hash(state);
|
||||
match self {
|
||||
ConsumeEffect::ApplyEffects(tuple) => {
|
||||
tuple.0.hash(state);
|
||||
unsafe { (*(&raw const tuple.1).cast::<u32>()).hash(state) };
|
||||
}
|
||||
ConsumeEffect::RemoveEffects(status_effect_instances) => {
|
||||
status_effect_instances.hash(state)
|
||||
}
|
||||
ConsumeEffect::ClearAllEffects => (),
|
||||
ConsumeEffect::TeleportRandomly(dst) => unsafe {
|
||||
(*(&raw const dst).cast::<u32>()).hash(state)
|
||||
},
|
||||
ConsumeEffect::PlaySound(id_or) => id_or.hash(state),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ConsumeEffect {
|
||||
pub fn to_str(&self) -> &str {
|
||||
match self {
|
||||
ConsumeEffect::ApplyEffects(_) => "apply_effects",
|
||||
ConsumeEffect::RemoveEffects(_) => "remove_effects",
|
||||
ConsumeEffect::ClearAllEffects => "clear_all_effects",
|
||||
ConsumeEffect::TeleportRandomly(_) => "teleport_randomly",
|
||||
ConsumeEffect::PlaySound(_) => "play_sound",
|
||||
}
|
||||
}
|
||||
pub fn registry_id(&self) -> u8 {
|
||||
match self {
|
||||
ConsumeEffect::ApplyEffects(_) => 0,
|
||||
ConsumeEffect::RemoveEffects(_) => 1,
|
||||
ConsumeEffect::ClearAllEffects => 2,
|
||||
ConsumeEffect::TeleportRandomly(_) => 3,
|
||||
ConsumeEffect::PlaySound(_) => 4,
|
||||
}
|
||||
}
|
||||
pub fn read_data(nbt: &NbtTag) -> Option<Self> {
|
||||
let compound = nbt.extract_compound()?;
|
||||
let r#type = compound.get_string("type")?;
|
||||
match r#type {
|
||||
"remove_effects" => {
|
||||
let idset = IDSet::read(compound.get("effects")?)?;
|
||||
Some(Self::RemoveEffects(idset))
|
||||
}
|
||||
"clear_all_effects" => Some(Self::ClearAllEffects),
|
||||
"teleport_randomly" => {
|
||||
let dst = compound.get_float("diameter")?;
|
||||
Some(Self::TeleportRandomly(dst))
|
||||
}
|
||||
"play_sound" => {
|
||||
let sound = get_idor(compound, "sound", Sound::EntityGenericEat);
|
||||
Some(Self::PlaySound(sound))
|
||||
}
|
||||
"apply_effects" => {
|
||||
let probability = compound.get_float("probability")?;
|
||||
let effects_vec: Vec<StatusEffectInstance> = compound
|
||||
.get_list("effects")?
|
||||
.iter()
|
||||
.filter_map(StatusEffectInstance::read_data)
|
||||
.collect();
|
||||
let effects: Cow<'static, [StatusEffectInstance]> = Cow::Owned(effects_vec);
|
||||
Some(Self::ApplyEffects((effects, probability)))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn as_nbt(&self) -> NbtTag {
|
||||
let mut compound = NbtCompound::new();
|
||||
compound.put_string("type", self.to_str().to_string());
|
||||
match self {
|
||||
ConsumeEffect::ApplyEffects(data) => {
|
||||
let nbt_arr = data.0.iter().map(|x| x.as_nbt()).collect();
|
||||
compound.put_list("effects", nbt_arr);
|
||||
compound.put_float("probability", data.1);
|
||||
}
|
||||
ConsumeEffect::RemoveEffects(idset) => idset.write(&mut compound, "effects"),
|
||||
ConsumeEffect::ClearAllEffects => (),
|
||||
ConsumeEffect::TeleportRandomly(dst) => compound.put_float("diameter", *dst),
|
||||
ConsumeEffect::PlaySound(id_or) => {
|
||||
put_idor(&mut compound, "sound", id_or);
|
||||
}
|
||||
}
|
||||
NbtTag::Compound(compound)
|
||||
}
|
||||
pub fn get_hash(&self) -> i32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
match self {
|
||||
ConsumeEffect::ApplyEffects((effects, probability)) => {
|
||||
digest.update(&[1u8]);
|
||||
for effect in effects.iter() {
|
||||
digest.update(&effect.get_hash().to_le_bytes());
|
||||
}
|
||||
digest.update(&[13u8]);
|
||||
digest.update(&get_f32_hash(*probability).to_le_bytes());
|
||||
}
|
||||
ConsumeEffect::RemoveEffects(idset) => {
|
||||
digest.update(&[2u8]);
|
||||
digest.update(&get_idset_hash(idset).to_le_bytes());
|
||||
}
|
||||
ConsumeEffect::ClearAllEffects => {
|
||||
digest.update(&[3u8]);
|
||||
}
|
||||
ConsumeEffect::TeleportRandomly(dst) => {
|
||||
digest.update(&[4u8]);
|
||||
digest.update(&get_f32_hash(*dst).to_le_bytes());
|
||||
}
|
||||
ConsumeEffect::PlaySound(id_or) => {
|
||||
digest.update(&[5u8]);
|
||||
digest.update(&get_idor_hash(id_or).to_le_bytes());
|
||||
}
|
||||
}
|
||||
digest.finalize() as i32
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ConsumableImpl {
|
||||
pub consume_seconds: f32,
|
||||
pub animation: ConsumeAnimation,
|
||||
pub sound_event: IdOr<SoundEvent>,
|
||||
pub consume_particles: bool,
|
||||
pub effects: Cow<'static, [ConsumeEffect]>,
|
||||
}
|
||||
impl ConsumableImpl {
|
||||
pub const fn new(
|
||||
consume_seconds: f32,
|
||||
animation: ConsumeAnimation,
|
||||
sound_event: IdOr<SoundEvent>,
|
||||
consume_particles: bool,
|
||||
effects: Cow<'static, [ConsumeEffect]>,
|
||||
) -> Self {
|
||||
Self {
|
||||
consume_seconds,
|
||||
animation,
|
||||
sound_event,
|
||||
consume_particles,
|
||||
effects,
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
pub fn consume_ticks(&self) -> i32 {
|
||||
(self.consume_seconds * 20.0) as i32
|
||||
}
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
let compound = data.extract_compound()?;
|
||||
let consume_seconds = compound.get_float("consume_seconds")?;
|
||||
let animation = compound
|
||||
.get_string("animation")?
|
||||
.parse::<ConsumeAnimation>()
|
||||
.ok()?;
|
||||
let sound_event = get_idor(compound, "sound", Sound::EntityGenericEat);
|
||||
let consume_particles = compound.get_bool("has_consume_particles").unwrap_or(false);
|
||||
let opt_list = compound.get_list("on_consume_effects");
|
||||
let effects: Cow<'static, [ConsumeEffect]> = if let Some(effect_list) = opt_list {
|
||||
effect_list
|
||||
.iter()
|
||||
.filter_map(ConsumeEffect::read_data)
|
||||
.collect()
|
||||
} else {
|
||||
Cow::Borrowed(&[])
|
||||
};
|
||||
Some(Self {
|
||||
consume_seconds,
|
||||
animation,
|
||||
sound_event,
|
||||
consume_particles,
|
||||
effects,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for ConsumableImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
let mut compound = NbtCompound::new();
|
||||
compound.put_float("consume_seconds", self.consume_seconds);
|
||||
compound.put_string("animation", self.animation.to_str().to_string());
|
||||
put_idor(&mut compound, "sound", &self.sound_event);
|
||||
compound.put_bool("has_consume_particles", self.consume_particles);
|
||||
let nbt_vec = self.effects.iter().map(|x| x.as_nbt()).collect();
|
||||
compound.put_list("on_consume_effects", nbt_vec);
|
||||
NbtTag::Compound(compound)
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
digest.update(&[2u8]);
|
||||
digest.update(&get_f32_hash(self.consume_seconds).to_le_bytes());
|
||||
digest.update(&get_i32_hash(self.animation as i32).to_le_bytes());
|
||||
digest.update(&get_idor_hash(&self.sound_event).to_be_bytes());
|
||||
digest.update(&[self.consume_particles as u8]);
|
||||
for effect in self.effects.iter() {
|
||||
digest.update(&effect.get_hash().to_le_bytes());
|
||||
}
|
||||
digest.update(&[3u8]);
|
||||
digest.finalize() as i32
|
||||
}
|
||||
default_impl!(Consumable);
|
||||
}
|
||||
impl Hash for ConsumableImpl {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
unsafe { (*(&raw const self.consume_seconds).cast::<u32>()).hash(state) };
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct UseEffectsImpl;
|
||||
impl DataComponentImpl for UseEffectsImpl {
|
||||
default_impl!(UseEffects);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct UseRemainderImpl;
|
||||
impl DataComponentImpl for UseRemainderImpl {
|
||||
default_impl!(UseRemainder);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
|
||||
pub struct UseCooldownImpl {
|
||||
pub seconds: f32,
|
||||
pub cooldown_group: Option<String>,
|
||||
}
|
||||
impl UseCooldownImpl {
|
||||
pub fn new(seconds: f32, cooldown_group: Option<String>) -> Self {
|
||||
Self {
|
||||
seconds,
|
||||
cooldown_group,
|
||||
}
|
||||
}
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
let compound = data.extract_compound()?;
|
||||
let seconds = compound.get_float("seconds")?;
|
||||
let cooldown_group = compound.get_string("cooldown_group").map(|s| s.to_string());
|
||||
Some(Self {
|
||||
seconds,
|
||||
cooldown_group,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for UseCooldownImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
let mut compound = NbtCompound::new();
|
||||
compound.put_float("seconds", self.seconds);
|
||||
if let Some(group) = &self.cooldown_group {
|
||||
compound.put_string("cooldown_group", group.clone());
|
||||
}
|
||||
NbtTag::Compound(compound)
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
digest.update(&get_f32_hash(self.seconds).to_le_bytes());
|
||||
if let Some(group) = &self.cooldown_group {
|
||||
digest.update(&get_str_hash(group).to_le_bytes());
|
||||
}
|
||||
digest.finalize() as i32
|
||||
}
|
||||
default_impl!(UseCooldown);
|
||||
}
|
||||
impl Eq for UseCooldownImpl {}
|
||||
impl Hash for UseCooldownImpl {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.seconds.to_bits().hash(state);
|
||||
self.cooldown_group.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct PotionContentsImpl {
|
||||
pub potion_id: Option<i32>,
|
||||
pub custom_color: Option<i32>,
|
||||
pub custom_effects: Vec<StatusEffectInstance>,
|
||||
pub custom_name: Option<String>,
|
||||
}
|
||||
impl PotionContentsImpl {
|
||||
pub fn read_data(tag: &NbtTag) -> Option<Self> {
|
||||
let compound = tag.extract_compound()?;
|
||||
let potion_id = if let Some(id) = compound.get_int("potion") {
|
||||
Some(id)
|
||||
} else if let Some(name) = compound.get_string("potion") {
|
||||
let name = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||
crate::potion::Potion::from_name(name).map(|p| p.id as i32)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let custom_color = compound.get_int("custom_color");
|
||||
let custom_name = compound.get_string("custom_name").map(|s| s.to_string());
|
||||
let custom_effects = compound
|
||||
.get_list("custom_effects")
|
||||
.map(|list| {
|
||||
list.iter()
|
||||
.filter_map(|item| {
|
||||
let effect_tag = item.extract_compound()?;
|
||||
let id: Cow<'static, str> =
|
||||
Cow::Owned(effect_tag.get_string("id")?.to_string());
|
||||
let amplifier = effect_tag
|
||||
.get_int("amplifier")
|
||||
.or_else(|| effect_tag.get_byte("amplifier").map(i32::from))
|
||||
.unwrap_or(0);
|
||||
let duration = effect_tag
|
||||
.get_int("duration")
|
||||
.or_else(|| effect_tag.get_byte("duration").map(i32::from))
|
||||
.unwrap_or(0);
|
||||
let ambient = effect_tag.get_bool("ambient").unwrap_or(false);
|
||||
let show_particles = effect_tag.get_bool("show_particles").unwrap_or(true);
|
||||
let show_icon = effect_tag.get_bool("show_icon").unwrap_or(true);
|
||||
Some(StatusEffectInstance {
|
||||
effect_id: id,
|
||||
amplifier,
|
||||
duration,
|
||||
ambient,
|
||||
show_particles,
|
||||
show_icon,
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
Some(Self {
|
||||
potion_id,
|
||||
custom_color,
|
||||
custom_effects,
|
||||
custom_name,
|
||||
})
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for PotionContentsImpl {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
let mut compound = NbtCompound::new();
|
||||
if let Some(potion_id) = self.potion_id {
|
||||
compound.put_int("potion", potion_id);
|
||||
}
|
||||
if let Some(color) = self.custom_color {
|
||||
compound.put_int("custom_color", color);
|
||||
}
|
||||
if !self.custom_effects.is_empty() {
|
||||
let mut effects_list = Vec::new();
|
||||
for effect in &self.custom_effects {
|
||||
let mut effect_compound = NbtCompound::new();
|
||||
effect_compound.put_string("id", effect.effect_id.to_string());
|
||||
effect_compound.put_int("amplifier", effect.amplifier);
|
||||
effect_compound.put_int("duration", effect.duration);
|
||||
effect_compound.put_byte("ambient", effect.ambient as i8);
|
||||
effect_compound.put_byte("show_particles", effect.show_particles as i8);
|
||||
effect_compound.put_byte("show_icon", effect.show_icon as i8);
|
||||
effects_list.push(NbtTag::Compound(effect_compound));
|
||||
}
|
||||
compound.put("custom_effects", NbtTag::List(effects_list));
|
||||
}
|
||||
if let Some(name) = &self.custom_name {
|
||||
compound.put_string("custom_name", name.clone());
|
||||
}
|
||||
NbtTag::Compound(compound)
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
if let Some(id) = self.potion_id {
|
||||
digest.update(&[1u8]);
|
||||
digest.update(&get_i32_hash(id).to_le_bytes());
|
||||
}
|
||||
if let Some(color) = self.custom_color {
|
||||
digest.update(&[2u8]);
|
||||
digest.update(&get_i32_hash(color).to_le_bytes());
|
||||
}
|
||||
if let Some(name) = &self.custom_name {
|
||||
digest.update(&[3u8]);
|
||||
digest.update(&get_str_hash(name).to_le_bytes());
|
||||
}
|
||||
if !self.custom_effects.is_empty() {
|
||||
digest.update(&[4u8]);
|
||||
for effect in &self.custom_effects {
|
||||
digest.update(&effect.get_hash().to_le_bytes());
|
||||
}
|
||||
}
|
||||
digest.finalize() as i32
|
||||
}
|
||||
default_impl!(PotionContents);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct PotionDurationScaleImpl;
|
||||
impl DataComponentImpl for PotionDurationScaleImpl {
|
||||
default_impl!(PotionDurationScale);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct SuspiciousStewEffectsImpl;
|
||||
impl DataComponentImpl for SuspiciousStewEffectsImpl {
|
||||
default_impl!(SuspiciousStewEffects);
|
||||
}
|
||||
607
pumpkin-data/src/data_component_impl/mod.rs
Normal file
607
pumpkin-data/src/data_component_impl/mod.rs
Normal file
@ -0,0 +1,607 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::Block;
|
||||
use crate::BlockId;
|
||||
use crate::data_component::DataComponent;
|
||||
use crate::effect::StatusEffect;
|
||||
use crate::entity_type::EntityType;
|
||||
use crate::sound::Sound;
|
||||
use crate::tag::Taggable;
|
||||
use crc_fast::CrcAlgorithm::Crc32Iscsi;
|
||||
use crc_fast::Digest;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_nbt::tag::NbtTag;
|
||||
use std::any::Any;
|
||||
use std::borrow::Cow;
|
||||
|
||||
pub trait DataComponentImpl: Send + Sync {
|
||||
fn write_data(&self) -> NbtTag {
|
||||
NbtTag::End
|
||||
}
|
||||
fn get_hash(&self) -> i32 {
|
||||
0
|
||||
}
|
||||
/// make sure other is the same type component, or it will panic
|
||||
fn equal(&self, other: &dyn DataComponentImpl) -> bool;
|
||||
fn get_enum() -> DataComponent
|
||||
where
|
||||
Self: Sized;
|
||||
fn get_self_enum(&self) -> DataComponent; // only for debugging
|
||||
fn to_dyn(self) -> Box<dyn DataComponentImpl>;
|
||||
fn clone_dyn(&self) -> Box<dyn DataComponentImpl>;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn as_mut_any(&mut self) -> &mut dyn Any;
|
||||
}
|
||||
|
||||
impl Clone for Box<dyn DataComponentImpl> {
|
||||
fn clone(&self) -> Self {
|
||||
self.clone_dyn()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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 {}",
|
||||
value.get_self_enum().to_name(),
|
||||
T::get_enum().to_name()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_mut<T: DataComponentImpl + 'static>(value: &mut dyn DataComponentImpl) -> &mut T {
|
||||
value.as_mut_any().downcast_mut::<T>().unwrap()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
#[inline]
|
||||
fn get_enum() -> crate::data_component::DataComponent
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
crate::data_component::DataComponent::$t
|
||||
}
|
||||
fn get_self_enum(&self) -> crate::data_component::DataComponent {
|
||||
crate::data_component::DataComponent::$t
|
||||
}
|
||||
fn to_dyn(self) -> Box<dyn crate::data_component_impl::DataComponentImpl> {
|
||||
Box::new(self)
|
||||
}
|
||||
fn clone_dyn(&self) -> Box<dyn crate::data_component_impl::DataComponentImpl> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
fn as_mut_any(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) use default_impl;
|
||||
|
||||
pub fn get_str_hash(val: &str) -> u32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
digest.update(&[12u8]);
|
||||
digest.update(&(val.len() as u32).to_le_bytes());
|
||||
let byte = val.as_bytes();
|
||||
for i in byte {
|
||||
digest.update(&[*i, 0u8]);
|
||||
}
|
||||
digest.finalize() as u32
|
||||
}
|
||||
|
||||
pub fn get_i32_hash(val: i32) -> u32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
digest.update(&[8u8]);
|
||||
digest.update(&val.to_le_bytes());
|
||||
digest.finalize() as u32
|
||||
}
|
||||
|
||||
pub fn get_f32_hash(val: f32) -> u32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
digest.update(&[7u8]);
|
||||
digest.update(&val.to_bits().to_le_bytes());
|
||||
digest.finalize() as u32
|
||||
}
|
||||
|
||||
pub fn get_idor_hash(val: &IdOr<basic::SoundEvent>) -> u32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
digest.update(&[6u8]);
|
||||
match val {
|
||||
IdOr::Id(sound) => {
|
||||
digest.update(&[1u8]);
|
||||
digest.update(&get_str_hash(sound.to_name()).to_le_bytes());
|
||||
}
|
||||
IdOr::Value(sound) => {
|
||||
digest.update(&[2u8]);
|
||||
digest.update(&get_str_hash(sound.sound_name.as_str()).to_le_bytes());
|
||||
if let Some(range) = sound.range {
|
||||
digest.update(&[1u8]);
|
||||
digest.update(&get_f32_hash(range).to_le_bytes());
|
||||
} else {
|
||||
digest.update(&[0u8]);
|
||||
}
|
||||
}
|
||||
}
|
||||
digest.finalize() as u32
|
||||
}
|
||||
|
||||
pub fn put_idor(nbt: &mut NbtCompound, key: &str, val: &IdOr<basic::SoundEvent>) {
|
||||
match val {
|
||||
IdOr::Id(id) => nbt.put_string(key, format!("minecraft:{}", id.to_name())),
|
||||
IdOr::Value(sound) => {
|
||||
let mut sound_compound = NbtCompound::new();
|
||||
|
||||
sound_compound.put_string("sound_id", sound.sound_name.clone());
|
||||
if let Some(range) = sound.range {
|
||||
sound_compound.put_float("range", range);
|
||||
}
|
||||
nbt.put(key, NbtTag::Compound(sound_compound));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_idor(nbt: &NbtCompound, key: &str, default: Sound) -> IdOr<basic::SoundEvent> {
|
||||
if let Some(sound) = nbt.get_string(key) {
|
||||
let sound = sound.strip_prefix("minecraft:").unwrap_or(sound);
|
||||
IdOr::Id(Sound::from_name(sound).unwrap_or(default))
|
||||
} else if let Some(sound_compound) = nbt.get_compound(key) {
|
||||
let sound_name = sound_compound
|
||||
.get_string("sound_id")
|
||||
.expect("SoundEvent compound must have a 'sound_id' field");
|
||||
let range = sound_compound.get_float("range");
|
||||
IdOr::Value(basic::SoundEvent {
|
||||
sound_name: sound_name.to_string(),
|
||||
range,
|
||||
})
|
||||
} else {
|
||||
IdOr::Id(default)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_idset_hash<T: IDSetContent>(val: &IDSet<T>) -> u32 {
|
||||
let mut digest = Digest::new(Crc32Iscsi);
|
||||
match val {
|
||||
IDSet::Tag(tag) => {
|
||||
digest.update(&[1u8]);
|
||||
digest.update(&get_str_hash(tag).to_le_bytes());
|
||||
}
|
||||
IDSet::IDs(ids) => {
|
||||
digest.update(&[2u8]);
|
||||
for id in ids.iter() {
|
||||
digest.update(&[3u8]);
|
||||
digest.update(&get_i32_hash(id.registry_id() as i32).to_le_bytes());
|
||||
}
|
||||
}
|
||||
}
|
||||
digest.finalize() as u32
|
||||
}
|
||||
|
||||
pub trait IDSetContent {
|
||||
fn registry_id(&self) -> u16;
|
||||
fn to_string(&self) -> String;
|
||||
fn from_id(id: u16) -> Option<&'static Self>;
|
||||
fn from_str(name: &str) -> Option<&'static Self>;
|
||||
}
|
||||
|
||||
impl IDSetContent for Block {
|
||||
fn registry_id(&self) -> u16 {
|
||||
Taggable::registry_id(self)
|
||||
}
|
||||
|
||||
fn from_id(id: u16) -> Option<&'static Self> {
|
||||
BlockId::new(id).map(Self::from_id)
|
||||
}
|
||||
|
||||
fn from_str(name: &str) -> Option<&'static Self> {
|
||||
Block::from_name(name)
|
||||
}
|
||||
|
||||
fn to_string(&self) -> String {
|
||||
self.name.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Debug)]
|
||||
pub enum IDSet<T: IDSetContent + 'static> {
|
||||
Tag(Cow<'static, str>),
|
||||
IDs(Cow<'static, [&'static T]>),
|
||||
}
|
||||
|
||||
impl<T: IDSetContent + 'static> IDSet<T> {
|
||||
pub fn read(data: &NbtTag) -> Option<Self> {
|
||||
match data.clone() {
|
||||
NbtTag::String(tag) => {
|
||||
if tag.starts_with("#") {
|
||||
Some(Self::Tag(Cow::Owned(tag.strip_prefix("#")?.to_string())))
|
||||
} else {
|
||||
Some(Self::IDs(Cow::Owned([T::from_str(tag.as_ref())?].to_vec())))
|
||||
}
|
||||
}
|
||||
NbtTag::List(nbt_tags) => {
|
||||
let mut ids = Vec::<&T>::new();
|
||||
for nbt in nbt_tags {
|
||||
if let NbtTag::String(id) = nbt
|
||||
&& let Some(instance) = T::from_str(id.as_ref())
|
||||
{
|
||||
ids.push(instance);
|
||||
}
|
||||
}
|
||||
Some(Self::IDs(Cow::Owned(ids)))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write(&self, compound: &mut NbtCompound, key: &str) {
|
||||
match self {
|
||||
Self::Tag(cow) => {
|
||||
let mut tag = cow.to_string();
|
||||
tag.insert(0, '#');
|
||||
compound.put_string(key, tag);
|
||||
}
|
||||
Self::IDs(arr) => {
|
||||
let id_vec: Vec<NbtTag> = arr
|
||||
.iter()
|
||||
.map(|x| NbtTag::String(x.to_string().into()))
|
||||
.collect();
|
||||
compound.put_list(key, id_vec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
pub enum EquipmentType {
|
||||
Hand,
|
||||
HumanoidArmor,
|
||||
AnimalArmor,
|
||||
Saddle,
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
pub struct EquipmentSlotData {
|
||||
pub slot_type: EquipmentType,
|
||||
pub entity_id: i32,
|
||||
pub max_count: i32,
|
||||
pub index: i32,
|
||||
pub name: Cow<'static, str>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
#[repr(i8)]
|
||||
pub enum EquipmentSlot {
|
||||
MainHand(EquipmentSlotData),
|
||||
OffHand(EquipmentSlotData),
|
||||
Feet(EquipmentSlotData),
|
||||
Legs(EquipmentSlotData),
|
||||
Chest(EquipmentSlotData),
|
||||
Head(EquipmentSlotData),
|
||||
Body(EquipmentSlotData),
|
||||
Saddle(EquipmentSlotData),
|
||||
}
|
||||
|
||||
impl EquipmentSlot {
|
||||
pub const MAIN_HAND: Self = Self::MainHand(EquipmentSlotData {
|
||||
slot_type: EquipmentType::Hand,
|
||||
entity_id: 0,
|
||||
index: 0,
|
||||
max_count: 0,
|
||||
name: Cow::Borrowed("mainhand"),
|
||||
});
|
||||
pub const OFF_HAND: Self = Self::OffHand(EquipmentSlotData {
|
||||
slot_type: EquipmentType::Hand,
|
||||
entity_id: 1,
|
||||
index: 5,
|
||||
max_count: 0,
|
||||
name: Cow::Borrowed("offhand"),
|
||||
});
|
||||
pub const FEET: Self = Self::Feet(EquipmentSlotData {
|
||||
slot_type: EquipmentType::HumanoidArmor,
|
||||
entity_id: 0,
|
||||
index: 1,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("feet"),
|
||||
});
|
||||
pub const LEGS: Self = Self::Legs(EquipmentSlotData {
|
||||
slot_type: EquipmentType::HumanoidArmor,
|
||||
entity_id: 1,
|
||||
index: 2,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("legs"),
|
||||
});
|
||||
pub const CHEST: Self = Self::Chest(EquipmentSlotData {
|
||||
slot_type: EquipmentType::HumanoidArmor,
|
||||
entity_id: 2,
|
||||
index: 3,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("chest"),
|
||||
});
|
||||
pub const HEAD: Self = Self::Head(EquipmentSlotData {
|
||||
slot_type: EquipmentType::HumanoidArmor,
|
||||
entity_id: 3,
|
||||
index: 4,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("head"),
|
||||
});
|
||||
pub const BODY: Self = Self::Body(EquipmentSlotData {
|
||||
slot_type: EquipmentType::AnimalArmor,
|
||||
entity_id: 0,
|
||||
index: 6,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("body"),
|
||||
});
|
||||
pub const SADDLE: Self = Self::Saddle(EquipmentSlotData {
|
||||
slot_type: EquipmentType::Saddle,
|
||||
entity_id: 0,
|
||||
index: 7,
|
||||
max_count: 1,
|
||||
name: Cow::Borrowed("saddle"),
|
||||
});
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_entity_slot_id(&self) -> i32 {
|
||||
match self {
|
||||
Self::MainHand(data) => data.entity_id,
|
||||
Self::OffHand(data) => data.entity_id,
|
||||
Self::Feet(data) => data.entity_id,
|
||||
Self::Legs(data) => data.entity_id,
|
||||
Self::Chest(data) => data.entity_id,
|
||||
Self::Head(data) => data.entity_id,
|
||||
Self::Body(data) => data.entity_id,
|
||||
Self::Saddle(data) => data.entity_id,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_slot_index(&self) -> i32 {
|
||||
match self {
|
||||
Self::MainHand(data) => data.index,
|
||||
Self::OffHand(data) => data.index,
|
||||
Self::Feet(data) => data.index,
|
||||
Self::Legs(data) => data.index,
|
||||
Self::Chest(data) => data.index,
|
||||
Self::Head(data) => data.index,
|
||||
Self::Body(data) => data.index,
|
||||
Self::Saddle(data) => data.index,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn from_slot_index(name: i32) -> Option<&'static Self> {
|
||||
match name {
|
||||
0 => Some(&Self::MAIN_HAND),
|
||||
1 => Some(&Self::FEET),
|
||||
2 => Some(&Self::LEGS),
|
||||
3 => Some(&Self::CHEST),
|
||||
4 => Some(&Self::HEAD),
|
||||
5 => Some(&Self::OFF_HAND),
|
||||
6 => Some(&Self::BODY),
|
||||
7 => Some(&Self::SADDLE),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_from_name(name: &str) -> Option<&'static Self> {
|
||||
match name {
|
||||
"mainhand" => Some(&Self::MAIN_HAND),
|
||||
"offhand" => Some(&Self::OFF_HAND),
|
||||
"feet" => Some(&Self::FEET),
|
||||
"legs" => Some(&Self::LEGS),
|
||||
"chest" => Some(&Self::CHEST),
|
||||
"head" => Some(&Self::HEAD),
|
||||
"body" => Some(&Self::BODY),
|
||||
"saddle" => Some(&Self::SADDLE),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn to_name(&self) -> &Cow<'static, str> {
|
||||
match self {
|
||||
Self::MainHand(data) => &data.name,
|
||||
Self::OffHand(data) => &data.name,
|
||||
Self::Feet(data) => &data.name,
|
||||
Self::Legs(data) => &data.name,
|
||||
Self::Chest(data) => &data.name,
|
||||
Self::Head(data) => &data.name,
|
||||
Self::Body(data) => &data.name,
|
||||
Self::Saddle(data) => &data.name,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn get_offset_entity_slot_id(&self, offset: i32) -> i32 {
|
||||
self.get_entity_slot_id() + offset
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn slot_type(&self) -> EquipmentType {
|
||||
match self {
|
||||
Self::MainHand(data) => data.slot_type,
|
||||
Self::OffHand(data) => data.slot_type,
|
||||
Self::Feet(data) => data.slot_type,
|
||||
Self::Legs(data) => data.slot_type,
|
||||
Self::Chest(data) => data.slot_type,
|
||||
Self::Head(data) => data.slot_type,
|
||||
Self::Body(data) => data.slot_type,
|
||||
Self::Saddle(data) => data.slot_type,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn is_armor_slot(&self) -> bool {
|
||||
matches!(
|
||||
self.slot_type(),
|
||||
EquipmentType::HumanoidArmor | EquipmentType::AnimalArmor
|
||||
)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn discriminant(&self) -> i8 {
|
||||
match self {
|
||||
Self::MainHand(_) => 0,
|
||||
Self::OffHand(_) => 1,
|
||||
Self::Feet(_) => 2,
|
||||
Self::Legs(_) => 3,
|
||||
Self::Chest(_) => 4,
|
||||
Self::Head(_) => 5,
|
||||
Self::Body(_) => 6,
|
||||
Self::Saddle(_) => 7,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub enum EntityTypeOrTag {
|
||||
Tag(&'static crate::tag::Tag),
|
||||
Single(&'static EntityType),
|
||||
}
|
||||
|
||||
impl std::hash::Hash for EntityTypeOrTag {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
match self {
|
||||
Self::Tag(tag) => {
|
||||
for x in tag.0 {
|
||||
x.hash(state);
|
||||
}
|
||||
}
|
||||
Self::Single(entity_type) => {
|
||||
entity_type.id.hash(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum IdOr<T> {
|
||||
Id(Sound),
|
||||
Value(T),
|
||||
}
|
||||
|
||||
pub mod basic;
|
||||
pub mod block_entity;
|
||||
pub mod book;
|
||||
pub mod combat;
|
||||
pub mod entity_variant;
|
||||
pub mod food;
|
||||
pub mod utility;
|
||||
|
||||
// Re-export all component implementations
|
||||
pub use basic::*;
|
||||
pub use block_entity::*;
|
||||
pub use book::*;
|
||||
pub use combat::*;
|
||||
pub use entity_variant::*;
|
||||
pub use food::*;
|
||||
pub use utility::*;
|
||||
|
||||
#[must_use]
|
||||
pub fn read_data(id: DataComponent, data: &NbtTag) -> Option<Box<dyn DataComponentImpl>> {
|
||||
match id {
|
||||
DataComponent::MaxStackSize => Some(MaxStackSizeImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::CustomData => Some(CustomDataImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::Enchantments => Some(EnchantmentsImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::Damage => Some(DamageImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::Unbreakable => Some(UnbreakableImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::DamageResistant => Some(DamageResistantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::PotionContents => Some(PotionContentsImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::Fireworks => Some(FireworksImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::FireworkExplosion => Some(FireworkExplosionImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::CustomName => Some(CustomNameImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::ItemModel => Some(ItemModelImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::Consumable => Some(ConsumableImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::Equippable => Some(EquippableImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::StoredEnchantments => {
|
||||
Some(StoredEnchantmentsImpl::read_data(data)?.to_dyn())
|
||||
}
|
||||
DataComponent::UseCooldown => Some(UseCooldownImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::MapId => Some(MapIdImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::ChargedProjectiles => {
|
||||
Some(ChargedProjectilesImpl::read_data(data)?.to_dyn())
|
||||
}
|
||||
DataComponent::BlockEntityData => Some(BlockEntityDataImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::BundleContents => Some(BundleContentsImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::Container => Some(ContainerImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::WrittenBookContent => {
|
||||
Some(WrittenBookContentImpl::read_data(data)?.to_dyn())
|
||||
}
|
||||
DataComponent::WritableBookContent => {
|
||||
Some(WritableBookContentImpl::read_data(data)?.to_dyn())
|
||||
}
|
||||
DataComponent::OminousBottleAmplifier => {
|
||||
Some(OminousBottleAmplifierImpl::read_data(data)?.to_dyn())
|
||||
}
|
||||
DataComponent::BlockState => Some(BlockStateImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::Profile => Some(ProfileImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::ChickenVariant => Some(ChickenVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::VillagerVariant => Some(VillagerVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::WolfVariant => Some(WolfVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::WolfSoundVariant => Some(WolfSoundVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::WolfCollar => Some(WolfCollarImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::FoxVariant => Some(FoxVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::SalmonSize => Some(SalmonSizeImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::ParrotVariant => Some(ParrotVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::TropicalFishPattern => {
|
||||
Some(TropicalFishPatternImpl::read_data(data)?.to_dyn())
|
||||
}
|
||||
DataComponent::TropicalFishBaseColor => {
|
||||
Some(TropicalFishBaseColorImpl::read_data(data)?.to_dyn())
|
||||
}
|
||||
DataComponent::TropicalFishPatternColor => {
|
||||
Some(TropicalFishPatternColorImpl::read_data(data)?.to_dyn())
|
||||
}
|
||||
DataComponent::MooshroomVariant => Some(MooshroomVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::RabbitVariant => Some(RabbitVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::PigVariant => Some(PigVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::PigSoundVariant => Some(PigSoundVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::CowVariant => Some(CowVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::CowSoundVariant => Some(CowSoundVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::ChickenSoundVariant => {
|
||||
Some(ChickenSoundVariantImpl::read_data(data)?.to_dyn())
|
||||
}
|
||||
DataComponent::ZombieNautilusVariant => {
|
||||
Some(ZombieNautilusVariantImpl::read_data(data)?.to_dyn())
|
||||
}
|
||||
DataComponent::FrogVariant => Some(FrogVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::HorseVariant => Some(HorseVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::PaintingVariant => Some(PaintingVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::LlamaVariant => Some(LlamaVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::AxolotlVariant => Some(AxolotlVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::CatVariant => Some(CatVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::CatSoundVariant => Some(CatSoundVariantImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::CatCollar => Some(CatCollarImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::SheepColor => Some(SheepColorImpl::read_data(data)?.to_dyn()),
|
||||
DataComponent::ShulkerColor => Some(ShulkerColorImpl::read_data(data)?.to_dyn()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn hash() {
|
||||
assert_eq!(get_str_hash("minecraft:sharpness"), 2734053906u32);
|
||||
assert_eq!(get_i32_hash(3), 3795317917u32);
|
||||
assert_eq!(
|
||||
EnchantmentsImpl {
|
||||
enchantment: Cow::Borrowed(&[(&crate::Enchantment::SHARPNESS, 2)]),
|
||||
}
|
||||
.get_hash(),
|
||||
-1580618251i32
|
||||
);
|
||||
assert_eq!(MaxStackSizeImpl { size: 99 }.get_hash(), -1632321551i32);
|
||||
assert_eq!(MapIdImpl { id: 10 }.get_hash(), -919192125i32);
|
||||
}
|
||||
}
|
||||
493
pumpkin-data/src/data_component_impl/utility.rs
Normal file
493
pumpkin-data/src/data_component_impl/utility.rs
Normal file
@ -0,0 +1,493 @@
|
||||
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;
|
||||
impl DataComponentImpl for DyedColorImpl {
|
||||
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<Self> {
|
||||
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<NbtCompound>,
|
||||
}
|
||||
impl ChargedProjectilesImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
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<crate::item_stack::ItemStack>,
|
||||
}
|
||||
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<Self> {
|
||||
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::<BundleContentsImpl>().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<crate::item_stack::ItemStack> {
|
||||
if self.items.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.items.remove(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
impl DataComponentImpl for BundleContentsImpl {
|
||||
default_impl!(BundleContents);
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct LodestoneTrackerImpl;
|
||||
impl DataComponentImpl for LodestoneTrackerImpl {
|
||||
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<Self> {
|
||||
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<Self> {
|
||||
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<i32>,
|
||||
pub fade_colors: Vec<i32>,
|
||||
pub has_trail: bool,
|
||||
pub has_twinkle: bool,
|
||||
}
|
||||
impl FireworkExplosionImpl {
|
||||
pub fn new(
|
||||
shape: FireworkExplosionShape,
|
||||
colors: Vec<i32>,
|
||||
fade_colors: Vec<i32>,
|
||||
has_trail: bool,
|
||||
has_twinkle: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
shape,
|
||||
colors,
|
||||
fade_colors,
|
||||
has_trail,
|
||||
has_twinkle,
|
||||
}
|
||||
}
|
||||
pub fn read_data(tag: &NbtTag) -> Option<Self> {
|
||||
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<FireworkExplosionImpl>,
|
||||
}
|
||||
impl FireworksImpl {
|
||||
pub fn new(flight_duration: i32, explosions: Vec<FireworkExplosionImpl>) -> Self {
|
||||
Self {
|
||||
flight_duration,
|
||||
explosions,
|
||||
}
|
||||
}
|
||||
pub fn read_data(tag: &NbtTag) -> Option<Self> {
|
||||
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<NbtTag> = 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<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
|
||||
pub struct ProfileImpl {
|
||||
pub name: Option<String>,
|
||||
pub id: Option<[i32; 4]>,
|
||||
pub properties: Vec<ProfileProperty>,
|
||||
pub texture: Option<String>,
|
||||
pub cape: Option<String>,
|
||||
pub elytra: Option<String>,
|
||||
pub model: Option<String>,
|
||||
}
|
||||
impl ProfileImpl {
|
||||
pub fn read_data(data: &NbtTag) -> Option<Self> {
|
||||
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);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -4,11 +4,18 @@ 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, CustomDataImpl,
|
||||
CustomNameImpl, DamageImpl, DataComponentImpl, EnchantmentsImpl, EquipmentSlot, EquippableImpl,
|
||||
FireworkExplosionImpl, FireworkExplosionShape, FireworksImpl, IDSet, IDSetContent, IdOr,
|
||||
ItemModelImpl, MapIdImpl, MaxStackSizeImpl, PotionContentsImpl, SoundEvent,
|
||||
StatusEffectInstance, StoredEnchantmentsImpl, UnbreakableImpl, UseCooldownImpl, get,
|
||||
AxolotlVariantImpl, BundleContentsImpl, CatCollarImpl, CatSoundVariantImpl, CatVariantImpl,
|
||||
ChickenSoundVariantImpl, ChickenVariantImpl, ConsumableImpl, ConsumeAnimation, ConsumeEffect,
|
||||
CowSoundVariantImpl, CowVariantImpl, CustomDataImpl, CustomNameImpl, DamageImpl,
|
||||
DataComponentImpl, EnchantmentsImpl, EquipmentSlot, EquippableImpl, FireworkExplosionImpl,
|
||||
FireworkExplosionShape, FireworksImpl, FoxVariantImpl, FrogVariantImpl, HorseVariantImpl,
|
||||
IDSet, IDSetContent, IdOr, ItemModelImpl, LlamaVariantImpl, MapIdImpl, MaxStackSizeImpl,
|
||||
MooshroomVariantImpl, PaintingVariantImpl, ParrotVariantImpl, PigSoundVariantImpl,
|
||||
PigVariantImpl, PotionContentsImpl, RabbitVariantImpl, SalmonSizeImpl, SheepColorImpl,
|
||||
ShulkerColorImpl, SoundEvent, StatusEffectInstance, StoredEnchantmentsImpl,
|
||||
TropicalFishBaseColorImpl, TropicalFishPatternColorImpl, TropicalFishPatternImpl,
|
||||
UnbreakableImpl, UseCooldownImpl, VillagerVariantImpl, WolfCollarImpl, WolfSoundVariantImpl,
|
||||
WolfVariantImpl, ZombieNautilusVariantImpl, get,
|
||||
};
|
||||
use pumpkin_data::effect::StatusEffect;
|
||||
use pumpkin_data::entity::EntityType;
|
||||
@ -337,14 +344,14 @@ impl DataComponentCodec<Self> for UnbreakableImpl {
|
||||
|
||||
impl DataComponentCodec<Self> for ItemModelImpl {
|
||||
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
|
||||
seq.serialize_field::<String>("", &self.id)
|
||||
seq.serialize_field("", &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 })
|
||||
Ok(Self { id: Cow::Owned(id) })
|
||||
}
|
||||
}
|
||||
|
||||
@ -403,7 +410,7 @@ impl serde::Serialize for NetworkNbtTag {
|
||||
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::<VarInt>("", &VarInt(self.animation as i32))?;
|
||||
seq.serialize_field::<crate::IdOr<crate::SoundEvent>>(
|
||||
"",
|
||||
&data_to_proto_sound(&self.sound_event),
|
||||
@ -1088,3 +1095,55 @@ impl DataComponentCodec<Self> for BundleContentsImpl {
|
||||
Ok(Self { items })
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! codec_string_variant {
|
||||
($struct_name:ident) => {
|
||||
impl DataComponentCodec<Self> for $struct_name {
|
||||
fn serialize<T: SerializeStruct>(&self, seq: &mut T) -> Result<(), T::Error> {
|
||||
seq.serialize_field("", &self.value)
|
||||
}
|
||||
fn deserialize<'a, A: SeqAccess<'a>>(seq: &mut A) -> Result<Self, A::Error> {
|
||||
let value = seq
|
||||
.next_element::<String>()?
|
||||
.ok_or(de::Error::custom(concat!(
|
||||
"No ",
|
||||
stringify!($struct_name),
|
||||
" value string!"
|
||||
)))?;
|
||||
Ok(Self {
|
||||
value: Cow::Owned(value),
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
codec_string_variant!(VillagerVariantImpl);
|
||||
codec_string_variant!(WolfVariantImpl);
|
||||
codec_string_variant!(WolfSoundVariantImpl);
|
||||
codec_string_variant!(WolfCollarImpl);
|
||||
codec_string_variant!(FoxVariantImpl);
|
||||
codec_string_variant!(SalmonSizeImpl);
|
||||
codec_string_variant!(ParrotVariantImpl);
|
||||
codec_string_variant!(TropicalFishPatternImpl);
|
||||
codec_string_variant!(TropicalFishBaseColorImpl);
|
||||
codec_string_variant!(TropicalFishPatternColorImpl);
|
||||
codec_string_variant!(MooshroomVariantImpl);
|
||||
codec_string_variant!(RabbitVariantImpl);
|
||||
codec_string_variant!(PigVariantImpl);
|
||||
codec_string_variant!(PigSoundVariantImpl);
|
||||
codec_string_variant!(CowVariantImpl);
|
||||
codec_string_variant!(CowSoundVariantImpl);
|
||||
codec_string_variant!(ChickenVariantImpl);
|
||||
codec_string_variant!(ChickenSoundVariantImpl);
|
||||
codec_string_variant!(ZombieNautilusVariantImpl);
|
||||
codec_string_variant!(FrogVariantImpl);
|
||||
codec_string_variant!(HorseVariantImpl);
|
||||
codec_string_variant!(PaintingVariantImpl);
|
||||
codec_string_variant!(LlamaVariantImpl);
|
||||
codec_string_variant!(AxolotlVariantImpl);
|
||||
codec_string_variant!(CatVariantImpl);
|
||||
codec_string_variant!(CatSoundVariantImpl);
|
||||
codec_string_variant!(CatCollarImpl);
|
||||
codec_string_variant!(SheepColorImpl);
|
||||
codec_string_variant!(ShulkerColorImpl);
|
||||
|
||||
@ -39,13 +39,14 @@ use pumpkin_data::attributes::Attributes;
|
||||
use pumpkin_data::damage::DeathMessageType;
|
||||
use pumpkin_data::data_component_impl::Operation;
|
||||
use pumpkin_data::data_component_impl::{
|
||||
BlocksAttacksImpl, DeathProtectionImpl, EquipmentSlot, EquippableImpl, FoodImpl,
|
||||
AttributeModifiersImpl, BlocksAttacksImpl, DeathProtectionImpl, EnchantmentsImpl,
|
||||
EquipmentSlot, EquippableImpl, FoodImpl,
|
||||
};
|
||||
use pumpkin_data::effect::StatusEffect;
|
||||
use pumpkin_data::entity::{EntityPose, EntityStatus, EntityType};
|
||||
use pumpkin_data::item_stack::{DamageResult, ItemStack};
|
||||
use pumpkin_data::sound::SoundCategory;
|
||||
use pumpkin_data::{Block, translation};
|
||||
use pumpkin_data::{Block, Enchantment, translation};
|
||||
use pumpkin_data::{damage::DamageType, sound::Sound};
|
||||
use pumpkin_inventory::entity_equipment::EntityEquipment;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
@ -2031,10 +2032,105 @@ impl EntityBase for LivingEntity {
|
||||
let bypasses_cooldown_protection =
|
||||
damage_type == DamageType::GENERIC_KILL || damage_type == DamageType::OUT_OF_WORLD;
|
||||
|
||||
let mut damage_after_armor = amount;
|
||||
if !bypasses_armor_durability(&damage_type) {
|
||||
let mut armor = 0.0f32;
|
||||
let mut toughness = 0.0f32;
|
||||
{
|
||||
let equipment_lock = self.entity_equipment.lock().await;
|
||||
for slot in [
|
||||
EquipmentSlot::HEAD,
|
||||
EquipmentSlot::CHEST,
|
||||
EquipmentSlot::LEGS,
|
||||
EquipmentSlot::FEET,
|
||||
] {
|
||||
let stack_arc = equipment_lock.get(&slot);
|
||||
let stack = stack_arc.lock().await;
|
||||
if !stack.is_empty()
|
||||
&& let Some(modifiers) =
|
||||
stack.get_data_component::<AttributeModifiersImpl>()
|
||||
{
|
||||
for modifier in modifiers.attribute_modifiers.iter() {
|
||||
if modifier.r#type == &Attributes::ARMOR {
|
||||
armor += modifier.amount as f32;
|
||||
} else if modifier.r#type == &Attributes::ARMOR_TOUGHNESS {
|
||||
toughness += modifier.amount as f32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let value = 2.0f32 + toughness / 4.0;
|
||||
let clamped_armor = (armor - damage_after_armor / value)
|
||||
.max(armor / 5.0)
|
||||
.min(20.0);
|
||||
damage_after_armor *= 1.0 - clamped_armor / 25.0;
|
||||
}
|
||||
|
||||
let mut damage_after_enchantments = damage_after_armor;
|
||||
if damage_type != DamageType::OUT_OF_WORLD {
|
||||
let mut epf = 0i32;
|
||||
{
|
||||
let equipment_lock = self.entity_equipment.lock().await;
|
||||
for slot in [
|
||||
EquipmentSlot::HEAD,
|
||||
EquipmentSlot::CHEST,
|
||||
EquipmentSlot::LEGS,
|
||||
EquipmentSlot::FEET,
|
||||
] {
|
||||
let stack_arc = equipment_lock.get(&slot);
|
||||
let stack = stack_arc.lock().await;
|
||||
if !stack.is_empty()
|
||||
&& let Some(enchantments) =
|
||||
stack.get_data_component::<EnchantmentsImpl>()
|
||||
{
|
||||
for (enchantment, level) in enchantments.enchantment.iter() {
|
||||
let mut factor = 0;
|
||||
let enc = *enchantment;
|
||||
if enc == &Enchantment::PROTECTION {
|
||||
if damage_type != DamageType::DROWN
|
||||
&& damage_type != DamageType::STARVE
|
||||
&& damage_type != DamageType::GENERIC_KILL
|
||||
{
|
||||
factor = *level;
|
||||
}
|
||||
} else if enc == &Enchantment::FIRE_PROTECTION {
|
||||
if is_fire_damage {
|
||||
factor = *level * 2;
|
||||
}
|
||||
} else if enc == &Enchantment::BLAST_PROTECTION {
|
||||
if damage_type == DamageType::EXPLOSION
|
||||
|| damage_type == DamageType::PLAYER_EXPLOSION
|
||||
{
|
||||
factor = *level * 2;
|
||||
}
|
||||
} else if enc == &Enchantment::PROJECTILE_PROTECTION {
|
||||
if damage_type == DamageType::ARROW
|
||||
|| damage_type == DamageType::MOB_PROJECTILE
|
||||
|| damage_type == DamageType::THROWN
|
||||
{
|
||||
factor = (*level) * 2;
|
||||
}
|
||||
} else if enc == &Enchantment::FEATHER_FALLING
|
||||
&& damage_type == DamageType::FALL
|
||||
{
|
||||
factor = (*level) * 4;
|
||||
}
|
||||
epf += factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
epf = epf.min(20);
|
||||
if epf > 0 {
|
||||
damage_after_enchantments *= 1.0 - (epf as f32 * 0.04);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply Resistance effect reduction (20% per level), excluding bypasses_cooldown_protection and starvation damage
|
||||
let resistance_reduction =
|
||||
if !bypasses_cooldown_protection && damage_type != DamageType::STARVE {
|
||||
self.get_effect(&pumpkin_data::effect::StatusEffect::RESISTANCE)
|
||||
self.get_effect(&StatusEffect::RESISTANCE)
|
||||
.await
|
||||
.map_or(0.0, |e| 0.2 * (e.amplifier + 1) as f32)
|
||||
} else {
|
||||
@ -2042,10 +2138,10 @@ impl EntityBase for LivingEntity {
|
||||
};
|
||||
|
||||
// Total damage after reductions
|
||||
let effective_amount = amount * (1.0 - resistance_reduction);
|
||||
let effective_amount = damage_after_enchantments * (1.0 - resistance_reduction);
|
||||
|
||||
if resistance_reduction > 0.0 {
|
||||
let resisted = amount * resistance_reduction;
|
||||
let resisted = damage_after_enchantments * resistance_reduction;
|
||||
if let Some(player) = caller.get_player() {
|
||||
player
|
||||
.increment_stat(
|
||||
|
||||
@ -487,8 +487,34 @@ pub trait Mob: EntityBase + Send + Sync {
|
||||
fn get_base_experience_reward(&self) -> u32 {
|
||||
self.get_entity().entity_type.experience_reward
|
||||
}
|
||||
|
||||
fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = self.get_entity();
|
||||
let is_baby = entity.age.load(std::sync::atomic::Ordering::Relaxed) < 0;
|
||||
if is_baby {
|
||||
entity.send_meta_data(&[Metadata::new(
|
||||
TrackedData::BABY_ID,
|
||||
MetaDataType::BOOLEAN,
|
||||
true,
|
||||
)]);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn mob_set_variant_name(&self, _name: &str) {}
|
||||
}
|
||||
impl<T: Mob + Send + 'static> EntityBase for T {
|
||||
fn init_data_tracker(&self) -> EntityBaseFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
self.mob_init_data_tracker().await;
|
||||
})
|
||||
}
|
||||
|
||||
fn set_variant_name(&self, name: &str) {
|
||||
self.mob_set_variant_name(name);
|
||||
}
|
||||
|
||||
fn tick<'a>(
|
||||
&'a self,
|
||||
caller: &'a Arc<dyn EntityBase>,
|
||||
|
||||
@ -187,6 +187,7 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any {
|
||||
}
|
||||
})
|
||||
}
|
||||
fn set_variant_name(&self, _name: &str) {}
|
||||
|
||||
// This method takes ownership of Arc<Self>, so the lifetime bounds are different.
|
||||
fn teleport(
|
||||
|
||||
@ -1,9 +1,18 @@
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::{
|
||||
Arc, Weak,
|
||||
atomic::{AtomicU8, Ordering},
|
||||
};
|
||||
|
||||
use pumpkin_data::{entity::EntityType, item::Item};
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::meta_data_type::MetaDataType;
|
||||
use pumpkin_data::tracked_data::TrackedData;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_protocol::codec::var_int::VarInt;
|
||||
use pumpkin_protocol::java::client::play::Metadata;
|
||||
|
||||
use crate::entity::{
|
||||
Entity, NBTStorage,
|
||||
Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture,
|
||||
ai::goal::{
|
||||
breed::BreedGoal, escape_danger::EscapeDangerGoal, follow_parent::FollowParentGoal,
|
||||
look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal,
|
||||
@ -14,17 +23,18 @@ use crate::entity::{
|
||||
|
||||
const TEMPT_ITEMS: &[&Item] = &[&Item::COD, &Item::SALMON];
|
||||
|
||||
/// Represents a Cat, a passive mob that can be tamed and scares away creepers.
|
||||
///
|
||||
/// Wiki: <https://minecraft.wiki/w/Cat>
|
||||
pub struct CatEntity {
|
||||
pub mob_entity: MobEntity,
|
||||
pub variant: AtomicU8,
|
||||
}
|
||||
|
||||
impl CatEntity {
|
||||
pub fn new(entity: Entity) -> Arc<Self> {
|
||||
let mob_entity = MobEntity::new(entity);
|
||||
let cat = Self { mob_entity };
|
||||
let cat = Self {
|
||||
mob_entity,
|
||||
variant: AtomicU8::new(9), // Default to tabby
|
||||
};
|
||||
let mob_arc = Arc::new(cat);
|
||||
let mob_weak: Weak<dyn Mob> = {
|
||||
let mob_arc: Arc<dyn Mob> = mob_arc.clone();
|
||||
@ -53,10 +63,91 @@ impl CatEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl NBTStorage for CatEntity {}
|
||||
impl NBTStorage for CatEntity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||
let variant_str = match self.variant.load(Ordering::Relaxed) {
|
||||
0 => "minecraft:all_black",
|
||||
1 => "minecraft:black",
|
||||
2 => "minecraft:british_shorthair",
|
||||
3 => "minecraft:calico",
|
||||
4 => "minecraft:jellie",
|
||||
5 => "minecraft:persian",
|
||||
6 => "minecraft:ragdoll",
|
||||
7 => "minecraft:red",
|
||||
8 => "minecraft:siamese",
|
||||
10 => "minecraft:white",
|
||||
_ => "minecraft:tabby",
|
||||
};
|
||||
nbt.put_string("variant", variant_str.to_string());
|
||||
})
|
||||
}
|
||||
|
||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
if let Some(variant_str) = nbt.get_string("variant") {
|
||||
let variant = match variant_str
|
||||
.strip_prefix("minecraft:")
|
||||
.unwrap_or(variant_str)
|
||||
{
|
||||
"all_black" => 0,
|
||||
"black" => 1,
|
||||
"british_shorthair" => 2,
|
||||
"calico" => 3,
|
||||
"jellie" => 4,
|
||||
"persian" => 5,
|
||||
"ragdoll" => 6,
|
||||
"red" => 7,
|
||||
"siamese" => 8,
|
||||
"white" => 10,
|
||||
_ => 9,
|
||||
};
|
||||
self.variant.store(variant, Ordering::Relaxed);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Mob for CatEntity {
|
||||
fn get_mob_entity(&self) -> &MobEntity {
|
||||
&self.mob_entity
|
||||
}
|
||||
|
||||
fn mob_set_variant_name(&self, name: &str) {
|
||||
let variant = match name.strip_prefix("minecraft:").unwrap_or(name) {
|
||||
"all_black" => 0,
|
||||
"black" => 1,
|
||||
"british_shorthair" => 2,
|
||||
"calico" => 3,
|
||||
"jellie" => 4,
|
||||
"persian" => 5,
|
||||
"ragdoll" => 6,
|
||||
"red" => 7,
|
||||
"siamese" => 8,
|
||||
"white" => 10,
|
||||
_ => 9,
|
||||
};
|
||||
self.variant.store(variant, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = self.get_entity();
|
||||
let is_baby = entity.age.load(Ordering::Relaxed) < 0;
|
||||
if is_baby {
|
||||
entity.send_meta_data(&[Metadata::new(
|
||||
TrackedData::BABY_ID,
|
||||
MetaDataType::BOOLEAN,
|
||||
true,
|
||||
)]);
|
||||
}
|
||||
entity.send_meta_data(&[Metadata::new(
|
||||
TrackedData::CAT_VARIANT,
|
||||
MetaDataType::CAT_VARIANT,
|
||||
VarInt(self.variant.load(Ordering::Relaxed) as i32),
|
||||
)]);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
use std::sync::{
|
||||
Arc, Weak,
|
||||
atomic::{AtomicI32, Ordering, Ordering::Relaxed},
|
||||
atomic::{AtomicI32, AtomicU8, Ordering, Ordering::Relaxed},
|
||||
};
|
||||
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::meta_data_type::MetaDataType;
|
||||
use pumpkin_data::particle::Particle;
|
||||
use pumpkin_data::sound::{Sound, SoundCategory};
|
||||
use pumpkin_data::tracked_data::TrackedData;
|
||||
use pumpkin_data::{entity::EntityType, item::Item};
|
||||
use pumpkin_protocol::codec::var_int::VarInt;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
use rand::RngExt;
|
||||
|
||||
@ -36,6 +39,7 @@ const TEMPT_ITEMS: &[&Item] = &[
|
||||
/// Wiki: <https://minecraft.wiki/w/Chicken>
|
||||
pub struct ChickenEntity {
|
||||
pub mob_entity: MobEntity,
|
||||
pub variant: AtomicU8,
|
||||
egg_lay_time: AtomicI32,
|
||||
}
|
||||
|
||||
@ -45,6 +49,7 @@ impl ChickenEntity {
|
||||
let egg_lay_time = rand::rng().random_range(6000..12000);
|
||||
let chicken = Self {
|
||||
mob_entity,
|
||||
variant: AtomicU8::new(1), // Default to temperate
|
||||
egg_lay_time: AtomicI32::new(egg_lay_time),
|
||||
};
|
||||
let mob_arc = Arc::new(chicken);
|
||||
@ -78,6 +83,12 @@ impl NBTStorage for ChickenEntity {
|
||||
Box::pin(async {
|
||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||
nbt.put_int("EggLayTime", self.egg_lay_time.load(Ordering::Relaxed));
|
||||
let variant_str = match self.variant.load(Ordering::Relaxed) {
|
||||
0 => "minecraft:cold",
|
||||
2 => "minecraft:warm",
|
||||
_ => "minecraft:temperate",
|
||||
};
|
||||
nbt.put_string("variant", variant_str.to_string());
|
||||
})
|
||||
}
|
||||
|
||||
@ -86,6 +97,17 @@ impl NBTStorage for ChickenEntity {
|
||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
self.egg_lay_time
|
||||
.store(nbt.get_int("EggLayTime").unwrap_or(6000), Ordering::Relaxed);
|
||||
if let Some(variant_str) = nbt.get_string("variant") {
|
||||
let variant = match variant_str
|
||||
.strip_prefix("minecraft:")
|
||||
.unwrap_or(variant_str)
|
||||
{
|
||||
"cold" => 0,
|
||||
"warm" => 2,
|
||||
_ => 1,
|
||||
};
|
||||
self.variant.store(variant, Ordering::Relaxed);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -95,6 +117,34 @@ impl Mob for ChickenEntity {
|
||||
&self.mob_entity
|
||||
}
|
||||
|
||||
fn mob_set_variant_name(&self, name: &str) {
|
||||
let variant = match name.strip_prefix("minecraft:").unwrap_or(name) {
|
||||
"cold" => 0,
|
||||
"warm" => 2,
|
||||
_ => 1,
|
||||
};
|
||||
self.variant.store(variant, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = self.get_entity();
|
||||
let is_baby = entity.age.load(Ordering::Relaxed) < 0;
|
||||
if is_baby {
|
||||
entity.send_meta_data(&[pumpkin_protocol::java::client::play::Metadata::new(
|
||||
TrackedData::BABY_ID,
|
||||
MetaDataType::BOOLEAN,
|
||||
true,
|
||||
)]);
|
||||
}
|
||||
entity.send_meta_data(&[pumpkin_protocol::java::client::play::Metadata::new(
|
||||
TrackedData::VARIANT,
|
||||
MetaDataType::CHICKEN_VARIANT,
|
||||
VarInt(self.variant.load(Ordering::Relaxed) as i32),
|
||||
)]);
|
||||
})
|
||||
}
|
||||
|
||||
fn mob_tick<'a>(&'a self, _caller: &'a Arc<dyn EntityBase>) -> EntityBaseFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
if self.mob_entity.living_entity.dead.load(Relaxed) {
|
||||
|
||||
@ -1,9 +1,17 @@
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::{
|
||||
Arc, Weak,
|
||||
atomic::{AtomicU8, Ordering},
|
||||
};
|
||||
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_data::meta_data_type::MetaDataType;
|
||||
use pumpkin_data::tracked_data::TrackedData;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_protocol::codec::var_int::VarInt;
|
||||
use pumpkin_protocol::java::client::play::Metadata;
|
||||
|
||||
use crate::entity::{
|
||||
Entity, NBTStorage,
|
||||
Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture,
|
||||
ai::goal::{
|
||||
look_around::RandomLookAroundGoal, look_at_entity::LookAtEntityGoal, swim::SwimGoal,
|
||||
wander_around::WanderAroundGoal,
|
||||
@ -16,12 +24,16 @@ use crate::entity::{
|
||||
/// Wiki: <https://minecraft.wiki/w/Frog>
|
||||
pub struct FrogEntity {
|
||||
pub mob_entity: MobEntity,
|
||||
pub variant: AtomicU8,
|
||||
}
|
||||
|
||||
impl FrogEntity {
|
||||
pub fn new(entity: Entity) -> Arc<Self> {
|
||||
let mob_entity = MobEntity::new(entity);
|
||||
let frog = Self { mob_entity };
|
||||
let frog = Self {
|
||||
mob_entity,
|
||||
variant: AtomicU8::new(1), // Default to temperate
|
||||
};
|
||||
let mob_arc = Arc::new(frog);
|
||||
let mob_weak: Weak<dyn Mob> = {
|
||||
let mob_arc: Arc<dyn Mob> = mob_arc.clone();
|
||||
@ -44,10 +56,64 @@ impl FrogEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl NBTStorage for FrogEntity {}
|
||||
impl NBTStorage for FrogEntity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||
let variant_str = match self.variant.load(Ordering::Relaxed) {
|
||||
0 => "minecraft:cold",
|
||||
2 => "minecraft:warm",
|
||||
_ => "minecraft:temperate",
|
||||
};
|
||||
nbt.put_string("variant", variant_str.to_string());
|
||||
})
|
||||
}
|
||||
|
||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
if let Some(variant_str) = nbt.get_string("variant") {
|
||||
let variant = match variant_str {
|
||||
"minecraft:cold" | "cold" => 0,
|
||||
"minecraft:warm" | "warm" => 2,
|
||||
_ => 1,
|
||||
};
|
||||
self.variant.store(variant, Ordering::Relaxed);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Mob for FrogEntity {
|
||||
fn get_mob_entity(&self) -> &MobEntity {
|
||||
&self.mob_entity
|
||||
}
|
||||
|
||||
fn mob_set_variant_name(&self, name: &str) {
|
||||
let variant = match name {
|
||||
"minecraft:cold" | "cold" => 0,
|
||||
"minecraft:warm" | "warm" => 2,
|
||||
_ => 1,
|
||||
};
|
||||
self.variant.store(variant, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = self.get_entity();
|
||||
let is_baby = entity.age.load(Ordering::Relaxed) < 0;
|
||||
if is_baby {
|
||||
entity.send_meta_data(&[Metadata::new(
|
||||
TrackedData::BABY_ID,
|
||||
MetaDataType::BOOLEAN,
|
||||
true,
|
||||
)]);
|
||||
}
|
||||
entity.send_meta_data(&[Metadata::new(
|
||||
TrackedData::VARIANT,
|
||||
MetaDataType::FROG_VARIANT,
|
||||
VarInt(self.variant.load(Ordering::Relaxed) as i32),
|
||||
)]);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,18 @@
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::{
|
||||
Arc, Weak,
|
||||
atomic::{AtomicU8, Ordering},
|
||||
};
|
||||
|
||||
use pumpkin_data::entity::EntityType;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::meta_data_type::MetaDataType;
|
||||
use pumpkin_data::tracked_data::TrackedData;
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_protocol::codec::var_int::VarInt;
|
||||
use pumpkin_protocol::java::client::play::Metadata;
|
||||
|
||||
use crate::entity::{
|
||||
Entity, NBTStorage,
|
||||
Entity, EntityBase, EntityBaseFuture, NBTStorage, NbtFuture,
|
||||
ai::goal::{
|
||||
beg::BegGoal, breed::BreedGoal, escape_danger::EscapeDangerGoal,
|
||||
follow_parent::FollowParentGoal, look_around::RandomLookAroundGoal,
|
||||
@ -15,12 +23,16 @@ use crate::entity::{
|
||||
|
||||
pub struct WolfEntity {
|
||||
pub mob_entity: MobEntity,
|
||||
pub variant: AtomicU8,
|
||||
}
|
||||
|
||||
impl WolfEntity {
|
||||
pub fn new(entity: Entity) -> Arc<Self> {
|
||||
let mob_entity = MobEntity::new(entity);
|
||||
let wolf = Self { mob_entity };
|
||||
let wolf = Self {
|
||||
mob_entity,
|
||||
variant: AtomicU8::new(3), // Default to pale
|
||||
};
|
||||
let mob_arc = Arc::new(wolf);
|
||||
let mob_weak: Weak<dyn Mob> = {
|
||||
let mob_arc: Arc<dyn Mob> = mob_arc.clone();
|
||||
@ -49,10 +61,85 @@ impl WolfEntity {
|
||||
}
|
||||
}
|
||||
|
||||
impl NBTStorage for WolfEntity {}
|
||||
impl NBTStorage for WolfEntity {
|
||||
fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) -> NbtFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
self.mob_entity.living_entity.write_nbt(nbt).await;
|
||||
let variant_str = match self.variant.load(Ordering::Relaxed) {
|
||||
0 => "minecraft:ashen",
|
||||
1 => "minecraft:black",
|
||||
2 => "minecraft:chestnut",
|
||||
4 => "minecraft:rusty",
|
||||
5 => "minecraft:snowy",
|
||||
6 => "minecraft:spotted",
|
||||
7 => "minecraft:striped",
|
||||
8 => "minecraft:woods",
|
||||
_ => "minecraft:pale",
|
||||
};
|
||||
nbt.put_string("variant", variant_str.to_string());
|
||||
})
|
||||
}
|
||||
|
||||
fn read_nbt_non_mut<'a>(&'a self, nbt: &'a NbtCompound) -> NbtFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
self.mob_entity.living_entity.read_nbt_non_mut(nbt).await;
|
||||
if let Some(variant_str) = nbt.get_string("variant") {
|
||||
let variant = match variant_str
|
||||
.strip_prefix("minecraft:")
|
||||
.unwrap_or(variant_str)
|
||||
{
|
||||
"ashen" => 0,
|
||||
"black" => 1,
|
||||
"chestnut" => 2,
|
||||
"rusty" => 4,
|
||||
"snowy" => 5,
|
||||
"spotted" => 6,
|
||||
"striped" => 7,
|
||||
"woods" => 8,
|
||||
_ => 3,
|
||||
};
|
||||
self.variant.store(variant, Ordering::Relaxed);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Mob for WolfEntity {
|
||||
fn get_mob_entity(&self) -> &MobEntity {
|
||||
&self.mob_entity
|
||||
}
|
||||
|
||||
fn mob_set_variant_name(&self, name: &str) {
|
||||
let variant = match name.strip_prefix("minecraft:").unwrap_or(name) {
|
||||
"ashen" => 0,
|
||||
"black" => 1,
|
||||
"chestnut" => 2,
|
||||
"rusty" => 4,
|
||||
"snowy" => 5,
|
||||
"spotted" => 6,
|
||||
"striped" => 7,
|
||||
"woods" => 8,
|
||||
_ => 3,
|
||||
};
|
||||
self.variant.store(variant, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn mob_init_data_tracker(&self) -> EntityBaseFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
let entity = self.get_entity();
|
||||
let is_baby = entity.age.load(Ordering::Relaxed) < 0;
|
||||
if is_baby {
|
||||
entity.send_meta_data(&[Metadata::new(
|
||||
TrackedData::BABY_ID,
|
||||
MetaDataType::BOOLEAN,
|
||||
true,
|
||||
)]);
|
||||
}
|
||||
entity.send_meta_data(&[Metadata::new(
|
||||
TrackedData::VARIANT,
|
||||
MetaDataType::WOLF_VARIANT,
|
||||
VarInt(self.variant.load(Ordering::Relaxed) as i32),
|
||||
)]);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -148,15 +148,11 @@ impl EntityBase for EggEntity {
|
||||
let world_clone = world.clone();
|
||||
let spawn_pos_clone = spawn_pos;
|
||||
|
||||
// Read the stack stored in set_item_stack
|
||||
//let stack = self.item_stack.lock().unwrap();
|
||||
|
||||
// TODO: Map the item ID to the chicken variant
|
||||
// let variant = match stack.item.id {
|
||||
// id if id == Item::BLUE_EGG.id => EntityVariant::Cold,
|
||||
// id if id == Item::BROWN_EGG.id => EntityVariant::Warm,
|
||||
// _ => EntityVariant::Default,
|
||||
// };
|
||||
let variant_name = {
|
||||
let stack = self.item_stack.read().await;
|
||||
stack.get_data_component::<pumpkin_data::data_component_impl::ChickenVariantImpl>()
|
||||
.map(|comp| comp.value.clone())
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
for _ in 0..to_spawn {
|
||||
@ -167,7 +163,9 @@ impl EntityBase for EggEntity {
|
||||
let new_entity = mob.get_entity();
|
||||
new_entity.set_rotation(yaw, 0.0);
|
||||
new_entity.set_age(-24000);
|
||||
//new_entity.set_variant(variant);
|
||||
if let Some(name) = &variant_name {
|
||||
mob.set_variant_name(name);
|
||||
}
|
||||
|
||||
world_clone.spawn_entity(mob).await;
|
||||
}
|
||||
|
||||
@ -1,10 +1,16 @@
|
||||
use std::pin::Pin;
|
||||
|
||||
use crate::block::entities::mob_spawner::MobSpawnerBlockEntity;
|
||||
use crate::entity::EntityBase;
|
||||
use crate::entity::player::Player;
|
||||
use crate::entity::r#type::from_type;
|
||||
use crate::item::{ItemBehaviour, ItemMetadata};
|
||||
use crate::server::Server;
|
||||
use pumpkin_data::data_component_impl::{
|
||||
AxolotlVariantImpl, CatVariantImpl, ChickenVariantImpl, CowVariantImpl, FoxVariantImpl,
|
||||
FrogVariantImpl, HorseVariantImpl, LlamaVariantImpl, MooshroomVariantImpl, PigVariantImpl,
|
||||
RabbitVariantImpl, SheepColorImpl, ShulkerColorImpl, VillagerVariantImpl, WolfVariantImpl,
|
||||
};
|
||||
use pumpkin_data::entity::entity_from_egg;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::{Block, BlockDirection};
|
||||
@ -21,6 +27,40 @@ impl ItemMetadata for SpawnEggItem {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_entity_variant(item: &ItemStack, mob: &dyn EntityBase) {
|
||||
if let Some(comp) = item.get_data_component::<ChickenVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<FrogVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<WolfVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<CatVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<VillagerVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<FoxVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<MooshroomVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<RabbitVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<PigVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<CowVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<HorseVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<LlamaVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<AxolotlVariantImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<SheepColorImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
} else if let Some(comp) = item.get_data_component::<ShulkerColorImpl>() {
|
||||
mob.set_variant_name(&comp.value);
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemBehaviour for SpawnEggItem {
|
||||
fn use_on_block<'a>(
|
||||
&'a self,
|
||||
@ -60,10 +100,11 @@ impl ItemBehaviour for SpawnEggItem {
|
||||
// Set the rotation
|
||||
mob.get_entity().set_rotation(yaw, 0.0);
|
||||
|
||||
apply_entity_variant(item, mob.as_ref());
|
||||
|
||||
// Broadcast the new mob to all players
|
||||
world.spawn_entity(mob).await;
|
||||
item.decrement_unless_creative(player.gamemode.load(), 1);
|
||||
// TODO: send/configure additional commands/data based on the type of entity (horse, slime, etc)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -255,14 +255,24 @@ impl LootFunctionExt for LootFunction {
|
||||
if !properties_to_copy.is_empty() {
|
||||
for stack in stacks.iter_mut() {
|
||||
if let Some(block_state_comp) = stack.get_data_component_mut::<pumpkin_data::data_component_impl::BlockStateImpl>() {
|
||||
let mut props = block_state_comp.properties.to_mut().clone();
|
||||
for (k, v) in &properties_to_copy {
|
||||
block_state_comp.properties.insert(k.clone(), v.clone());
|
||||
}
|
||||
if let Some(pos) = props.iter().position(|(pk, _)| pk.as_ref() == k) {
|
||||
props[pos].1 = std::borrow::Cow::Owned(v.clone());
|
||||
} else {
|
||||
props.push((std::borrow::Cow::Owned(k.clone()), std::borrow::Cow::Owned(v.clone())));
|
||||
}
|
||||
}
|
||||
block_state_comp.properties = std::borrow::Cow::Owned(props);
|
||||
} else {
|
||||
let properties: Vec<(std::borrow::Cow<'static, str>, std::borrow::Cow<'static, str>)> = properties_to_copy
|
||||
.iter()
|
||||
.map(|(k, v)| (std::borrow::Cow::Owned(k.clone()), std::borrow::Cow::Owned(v.clone())))
|
||||
.collect();
|
||||
stack.patch.push((
|
||||
pumpkin_data::data_component::DataComponent::BlockState,
|
||||
Some(Box::new(pumpkin_data::data_component_impl::BlockStateImpl {
|
||||
properties: properties_to_copy.clone(),
|
||||
properties: std::borrow::Cow::Owned(properties),
|
||||
})),
|
||||
));
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user