feat: add enchanting table
This commit is contained in:
parent
fd56a5be7a
commit
63d39f46d4
29
Cargo.lock
generated
29
Cargo.lock
generated
@ -892,9 +892,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crossfire"
|
||||
version = "3.1.7"
|
||||
version = "3.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0042e53977a94d5d10de04ce7eb6016aa212c08e83ea202f7a9f102ff104303"
|
||||
checksum = "ebed7d0a3a92ebc771f880516a5eecef5942a0fc67855dc4f30e719b04c77ac5"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"futures-core",
|
||||
@ -936,9 +936,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
@ -1035,7 +1035,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.4",
|
||||
"const-oid 0.9.6",
|
||||
"crypto-common 0.1.6",
|
||||
"crypto-common 0.1.7",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@ -1394,9 +1394,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.9"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
@ -2836,7 +2836,7 @@ dependencies = [
|
||||
"tracing-subscriber",
|
||||
"ureq",
|
||||
"uuid",
|
||||
"wasmparser 0.247.0",
|
||||
"wasmparser 0.248.0",
|
||||
"wasmtime",
|
||||
"wasmtime-wasi",
|
||||
]
|
||||
@ -2893,6 +2893,7 @@ dependencies = [
|
||||
"pumpkin-protocol",
|
||||
"pumpkin-util",
|
||||
"pumpkin-world",
|
||||
"rand 0.10.1",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@ -4464,6 +4465,18 @@ name = "wasmparser"
|
||||
version = "0.247.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e6fb4c2bee46c5ea4d40f8cdb5c131725cd976718ec56f1c8e82fbde5fa2a80"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"hashbrown 0.17.0",
|
||||
"indexmap",
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasmparser"
|
||||
version = "0.248.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa4439c5eee9df71ee0c6efb37f63b1fcb1fec38f85f5142c54e7ed05d33091a"
|
||||
dependencies = [
|
||||
"bitflags 2.11.1",
|
||||
"hashbrown 0.17.0",
|
||||
|
||||
@ -120,7 +120,7 @@ console-subscriber = { version = "0.5.0", default-features = false }
|
||||
crc-fast = "1.10.0"
|
||||
criterion = { version = "0.8", default-features = false }
|
||||
crossbeam-utils = "0.8.21"
|
||||
crossfire = { version = "3.1.7", features = ["compat"] }
|
||||
crossfire = { version = "3.1.10", features = ["compat"] }
|
||||
crypto-bigint = "0.7.3"
|
||||
dashmap = "6.1"
|
||||
ecdsa = "0.16.9"
|
||||
@ -160,7 +160,7 @@ ruzstd = "0.8.2"
|
||||
serde_json5 = "0.2.1"
|
||||
sha1 = "=0.11.0"
|
||||
sha2 = "=0.11.0"
|
||||
signature = "2.2.0"
|
||||
signature = "3.0.0"
|
||||
tracing = "0.1.44"
|
||||
tracing-subscriber = { version = "0.3.23", features = [
|
||||
"env-filter",
|
||||
@ -181,7 +181,7 @@ notify = "8.2.0"
|
||||
wasmtime = "44.0"
|
||||
wasmtime-wasi = "44.0"
|
||||
wit-bindgen = "0.57"
|
||||
wasmparser = "0.247"
|
||||
wasmparser = "0.248"
|
||||
|
||||
postcard = { version = "1.1", features = ["alloc"] }
|
||||
tracing-serde-structured = "0.4"
|
||||
|
||||
@ -159,7 +159,7 @@ mod test {
|
||||
fn encoding() {
|
||||
assert_encode_success!(3, JsonOps, json!(3));
|
||||
assert_encode_success!(-68i8, JsonOps, json!(-68));
|
||||
assert_encode_success!(-913813743, JsonOps, json!(-913813743));
|
||||
assert_encode_success!(-913_813_743, JsonOps, json!(-913_813_743));
|
||||
assert_encode_success!("Hello, world!".to_string(), JsonOps, json!("Hello, world!"));
|
||||
assert_encode_success!(String::new(), JsonOps, json!(""));
|
||||
assert_encode_success!(ByteBuffer::from(vec![1, 2, 3]), JsonOps, json!([1, 2, 3]));
|
||||
@ -175,7 +175,7 @@ mod test {
|
||||
);
|
||||
|
||||
assert_encode_success!(3u8, JsonOps, json!(3));
|
||||
assert_encode_success!(923482312u64, JsonOps, json!(923482312));
|
||||
assert_encode_success!(923_482_312u64, JsonOps, json!(923_482_312));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -192,7 +192,7 @@ mod test {
|
||||
assert_decode!(String, json!(1), JsonOps, is_error);
|
||||
|
||||
assert_decode!(u32, json!(-45), JsonOps, is_error);
|
||||
assert_decode!(u64, json!(-132541235), JsonOps, is_error);
|
||||
assert_decode!(u64, json!(132541235), JsonOps, is_success);
|
||||
assert_decode!(u64, json!(-132_541_235), JsonOps, is_error);
|
||||
assert_decode!(u64, json!(132_541_235), JsonOps, is_success);
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,18 @@ pub struct Enchantment {
|
||||
pub max_level: i32,
|
||||
/// Equipment slots this enchantment's attribute modifiers apply to.
|
||||
pub slots: Vec<AttributeModifierSlot>, // TODO: add more
|
||||
/// The weight of this enchantment (used for random selection).
|
||||
pub weight: i32,
|
||||
/// The minimum cost to get this enchantment.
|
||||
pub min_cost: Cost,
|
||||
/// The maximum cost to get this enchantment.
|
||||
pub max_cost: Cost,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Copy)]
|
||||
pub struct Cost {
|
||||
pub base: i32,
|
||||
pub per_level_above_first: i32,
|
||||
}
|
||||
|
||||
/// Equipment slot category that an enchantment's attribute modifier applies to.
|
||||
@ -81,6 +93,7 @@ pub fn build() -> TokenStream {
|
||||
.expect("Failed to parse enchantments.json");
|
||||
|
||||
let mut variants = TokenStream::new();
|
||||
let mut all_variants = TokenStream::new();
|
||||
let mut name_to_type = TokenStream::new();
|
||||
let mut id_to_type = TokenStream::new();
|
||||
|
||||
@ -88,6 +101,7 @@ pub fn build() -> TokenStream {
|
||||
let id = enchantment.id;
|
||||
let raw_name = name.strip_prefix("minecraft:").unwrap();
|
||||
let format_name = format_ident!("{}", raw_name.to_shouty_snake_case());
|
||||
all_variants.extend(quote! { &Self::#format_name, });
|
||||
let anvil_cost = enchantment.anvil_cost;
|
||||
let supported_items = format_ident!(
|
||||
"{}",
|
||||
@ -99,6 +113,12 @@ pub fn build() -> TokenStream {
|
||||
.to_uppercase()
|
||||
);
|
||||
let max_level = enchantment.max_level;
|
||||
let weight = enchantment.weight;
|
||||
let min_cost_base = enchantment.min_cost.base;
|
||||
let min_cost_per_level = enchantment.min_cost.per_level_above_first;
|
||||
let max_cost_base = enchantment.max_cost.base;
|
||||
let max_cost_per_level = enchantment.max_cost.per_level_above_first;
|
||||
|
||||
let slots = enchantment.slots;
|
||||
let slots = slots.iter().map(AttributeModifierSlot::to_tokens);
|
||||
let Translate { translate, bedrock_translate: _, with: _ } = &*enchantment.description.0.content else {
|
||||
@ -125,7 +145,16 @@ pub fn build() -> TokenStream {
|
||||
supported_items: &ItemTag::#supported_items,
|
||||
exclusive_set: Some(&EnchantmentTag::#exclusive_set),
|
||||
max_level: #max_level,
|
||||
slots: &[#(#slots),*]
|
||||
slots: &[#(#slots),*],
|
||||
weight: #weight,
|
||||
min_cost: Cost {
|
||||
base: #min_cost_base,
|
||||
per_level_above_first: #min_cost_per_level,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: #max_cost_base,
|
||||
per_level_above_first: #max_cost_per_level,
|
||||
},
|
||||
};
|
||||
}]);
|
||||
} else {
|
||||
@ -139,7 +168,16 @@ pub fn build() -> TokenStream {
|
||||
supported_items: &ItemTag::#supported_items,
|
||||
exclusive_set: None,
|
||||
max_level: #max_level,
|
||||
slots: &[#(#slots),*]
|
||||
slots: &[#(#slots),*],
|
||||
weight: #weight,
|
||||
min_cost: Cost {
|
||||
base: #min_cost_base,
|
||||
per_level_above_first: #min_cost_per_level,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: #max_cost_base,
|
||||
per_level_above_first: #max_cost_per_level,
|
||||
},
|
||||
};
|
||||
}]);
|
||||
}
|
||||
@ -168,9 +206,24 @@ pub fn build() -> TokenStream {
|
||||
pub supported_items: &'static Tag,
|
||||
pub exclusive_set: Option<&'static Tag>,
|
||||
pub max_level: i32,
|
||||
pub slots: &'static [AttributeModifierSlot]
|
||||
pub slots: &'static [AttributeModifierSlot],
|
||||
pub weight: i32,
|
||||
pub min_cost: Cost,
|
||||
pub max_cost: Cost,
|
||||
// TODO: add more
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Cost {
|
||||
pub base: i32,
|
||||
pub per_level_above_first: i32,
|
||||
}
|
||||
|
||||
impl Cost {
|
||||
pub fn calculate(&self, level: i32) -> i32 {
|
||||
self.base + self.per_level_above_first * (level - 1)
|
||||
}
|
||||
}
|
||||
impl Taggable for Enchantment {
|
||||
#[inline]
|
||||
fn tag_key() -> RegistryKey {
|
||||
@ -212,6 +265,12 @@ pub fn build() -> TokenStream {
|
||||
}
|
||||
|
||||
impl Enchantment {
|
||||
pub const ALL: &'static [&'static Self] = &[#all_variants];
|
||||
|
||||
pub fn all() -> Iter<'static, &'static Self> {
|
||||
Self::ALL.iter()
|
||||
}
|
||||
|
||||
#variants
|
||||
|
||||
pub fn from_name(name: &str) -> Option<&'static Self> {
|
||||
|
||||
@ -62,6 +62,13 @@ pub struct ItemComponents {
|
||||
pub damage_resistant: Option<DamageResistantComponent>,
|
||||
#[serde(rename = "minecraft:weapon")]
|
||||
pub weapon: Option<WeaponComponent>,
|
||||
#[serde(rename = "minecraft:enchantable")]
|
||||
pub enchantable: Option<EnchantableComponent>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct EnchantableComponent {
|
||||
pub value: i32,
|
||||
}
|
||||
|
||||
impl ToTokens for ItemComponents {
|
||||
@ -573,6 +580,11 @@ impl ToTokens for ItemComponents {
|
||||
shearing_sound: #shearing_sound
|
||||
}), });
|
||||
}
|
||||
|
||||
if let Some(enchantable) = &self.enchantable {
|
||||
let value = LitInt::new(&enchantable.value.to_string(), Span::call_site());
|
||||
tokens.extend(quote! { (Enchantable, &EnchantableImpl { value: #value }), });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,9 +4,9 @@ use crate::attributes::Attributes;
|
||||
use crate::data_component::DataComponent;
|
||||
use crate::data_component::DataComponent::{
|
||||
AttributeModifiers, BlocksAttacks, Consumable, CustomData, CustomName, Damage, DamageResistant,
|
||||
DeathProtection, Enchantments, Equippable, FireworkExplosion, Fireworks, Food, ItemModel,
|
||||
ItemName, JukeboxPlayable, MaxDamage, MaxStackSize, PotionContents, StoredEnchantments, Tool,
|
||||
Unbreakable, UseCooldown, Weapon,
|
||||
DeathProtection, Enchantable, Enchantments, Equippable, FireworkExplosion, Fireworks, Food,
|
||||
ItemModel, ItemName, JukeboxPlayable, MaxDamage, MaxStackSize, PotionContents,
|
||||
StoredEnchantments, Tool, Unbreakable, UseCooldown, Weapon,
|
||||
};
|
||||
use crate::effect::{self, StatusEffect};
|
||||
use crate::entity_type::EntityType;
|
||||
@ -274,7 +274,7 @@ impl DataComponentImpl for ItemModelImpl {
|
||||
pub struct LoreImpl;
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
pub struct RarityImpl;
|
||||
#[derive(Clone, Hash, PartialEq, Eq)]
|
||||
#[derive(Clone, Hash, PartialEq, Eq, Default)]
|
||||
pub struct EnchantmentsImpl {
|
||||
pub enchantment: Cow<'static, [(&'static Enchantment, i32)]>,
|
||||
}
|
||||
@ -1377,7 +1377,12 @@ impl Hash for EntityTypeOrTag {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct EnchantableImpl;
|
||||
pub struct EnchantableImpl {
|
||||
pub value: i32,
|
||||
}
|
||||
impl DataComponentImpl for EnchantableImpl {
|
||||
default_impl!(Enchantable);
|
||||
}
|
||||
#[derive(Clone, Hash, PartialEq)]
|
||||
pub struct EquippableImpl {
|
||||
pub slot: &'static EquipmentSlot,
|
||||
|
||||
@ -18,6 +18,19 @@ pub struct Enchantment {
|
||||
pub exclusive_set: Option<&'static Tag>,
|
||||
pub max_level: i32,
|
||||
pub slots: &'static [AttributeModifierSlot],
|
||||
pub weight: i32,
|
||||
pub min_cost: Cost,
|
||||
pub max_cost: Cost,
|
||||
}
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Cost {
|
||||
pub base: i32,
|
||||
pub per_level_above_first: i32,
|
||||
}
|
||||
impl Cost {
|
||||
pub fn calculate(&self, level: i32) -> i32 {
|
||||
self.base + self.per_level_above_first * (level - 1)
|
||||
}
|
||||
}
|
||||
impl Taggable for Enchantment {
|
||||
#[inline]
|
||||
@ -59,6 +72,54 @@ pub enum AttributeModifierSlot {
|
||||
Saddle,
|
||||
}
|
||||
impl Enchantment {
|
||||
pub const ALL: &'static [&'static Self] = &[
|
||||
&Self::AQUA_AFFINITY,
|
||||
&Self::BANE_OF_ARTHROPODS,
|
||||
&Self::BINDING_CURSE,
|
||||
&Self::BLAST_PROTECTION,
|
||||
&Self::BREACH,
|
||||
&Self::CHANNELING,
|
||||
&Self::DENSITY,
|
||||
&Self::DEPTH_STRIDER,
|
||||
&Self::EFFICIENCY,
|
||||
&Self::FEATHER_FALLING,
|
||||
&Self::FIRE_ASPECT,
|
||||
&Self::FIRE_PROTECTION,
|
||||
&Self::FLAME,
|
||||
&Self::FORTUNE,
|
||||
&Self::FROST_WALKER,
|
||||
&Self::IMPALING,
|
||||
&Self::INFINITY,
|
||||
&Self::KNOCKBACK,
|
||||
&Self::LOOTING,
|
||||
&Self::LOYALTY,
|
||||
&Self::LUCK_OF_THE_SEA,
|
||||
&Self::LUNGE,
|
||||
&Self::LURE,
|
||||
&Self::MENDING,
|
||||
&Self::MULTISHOT,
|
||||
&Self::PIERCING,
|
||||
&Self::POWER,
|
||||
&Self::PROJECTILE_PROTECTION,
|
||||
&Self::PROTECTION,
|
||||
&Self::PUNCH,
|
||||
&Self::QUICK_CHARGE,
|
||||
&Self::RESPIRATION,
|
||||
&Self::RIPTIDE,
|
||||
&Self::SHARPNESS,
|
||||
&Self::SILK_TOUCH,
|
||||
&Self::SMITE,
|
||||
&Self::SOUL_SPEED,
|
||||
&Self::SWEEPING_EDGE,
|
||||
&Self::SWIFT_SNEAK,
|
||||
&Self::THORNS,
|
||||
&Self::UNBREAKING,
|
||||
&Self::VANISHING_CURSE,
|
||||
&Self::WIND_BURST,
|
||||
];
|
||||
pub fn all() -> Iter<'static, &'static Self> {
|
||||
Self::ALL.iter()
|
||||
}
|
||||
pub const AQUA_AFFINITY: Self = Self {
|
||||
id: 0u8,
|
||||
name: "minecraft:aqua_affinity",
|
||||
@ -69,6 +130,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 1i32,
|
||||
slots: &[AttributeModifierSlot::Head],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 1i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 41i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const BANE_OF_ARTHROPODS: Self = Self {
|
||||
id: 1u8,
|
||||
@ -80,6 +150,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_DAMAGE),
|
||||
max_level: 5i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 5i32,
|
||||
min_cost: Cost {
|
||||
base: 5i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
};
|
||||
pub const BINDING_CURSE: Self = Self {
|
||||
id: 2u8,
|
||||
@ -91,6 +170,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 1i32,
|
||||
slots: &[AttributeModifierSlot::Armor],
|
||||
weight: 1i32,
|
||||
min_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 50i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const BLAST_PROTECTION: Self = Self {
|
||||
id: 3u8,
|
||||
@ -102,6 +190,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_ARMOR),
|
||||
max_level: 4i32,
|
||||
slots: &[AttributeModifierSlot::Armor],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 5i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 13i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
};
|
||||
pub const BREACH: Self = Self {
|
||||
id: 4u8,
|
||||
@ -113,6 +210,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_DAMAGE),
|
||||
max_level: 4i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 15i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 65i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
};
|
||||
pub const CHANNELING: Self = Self {
|
||||
id: 5u8,
|
||||
@ -124,6 +230,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 1i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 1i32,
|
||||
min_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 50i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const DENSITY: Self = Self {
|
||||
id: 6u8,
|
||||
@ -135,6 +250,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_DAMAGE),
|
||||
max_level: 5i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 5i32,
|
||||
min_cost: Cost {
|
||||
base: 5i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
};
|
||||
pub const DEPTH_STRIDER: Self = Self {
|
||||
id: 7u8,
|
||||
@ -146,6 +270,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_BOOTS),
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::Feet],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 10i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
};
|
||||
pub const EFFICIENCY: Self = Self {
|
||||
id: 8u8,
|
||||
@ -157,6 +290,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 5i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 10i32,
|
||||
min_cost: Cost {
|
||||
base: 1i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 51i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
};
|
||||
pub const FEATHER_FALLING: Self = Self {
|
||||
id: 9u8,
|
||||
@ -168,6 +310,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 4i32,
|
||||
slots: &[AttributeModifierSlot::Armor],
|
||||
weight: 5i32,
|
||||
min_cost: Cost {
|
||||
base: 5i32,
|
||||
per_level_above_first: 6i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 11i32,
|
||||
per_level_above_first: 6i32,
|
||||
},
|
||||
};
|
||||
pub const FIRE_ASPECT: Self = Self {
|
||||
id: 10u8,
|
||||
@ -179,6 +330,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 2i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 10i32,
|
||||
per_level_above_first: 20i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 60i32,
|
||||
per_level_above_first: 20i32,
|
||||
},
|
||||
};
|
||||
pub const FIRE_PROTECTION: Self = Self {
|
||||
id: 11u8,
|
||||
@ -190,6 +350,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_ARMOR),
|
||||
max_level: 4i32,
|
||||
slots: &[AttributeModifierSlot::Armor],
|
||||
weight: 5i32,
|
||||
min_cost: Cost {
|
||||
base: 10i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 18i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
};
|
||||
pub const FLAME: Self = Self {
|
||||
id: 12u8,
|
||||
@ -201,6 +370,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 1i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 20i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 50i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const FORTUNE: Self = Self {
|
||||
id: 13u8,
|
||||
@ -212,6 +390,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_MINING),
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 15i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 65i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
};
|
||||
pub const FROST_WALKER: Self = Self {
|
||||
id: 14u8,
|
||||
@ -223,6 +410,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_BOOTS),
|
||||
max_level: 2i32,
|
||||
slots: &[AttributeModifierSlot::Feet],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 10i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
};
|
||||
pub const IMPALING: Self = Self {
|
||||
id: 15u8,
|
||||
@ -234,6 +430,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_DAMAGE),
|
||||
max_level: 5i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 1i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 21i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
};
|
||||
pub const INFINITY: Self = Self {
|
||||
id: 16u8,
|
||||
@ -245,6 +450,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_BOW),
|
||||
max_level: 1i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 1i32,
|
||||
min_cost: Cost {
|
||||
base: 20i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 50i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const KNOCKBACK: Self = Self {
|
||||
id: 17u8,
|
||||
@ -256,6 +470,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 2i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 5i32,
|
||||
min_cost: Cost {
|
||||
base: 5i32,
|
||||
per_level_above_first: 20i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 55i32,
|
||||
per_level_above_first: 20i32,
|
||||
},
|
||||
};
|
||||
pub const LOOTING: Self = Self {
|
||||
id: 18u8,
|
||||
@ -267,6 +490,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 15i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 65i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
};
|
||||
pub const LOYALTY: Self = Self {
|
||||
id: 19u8,
|
||||
@ -278,6 +510,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 5i32,
|
||||
min_cost: Cost {
|
||||
base: 12i32,
|
||||
per_level_above_first: 7i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 50i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const LUCK_OF_THE_SEA: Self = Self {
|
||||
id: 20u8,
|
||||
@ -289,6 +530,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 15i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 65i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
};
|
||||
pub const LUNGE: Self = Self {
|
||||
id: 21u8,
|
||||
@ -300,6 +550,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::Hand],
|
||||
weight: 5i32,
|
||||
min_cost: Cost {
|
||||
base: 5i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
};
|
||||
pub const LURE: Self = Self {
|
||||
id: 22u8,
|
||||
@ -311,6 +570,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 15i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 65i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
};
|
||||
pub const MENDING: Self = Self {
|
||||
id: 23u8,
|
||||
@ -322,6 +590,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 1i32,
|
||||
slots: &[AttributeModifierSlot::Any],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 25i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 75i32,
|
||||
per_level_above_first: 25i32,
|
||||
},
|
||||
};
|
||||
pub const MULTISHOT: Self = Self {
|
||||
id: 24u8,
|
||||
@ -333,6 +610,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_CROSSBOW),
|
||||
max_level: 1i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 20i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 50i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const PIERCING: Self = Self {
|
||||
id: 25u8,
|
||||
@ -344,6 +630,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_CROSSBOW),
|
||||
max_level: 4i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 10i32,
|
||||
min_cost: Cost {
|
||||
base: 1i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 50i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const POWER: Self = Self {
|
||||
id: 26u8,
|
||||
@ -355,6 +650,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 5i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 10i32,
|
||||
min_cost: Cost {
|
||||
base: 1i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 16i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
};
|
||||
pub const PROJECTILE_PROTECTION: Self = Self {
|
||||
id: 27u8,
|
||||
@ -366,6 +670,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_ARMOR),
|
||||
max_level: 4i32,
|
||||
slots: &[AttributeModifierSlot::Armor],
|
||||
weight: 5i32,
|
||||
min_cost: Cost {
|
||||
base: 3i32,
|
||||
per_level_above_first: 6i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 9i32,
|
||||
per_level_above_first: 6i32,
|
||||
},
|
||||
};
|
||||
pub const PROTECTION: Self = Self {
|
||||
id: 28u8,
|
||||
@ -377,6 +690,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_ARMOR),
|
||||
max_level: 4i32,
|
||||
slots: &[AttributeModifierSlot::Armor],
|
||||
weight: 10i32,
|
||||
min_cost: Cost {
|
||||
base: 1i32,
|
||||
per_level_above_first: 11i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 12i32,
|
||||
per_level_above_first: 11i32,
|
||||
},
|
||||
};
|
||||
pub const PUNCH: Self = Self {
|
||||
id: 29u8,
|
||||
@ -388,6 +710,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 2i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 12i32,
|
||||
per_level_above_first: 20i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 37i32,
|
||||
per_level_above_first: 20i32,
|
||||
},
|
||||
};
|
||||
pub const QUICK_CHARGE: Self = Self {
|
||||
id: 30u8,
|
||||
@ -402,6 +733,15 @@ impl Enchantment {
|
||||
AttributeModifierSlot::MainHand,
|
||||
AttributeModifierSlot::OffHand,
|
||||
],
|
||||
weight: 5i32,
|
||||
min_cost: Cost {
|
||||
base: 12i32,
|
||||
per_level_above_first: 20i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 50i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const RESPIRATION: Self = Self {
|
||||
id: 31u8,
|
||||
@ -413,6 +753,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::Head],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 10i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 40i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
};
|
||||
pub const RIPTIDE: Self = Self {
|
||||
id: 32u8,
|
||||
@ -424,6 +773,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_RIPTIDE),
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::Hand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 17i32,
|
||||
per_level_above_first: 7i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 50i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const SHARPNESS: Self = Self {
|
||||
id: 33u8,
|
||||
@ -435,6 +793,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_DAMAGE),
|
||||
max_level: 5i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 10i32,
|
||||
min_cost: Cost {
|
||||
base: 1i32,
|
||||
per_level_above_first: 11i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 21i32,
|
||||
per_level_above_first: 11i32,
|
||||
},
|
||||
};
|
||||
pub const SILK_TOUCH: Self = Self {
|
||||
id: 34u8,
|
||||
@ -446,6 +813,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_MINING),
|
||||
max_level: 1i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 1i32,
|
||||
min_cost: Cost {
|
||||
base: 15i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 65i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const SMITE: Self = Self {
|
||||
id: 35u8,
|
||||
@ -457,6 +833,15 @@ impl Enchantment {
|
||||
exclusive_set: Some(&EnchantmentTag::MINECRAFT_EXCLUSIVE_SET_DAMAGE),
|
||||
max_level: 5i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 5i32,
|
||||
min_cost: Cost {
|
||||
base: 5i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
};
|
||||
pub const SOUL_SPEED: Self = Self {
|
||||
id: 36u8,
|
||||
@ -468,6 +853,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::Feet],
|
||||
weight: 1i32,
|
||||
min_cost: Cost {
|
||||
base: 10i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 10i32,
|
||||
},
|
||||
};
|
||||
pub const SWEEPING_EDGE: Self = Self {
|
||||
id: 37u8,
|
||||
@ -479,6 +873,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 5i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 20i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
};
|
||||
pub const SWIFT_SNEAK: Self = Self {
|
||||
id: 38u8,
|
||||
@ -490,6 +893,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::Legs],
|
||||
weight: 1i32,
|
||||
min_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 25i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 75i32,
|
||||
per_level_above_first: 25i32,
|
||||
},
|
||||
};
|
||||
pub const THORNS: Self = Self {
|
||||
id: 39u8,
|
||||
@ -501,6 +913,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::Any],
|
||||
weight: 1i32,
|
||||
min_cost: Cost {
|
||||
base: 10i32,
|
||||
per_level_above_first: 20i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 60i32,
|
||||
per_level_above_first: 20i32,
|
||||
},
|
||||
};
|
||||
pub const UNBREAKING: Self = Self {
|
||||
id: 40u8,
|
||||
@ -512,6 +933,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::Any],
|
||||
weight: 5i32,
|
||||
min_cost: Cost {
|
||||
base: 5i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 55i32,
|
||||
per_level_above_first: 8i32,
|
||||
},
|
||||
};
|
||||
pub const VANISHING_CURSE: Self = Self {
|
||||
id: 41u8,
|
||||
@ -523,6 +953,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 1i32,
|
||||
slots: &[AttributeModifierSlot::Any],
|
||||
weight: 1i32,
|
||||
min_cost: Cost {
|
||||
base: 25i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 50i32,
|
||||
per_level_above_first: 0i32,
|
||||
},
|
||||
};
|
||||
pub const WIND_BURST: Self = Self {
|
||||
id: 42u8,
|
||||
@ -534,6 +973,15 @@ impl Enchantment {
|
||||
exclusive_set: None,
|
||||
max_level: 3i32,
|
||||
slots: &[AttributeModifierSlot::MainHand],
|
||||
weight: 2i32,
|
||||
min_cost: Cost {
|
||||
base: 15i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
max_cost: Cost {
|
||||
base: 65i32,
|
||||
per_level_above_first: 9i32,
|
||||
},
|
||||
};
|
||||
pub fn from_name(name: &str) -> Option<&'static Self> {
|
||||
match name {
|
||||
|
||||
@ -2949,6 +2949,7 @@ impl Item {
|
||||
attribute_modifiers: Cow::Borrowed(&[]),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 1 }),
|
||||
],
|
||||
};
|
||||
pub const BOOKSHELF: Item = Item {
|
||||
@ -3008,6 +3009,7 @@ impl Item {
|
||||
attribute_modifiers: Cow::Borrowed(&[]),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 1 }),
|
||||
],
|
||||
};
|
||||
pub const BOWL: Item = Item {
|
||||
@ -4259,6 +4261,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 12 }),
|
||||
],
|
||||
};
|
||||
pub const CHAINMAIL_CHESTPLATE: Item = Item {
|
||||
@ -4311,6 +4314,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 12 }),
|
||||
],
|
||||
};
|
||||
pub const CHAINMAIL_HELMET: Item = Item {
|
||||
@ -4363,6 +4367,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 12 }),
|
||||
],
|
||||
};
|
||||
pub const CHAINMAIL_LEGGINGS: Item = Item {
|
||||
@ -4415,6 +4420,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 12 }),
|
||||
],
|
||||
};
|
||||
pub const CHARCOAL: Item = Item {
|
||||
@ -6118,6 +6124,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 13 }),
|
||||
],
|
||||
};
|
||||
pub const COPPER_BARS: Item = Item {
|
||||
@ -6208,6 +6215,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 8 }),
|
||||
],
|
||||
};
|
||||
pub const COPPER_BULB: Item = Item {
|
||||
@ -6317,6 +6325,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 8 }),
|
||||
],
|
||||
};
|
||||
pub const COPPER_DOOR: Item = Item {
|
||||
@ -6445,6 +6454,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 8 }),
|
||||
],
|
||||
};
|
||||
pub const COPPER_HOE: Item = Item {
|
||||
@ -6507,6 +6517,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 13 }),
|
||||
],
|
||||
};
|
||||
pub const COPPER_HORSE_ARMOR: Item = Item {
|
||||
@ -6647,6 +6658,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 8 }),
|
||||
],
|
||||
};
|
||||
pub const COPPER_NAUTILUS_ARMOR: Item = Item {
|
||||
@ -6797,6 +6809,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 13 }),
|
||||
],
|
||||
};
|
||||
pub const COPPER_SHOVEL: Item = Item {
|
||||
@ -6859,6 +6872,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 13 }),
|
||||
],
|
||||
};
|
||||
pub const COPPER_SPEAR: Item = Item {
|
||||
@ -6901,6 +6915,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 13 }),
|
||||
],
|
||||
};
|
||||
pub const COPPER_SWORD: Item = Item {
|
||||
@ -6968,6 +6983,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 13 }),
|
||||
],
|
||||
};
|
||||
pub const COPPER_TORCH: Item = Item {
|
||||
@ -7638,6 +7654,7 @@ impl Item {
|
||||
attribute_modifiers: Cow::Borrowed(&[]),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 1 }),
|
||||
],
|
||||
};
|
||||
pub const CRYING_OBSIDIAN: Item = Item {
|
||||
@ -9350,6 +9367,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 10 }),
|
||||
],
|
||||
};
|
||||
pub const DIAMOND_BLOCK: Item = Item {
|
||||
@ -9421,6 +9439,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 10 }),
|
||||
],
|
||||
};
|
||||
pub const DIAMOND_CHESTPLATE: Item = Item {
|
||||
@ -9473,6 +9492,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 10 }),
|
||||
],
|
||||
};
|
||||
pub const DIAMOND_HELMET: Item = Item {
|
||||
@ -9525,6 +9545,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 10 }),
|
||||
],
|
||||
};
|
||||
pub const DIAMOND_HOE: Item = Item {
|
||||
@ -9587,6 +9608,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 10 }),
|
||||
],
|
||||
};
|
||||
pub const DIAMOND_HORSE_ARMOR: Item = Item {
|
||||
@ -9689,6 +9711,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 10 }),
|
||||
],
|
||||
};
|
||||
pub const DIAMOND_NAUTILUS_ARMOR: Item = Item {
|
||||
@ -9820,6 +9843,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 10 }),
|
||||
],
|
||||
};
|
||||
pub const DIAMOND_SHOVEL: Item = Item {
|
||||
@ -9882,6 +9906,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 10 }),
|
||||
],
|
||||
};
|
||||
pub const DIAMOND_SPEAR: Item = Item {
|
||||
@ -9924,6 +9949,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 10 }),
|
||||
],
|
||||
};
|
||||
pub const DIAMOND_SWORD: Item = Item {
|
||||
@ -9991,6 +10017,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 10 }),
|
||||
],
|
||||
};
|
||||
pub const DIORITE: Item = Item {
|
||||
@ -11568,6 +11595,7 @@ impl Item {
|
||||
attribute_modifiers: Cow::Borrowed(&[]),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 1 }),
|
||||
],
|
||||
};
|
||||
pub const FLETCHING_TABLE: Item = Item {
|
||||
@ -12410,6 +12438,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 22 }),
|
||||
],
|
||||
};
|
||||
pub const GOLDEN_BOOTS: Item = Item {
|
||||
@ -12462,6 +12491,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 25 }),
|
||||
],
|
||||
};
|
||||
pub const GOLDEN_CARROT: Item = Item {
|
||||
@ -12551,6 +12581,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 25 }),
|
||||
],
|
||||
};
|
||||
pub const GOLDEN_DANDELION: Item = Item {
|
||||
@ -12622,6 +12653,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 25 }),
|
||||
],
|
||||
};
|
||||
pub const GOLDEN_HOE: Item = Item {
|
||||
@ -12684,6 +12716,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 22 }),
|
||||
],
|
||||
};
|
||||
pub const GOLDEN_HORSE_ARMOR: Item = Item {
|
||||
@ -12786,6 +12819,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 25 }),
|
||||
],
|
||||
};
|
||||
pub const GOLDEN_NAUTILUS_ARMOR: Item = Item {
|
||||
@ -12898,6 +12932,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 22 }),
|
||||
],
|
||||
};
|
||||
pub const GOLDEN_SHOVEL: Item = Item {
|
||||
@ -12960,6 +12995,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 22 }),
|
||||
],
|
||||
};
|
||||
pub const GOLDEN_SPEAR: Item = Item {
|
||||
@ -13002,6 +13038,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 22 }),
|
||||
],
|
||||
};
|
||||
pub const GOLDEN_SWORD: Item = Item {
|
||||
@ -13069,6 +13106,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 22 }),
|
||||
],
|
||||
};
|
||||
pub const GRANITE: Item = Item {
|
||||
@ -14589,6 +14627,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 14 }),
|
||||
],
|
||||
};
|
||||
pub const IRON_BARS: Item = Item {
|
||||
@ -14679,6 +14718,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 9 }),
|
||||
],
|
||||
};
|
||||
pub const IRON_CHAIN: Item = Item {
|
||||
@ -14750,6 +14790,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 9 }),
|
||||
],
|
||||
};
|
||||
pub const IRON_DOOR: Item = Item {
|
||||
@ -14840,6 +14881,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 9 }),
|
||||
],
|
||||
};
|
||||
pub const IRON_HOE: Item = Item {
|
||||
@ -14902,6 +14944,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 14 }),
|
||||
],
|
||||
};
|
||||
pub const IRON_HORSE_ARMOR: Item = Item {
|
||||
@ -15023,6 +15066,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 9 }),
|
||||
],
|
||||
};
|
||||
pub const IRON_NAUTILUS_ARMOR: Item = Item {
|
||||
@ -15173,6 +15217,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 14 }),
|
||||
],
|
||||
};
|
||||
pub const IRON_SHOVEL: Item = Item {
|
||||
@ -15235,6 +15280,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 14 }),
|
||||
],
|
||||
};
|
||||
pub const IRON_SPEAR: Item = Item {
|
||||
@ -15277,6 +15323,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 14 }),
|
||||
],
|
||||
};
|
||||
pub const IRON_SWORD: Item = Item {
|
||||
@ -15344,6 +15391,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 14 }),
|
||||
],
|
||||
};
|
||||
pub const IRON_TRAPDOOR: Item = Item {
|
||||
@ -16080,6 +16128,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const LEATHER_CHESTPLATE: Item = Item {
|
||||
@ -16132,6 +16181,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const LEATHER_HELMET: Item = Item {
|
||||
@ -16184,6 +16234,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const LEATHER_HORSE_ARMOR: Item = Item {
|
||||
@ -16286,6 +16337,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const LECTERN: Item = Item {
|
||||
@ -17525,6 +17577,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const MAGENTA_BANNER: Item = Item {
|
||||
@ -19819,6 +19872,7 @@ impl Item {
|
||||
res_type: DamageResistantType::Fire,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const NETHERITE_BLOCK: Item = Item {
|
||||
@ -19909,6 +19963,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const NETHERITE_CHESTPLATE: Item = Item {
|
||||
@ -19974,6 +20029,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const NETHERITE_HELMET: Item = Item {
|
||||
@ -20039,6 +20095,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const NETHERITE_HOE: Item = Item {
|
||||
@ -20107,6 +20164,7 @@ impl Item {
|
||||
res_type: DamageResistantType::Fire,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const NETHERITE_HORSE_ARMOR: Item = Item {
|
||||
@ -20260,6 +20318,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const NETHERITE_NAUTILUS_ARMOR: Item = Item {
|
||||
@ -20391,6 +20450,7 @@ impl Item {
|
||||
res_type: DamageResistantType::Fire,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const NETHERITE_SCRAP: Item = Item {
|
||||
@ -20484,6 +20544,7 @@ impl Item {
|
||||
res_type: DamageResistantType::Fire,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const NETHERITE_SPEAR: Item = Item {
|
||||
@ -20532,6 +20593,7 @@ impl Item {
|
||||
res_type: DamageResistantType::Fire,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const NETHERITE_SWORD: Item = Item {
|
||||
@ -20605,6 +20667,7 @@ impl Item {
|
||||
res_type: DamageResistantType::Fire,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const NETHERITE_UPGRADE_SMITHING_TEMPLATE: Item = Item {
|
||||
@ -28151,6 +28214,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 5 }),
|
||||
],
|
||||
};
|
||||
pub const STONE_BRICK_SLAB: Item = Item {
|
||||
@ -28308,6 +28372,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 5 }),
|
||||
],
|
||||
};
|
||||
pub const STONE_PICKAXE: Item = Item {
|
||||
@ -28370,6 +28435,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 5 }),
|
||||
],
|
||||
};
|
||||
pub const STONE_PRESSURE_PLATE: Item = Item {
|
||||
@ -28451,6 +28517,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 5 }),
|
||||
],
|
||||
};
|
||||
pub const STONE_SLAB: Item = Item {
|
||||
@ -28512,6 +28579,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 5 }),
|
||||
],
|
||||
};
|
||||
pub const STONE_STAIRS: Item = Item {
|
||||
@ -28598,6 +28666,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 5 }),
|
||||
],
|
||||
};
|
||||
pub const STONECUTTER: Item = Item {
|
||||
@ -29769,6 +29838,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 1 }),
|
||||
],
|
||||
};
|
||||
pub const TRIPWIRE_HOOK: Item = Item {
|
||||
@ -30151,6 +30221,7 @@ impl Item {
|
||||
shearing_sound: IdOr::Id(Sound::ItemShearsSnip),
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 9 }),
|
||||
],
|
||||
};
|
||||
pub const TURTLE_SCUTE: Item = Item {
|
||||
@ -32948,6 +33019,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const WOODEN_HOE: Item = Item {
|
||||
@ -33010,6 +33082,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const WOODEN_PICKAXE: Item = Item {
|
||||
@ -33072,6 +33145,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const WOODEN_SHOVEL: Item = Item {
|
||||
@ -33134,6 +33208,7 @@ impl Item {
|
||||
item_damage_per_attack: 2,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const WOODEN_SPEAR: Item = Item {
|
||||
@ -33176,6 +33251,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const WOODEN_SWORD: Item = Item {
|
||||
@ -33243,6 +33319,7 @@ impl Item {
|
||||
item_damage_per_attack: 1,
|
||||
},
|
||||
),
|
||||
(Enchantable, &EnchantableImpl { value: 15 }),
|
||||
],
|
||||
};
|
||||
pub const WRITABLE_BOOK: Item = Item {
|
||||
|
||||
@ -95,17 +95,54 @@ impl ItemStack {
|
||||
None
|
||||
}
|
||||
pub fn get_data_component_mut<T: DataComponentImpl + 'static>(&mut self) -> Option<&mut T> {
|
||||
let to_get_id = &T::get_enum();
|
||||
for (id, component) in &mut self.patch {
|
||||
if id == to_get_id {
|
||||
return component
|
||||
let to_get_id = T::get_enum();
|
||||
if let Some(index) = self.patch.iter().position(|(id, _)| *id == to_get_id) {
|
||||
return self.patch[index]
|
||||
.1
|
||||
.as_mut()
|
||||
.map(|component| get_mut::<T>(component.as_mut()));
|
||||
}
|
||||
|
||||
// If not in patch, clone from item to patch and return mut
|
||||
let mut cloned = None;
|
||||
for (id, component) in self.item.components {
|
||||
if *id == to_get_id {
|
||||
cloned = Some((*id, Some(component.clone_dyn())));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some((id, component)) = cloned {
|
||||
self.patch.push((id, component));
|
||||
return self
|
||||
.patch
|
||||
.last_mut()
|
||||
.unwrap()
|
||||
.1
|
||||
.as_mut()
|
||||
.map(|c| get_mut::<T>(c.as_mut()));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn has_enchantments(&self) -> bool {
|
||||
self.get_data_component::<EnchantmentsImpl>()
|
||||
.is_some_and(|e| !e.enchantment.is_empty())
|
||||
}
|
||||
|
||||
pub fn add_enchantment(&mut self, enchantment: &'static Enchantment, level: u16) {
|
||||
if let Some(enchantments) = self.get_data_component_mut::<EnchantmentsImpl>() {
|
||||
let mut new_vec = enchantments.enchantment.to_vec();
|
||||
new_vec.push((enchantment, level as i32));
|
||||
enchantments.enchantment = Cow::Owned(new_vec);
|
||||
} else {
|
||||
let enchantments = EnchantmentsImpl {
|
||||
enchantment: Cow::Owned(vec![(enchantment, level as i32)]),
|
||||
};
|
||||
self.patch
|
||||
.push((DataComponent::Enchantments, Some(Box::new(enchantments))));
|
||||
}
|
||||
}
|
||||
|
||||
pub const EMPTY: &'static Self = &Self {
|
||||
item_count: 0,
|
||||
item: &Item::AIR,
|
||||
|
||||
@ -12,6 +12,7 @@ pumpkin-data.workspace = true
|
||||
pumpkin-world.workspace = true
|
||||
pumpkin-util.workspace = true
|
||||
|
||||
rand.workspace = true
|
||||
tracing.workspace = true
|
||||
tokio.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::{
|
||||
player::player_inventory::PlayerInventory,
|
||||
screen_handler::{
|
||||
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
|
||||
ScreenHandlerFuture,
|
||||
ScreenHandlerFuture, offer_or_drop_stack,
|
||||
},
|
||||
slot::NormalSlot,
|
||||
window_property::{Anvil, WindowProperty},
|
||||
@ -121,7 +121,7 @@ impl ScreenHandler for AnvilScreenHandler {
|
||||
for i in 0..2 {
|
||||
let stack = self.inventory.remove_stack(i).await;
|
||||
if !stack.is_empty() {
|
||||
player.drop_item(stack, false).await;
|
||||
offer_or_drop_stack(player, stack).await;
|
||||
}
|
||||
}
|
||||
self.inventory.set_stack(2, ItemStack::EMPTY.clone()).await;
|
||||
|
||||
437
pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs
Normal file
437
pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs
Normal file
@ -0,0 +1,437 @@
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
use pumpkin_data::Enchantment;
|
||||
use pumpkin_data::data_component_impl::EnchantableImpl;
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::item_stack::ItemStack;
|
||||
use pumpkin_data::screen::WindowType;
|
||||
use pumpkin_data::tag::{Enchantment as EnchantmentTag, Taggable};
|
||||
use pumpkin_util::random::RandomImpl;
|
||||
use pumpkin_world::inventory::Inventory;
|
||||
|
||||
use crate::{
|
||||
player::player_inventory::PlayerInventory,
|
||||
screen_handler::{
|
||||
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
|
||||
ScreenHandlerFuture, offer_or_drop_stack,
|
||||
},
|
||||
slot::NormalSlot,
|
||||
window_property::{EnchantmentTable, WindowProperty},
|
||||
};
|
||||
|
||||
pub struct EnchantingTableScreenHandler {
|
||||
pub inventory: Arc<dyn Inventory>,
|
||||
behaviour: ScreenHandlerBehaviour,
|
||||
pub level_requirements: [i32; 3],
|
||||
pub enchantment_id: [i32; 3],
|
||||
pub enchantment_level: [i32; 3],
|
||||
pub enchantment_seed: i32,
|
||||
pub bookshelf_count: i32,
|
||||
}
|
||||
|
||||
impl EnchantingTableScreenHandler {
|
||||
pub fn new(
|
||||
sync_id: u8,
|
||||
player_inventory: &Arc<PlayerInventory>,
|
||||
inventory: &Arc<dyn Inventory>,
|
||||
enchantment_seed: i32,
|
||||
bookshelf_count: i32,
|
||||
) -> Self {
|
||||
let mut handler = Self {
|
||||
inventory: inventory.clone(),
|
||||
behaviour: ScreenHandlerBehaviour::new(sync_id, Some(WindowType::Enchantment)),
|
||||
level_requirements: [0; 3],
|
||||
enchantment_id: [-1; 3],
|
||||
enchantment_level: [-1; 3],
|
||||
enchantment_seed,
|
||||
bookshelf_count,
|
||||
};
|
||||
|
||||
// Enchanting slots: 0 is item, 1 is lapis
|
||||
handler.add_slot(Arc::new(NormalSlot::new(inventory.clone(), 0)));
|
||||
handler.add_slot(Arc::new(NormalSlot::new(inventory.clone(), 1)));
|
||||
|
||||
let player_inventory: Arc<dyn Inventory> = player_inventory.clone();
|
||||
handler.add_player_slots(&player_inventory);
|
||||
|
||||
handler
|
||||
}
|
||||
|
||||
pub async fn update_enchantments(&mut self, _player: &dyn InventoryPlayer) {
|
||||
let item = self.inventory.get_stack(0).await;
|
||||
let item = item.lock().await;
|
||||
|
||||
if item.is_empty() || item.has_enchantments() {
|
||||
for i in 0..3 {
|
||||
self.level_requirements[i] = 0;
|
||||
self.enchantment_id[i] = -1;
|
||||
self.enchantment_level[i] = -1;
|
||||
}
|
||||
} else {
|
||||
let enchantability = item
|
||||
.get_data_component::<EnchantableImpl>()
|
||||
.map_or(0, |e| e.value);
|
||||
|
||||
if enchantability <= 0 {
|
||||
for i in 0..3 {
|
||||
self.level_requirements[i] = 0;
|
||||
self.enchantment_id[i] = -1;
|
||||
self.enchantment_level[i] = -1;
|
||||
}
|
||||
} else {
|
||||
let mut random = pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(
|
||||
self.enchantment_seed as u64,
|
||||
);
|
||||
|
||||
for i in 0..3 {
|
||||
let level = self.calculate_level_requirement(&mut random, i, enchantability);
|
||||
self.level_requirements[i] = level;
|
||||
}
|
||||
|
||||
for i in 0..3 {
|
||||
if self.level_requirements[i] > 0 {
|
||||
let enchantments = Self::get_enchantment_list(
|
||||
&mut random,
|
||||
&item,
|
||||
i,
|
||||
self.level_requirements[i],
|
||||
);
|
||||
if let Some(first) = enchantments.first() {
|
||||
self.enchantment_id[i] = first.0.id as i32;
|
||||
self.enchantment_level[i] = first.1;
|
||||
} else {
|
||||
self.enchantment_id[i] = -1;
|
||||
self.enchantment_level[i] = -1;
|
||||
}
|
||||
} else {
|
||||
self.enchantment_id[i] = -1;
|
||||
self.enchantment_level[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.send_property_updates().await;
|
||||
}
|
||||
|
||||
fn calculate_level_requirement(
|
||||
&self,
|
||||
random: &mut pumpkin_util::random::xoroshiro128::Xoroshiro,
|
||||
slot: usize,
|
||||
_enchantability: i32,
|
||||
) -> i32 {
|
||||
let b = self.bookshelf_count;
|
||||
let level = random.next_bounded_i32(8) + 1 + (b >> 1) + random.next_bounded_i32(b + 1);
|
||||
|
||||
match slot {
|
||||
0 => (level / 3).max(1),
|
||||
1 => (level * 2 / 3 + 7).max(1),
|
||||
2 => level.max(b * 2).max(1),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_enchantment_list(
|
||||
random: &mut pumpkin_util::random::xoroshiro128::Xoroshiro,
|
||||
item: &ItemStack,
|
||||
_slot: usize,
|
||||
level: i32,
|
||||
) -> Vec<(&'static Enchantment, i32)> {
|
||||
let enchantability = item
|
||||
.get_data_component::<EnchantableImpl>()
|
||||
.map_or(0, |e| e.value);
|
||||
let mut enchant_level = level
|
||||
+ 1
|
||||
+ random.next_bounded_i32(enchantability / 4 + 1)
|
||||
+ random.next_bounded_i32(enchantability / 4 + 1);
|
||||
let bonus = (random.next_f32() + random.next_f32() - 1.0) * 0.15;
|
||||
enchant_level = (enchant_level as f32 * (1.0 + bonus)).round() as i32;
|
||||
enchant_level = enchant_level.max(1);
|
||||
|
||||
let mut available = Vec::new();
|
||||
for enchant in Enchantment::all() {
|
||||
if enchant.has_tag(&EnchantmentTag::MINECRAFT_IN_ENCHANTING_TABLE)
|
||||
&& enchant.can_enchant(item.item)
|
||||
{
|
||||
for l in (1..=enchant.max_level).rev() {
|
||||
if enchant_level >= enchant.min_cost.calculate(l)
|
||||
&& enchant_level <= enchant.max_cost.calculate(l)
|
||||
{
|
||||
available.push((*enchant, l));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if available.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let total_weight: i32 = available.iter().map(|(e, _)| e.weight).sum();
|
||||
if total_weight <= 0 {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let mut weight = random.next_bounded_i32(total_weight);
|
||||
let mut selected = None;
|
||||
for (e, l) in &available {
|
||||
weight -= e.weight;
|
||||
if weight < 0 {
|
||||
selected = Some((*e, *l));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut result = Vec::new();
|
||||
if let Some(s) = selected {
|
||||
result.push(s);
|
||||
|
||||
// Add more?
|
||||
let mut current_level = enchant_level;
|
||||
while random.next_bounded_i32(50) <= (current_level + 1) / 2 {
|
||||
available.retain(|(e, _)| {
|
||||
for (se, _) in &result {
|
||||
if !e.are_compatible(se) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
|
||||
if available.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
let total_weight: i32 = available.iter().map(|(e, _)| e.weight).sum();
|
||||
let mut weight = random.next_bounded_i32(total_weight);
|
||||
for (e, l) in &available {
|
||||
weight -= e.weight;
|
||||
if weight < 0 {
|
||||
result.push((*e, *l));
|
||||
break;
|
||||
}
|
||||
}
|
||||
current_level /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
async fn send_property_updates(&self) {
|
||||
if let Some(sync_handler) = self.behaviour.sync_handler.as_ref() {
|
||||
for i in 0..3 {
|
||||
let (id, val) = WindowProperty::new(
|
||||
EnchantmentTable::LevelRequirement { slot: i as u8 },
|
||||
self.level_requirements[i] as i16,
|
||||
)
|
||||
.into_tuple();
|
||||
sync_handler
|
||||
.update_property(&self.behaviour, id as i32, val as i32)
|
||||
.await;
|
||||
|
||||
let (id, val) = WindowProperty::new(
|
||||
EnchantmentTable::EnchantmentId { slot: i as u8 },
|
||||
self.enchantment_id[i] as i16,
|
||||
)
|
||||
.into_tuple();
|
||||
sync_handler
|
||||
.update_property(&self.behaviour, id as i32, val as i32)
|
||||
.await;
|
||||
|
||||
let (id, val) = WindowProperty::new(
|
||||
EnchantmentTable::EnchantmentLevel { slot: i as u8 },
|
||||
self.enchantment_level[i] as i16,
|
||||
)
|
||||
.into_tuple();
|
||||
sync_handler
|
||||
.update_property(&self.behaviour, id as i32, val as i32)
|
||||
.await;
|
||||
}
|
||||
|
||||
let (id, val) = WindowProperty::new(
|
||||
EnchantmentTable::EnchantmentSeed,
|
||||
(self.enchantment_seed & 0xFFFF) as i16,
|
||||
)
|
||||
.into_tuple();
|
||||
sync_handler
|
||||
.update_property(&self.behaviour, id as i32, val as i32)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScreenHandler for EnchantingTableScreenHandler {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn get_behaviour(&self) -> &ScreenHandlerBehaviour {
|
||||
&self.behaviour
|
||||
}
|
||||
|
||||
fn get_behaviour_mut(&mut self) -> &mut ScreenHandlerBehaviour {
|
||||
&mut self.behaviour
|
||||
}
|
||||
|
||||
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
self.default_on_closed(player).await;
|
||||
self.inventory.on_close().await;
|
||||
// Return items to player
|
||||
for i in 0..2 {
|
||||
let stack = self.inventory.remove_stack(i).await;
|
||||
if !stack.is_empty() {
|
||||
offer_or_drop_stack(player, stack).await;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn on_button_click<'a>(
|
||||
&'a mut self,
|
||||
player: &'a dyn InventoryPlayer,
|
||||
id: i32,
|
||||
) -> ScreenHandlerFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
if !(0..3).contains(&id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let level_req = self.level_requirements[id as usize];
|
||||
if player.experience_level() < level_req && !player.is_creative() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let lapis_slot = self.inventory.get_stack(1).await;
|
||||
let mut lapis_stack = lapis_slot.lock().await;
|
||||
let lapis_cost = (id + 1) as u8;
|
||||
|
||||
if !player.is_creative()
|
||||
&& (lapis_stack.is_empty() || lapis_stack.item_count < lapis_cost)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Perform enchantment
|
||||
let item_slot = self.inventory.get_stack(0).await;
|
||||
let mut item_stack = item_slot.lock().await;
|
||||
|
||||
if item_stack.is_empty() || item_stack.has_enchantments() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut random = pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(
|
||||
self.enchantment_seed as u64,
|
||||
);
|
||||
let enchantments =
|
||||
Self::get_enchantment_list(&mut random, &item_stack, id as usize, level_req);
|
||||
|
||||
if enchantments.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !player.is_creative() {
|
||||
player.add_experience_levels(-(id + 1)).await;
|
||||
lapis_stack.decrement(lapis_cost);
|
||||
}
|
||||
|
||||
for (enchant, level) in enchantments {
|
||||
item_stack.add_enchantment(enchant, level as u16);
|
||||
}
|
||||
|
||||
// CRITICAL FIX: Drop locks *before* calling `update_enchantments`
|
||||
// Otherwise, update_enchantments will try to lock slot 0 again and deadlock!
|
||||
drop(item_stack);
|
||||
drop(lapis_stack);
|
||||
|
||||
// Update seed
|
||||
player.set_enchantment_seed(rand::random()).await;
|
||||
self.enchantment_seed = player.enchantment_seed();
|
||||
|
||||
self.update_enchantments(player).await;
|
||||
self.send_content_updates().await;
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
fn quick_move<'a>(
|
||||
&'a mut self,
|
||||
player: &'a dyn InventoryPlayer, // FIX: Changed _player to player
|
||||
slot_index: i32,
|
||||
) -> ItemStackFuture<'a> {
|
||||
Box::pin(async move {
|
||||
let mut stack_left = ItemStack::EMPTY.clone();
|
||||
let slot = self.get_behaviour().slots[slot_index as usize].clone();
|
||||
|
||||
if slot.has_stack().await {
|
||||
let slot_stack_lock = slot.get_stack().await;
|
||||
let slot_stack_guard = slot_stack_lock.lock().await;
|
||||
stack_left = slot_stack_guard.clone();
|
||||
drop(slot_stack_guard);
|
||||
|
||||
let mut slot_stack_mut = slot_stack_lock.lock().await;
|
||||
|
||||
if slot_index < 2 {
|
||||
// From enchanting to player
|
||||
if !self
|
||||
.insert_item(
|
||||
&mut slot_stack_mut,
|
||||
2,
|
||||
self.get_behaviour().slots.len() as i32,
|
||||
true,
|
||||
)
|
||||
.await
|
||||
{
|
||||
return ItemStack::EMPTY.clone();
|
||||
}
|
||||
} else {
|
||||
// From player to enchanting
|
||||
// Lapis check
|
||||
if slot_stack_mut.item == &Item::LAPIS_LAZULI {
|
||||
if !self.insert_item(&mut slot_stack_mut, 1, 2, false).await {
|
||||
return ItemStack::EMPTY.clone();
|
||||
}
|
||||
} else if !self.insert_item(&mut slot_stack_mut, 0, 1, false).await {
|
||||
return ItemStack::EMPTY.clone();
|
||||
}
|
||||
}
|
||||
|
||||
if slot_stack_mut.is_empty() {
|
||||
drop(slot_stack_mut);
|
||||
slot.set_stack(ItemStack::EMPTY.clone()).await;
|
||||
} else {
|
||||
drop(slot_stack_mut);
|
||||
slot.mark_dirty().await;
|
||||
}
|
||||
|
||||
// CRITICAL FIX: Ensure the client is notified when shift-clicking items into the slots
|
||||
self.update_enchantments(player).await;
|
||||
}
|
||||
|
||||
stack_left
|
||||
})
|
||||
}
|
||||
|
||||
fn on_slot_click<'a>(
|
||||
&'a mut self,
|
||||
slot_index: i32,
|
||||
button: i32,
|
||||
action_type: pumpkin_protocol::java::server::play::SlotActionType,
|
||||
player: &'a dyn InventoryPlayer,
|
||||
) -> ScreenHandlerFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
self.internal_on_slot_click(slot_index, button, action_type, player)
|
||||
.await;
|
||||
if slot_index == 0 || slot_index == 1 {
|
||||
self.update_enchantments(player).await;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
6
pumpkin-inventory/src/enchanting/mod.rs
Normal file
6
pumpkin-inventory/src/enchanting/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
//! Enchanting module.
|
||||
//!
|
||||
//! This module handles enchanting mechanics including:
|
||||
//! - [`EnchantingTableScreenHandler`] - Screen handler for enchanting tables
|
||||
|
||||
pub mod enchanting_screen_handler;
|
||||
@ -36,6 +36,7 @@ pub mod container_click;
|
||||
pub mod crafting;
|
||||
pub mod double;
|
||||
pub mod drag_handler;
|
||||
pub mod enchanting;
|
||||
pub mod entity_equipment;
|
||||
mod error;
|
||||
pub mod furnace_like;
|
||||
|
||||
@ -8,7 +8,7 @@ use crate::{
|
||||
player::player_inventory::PlayerInventory,
|
||||
screen_handler::{
|
||||
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
|
||||
ScreenHandlerFuture,
|
||||
ScreenHandlerFuture, offer_or_drop_stack,
|
||||
},
|
||||
slot::NormalSlot,
|
||||
};
|
||||
@ -110,7 +110,7 @@ impl ScreenHandler for MerchantScreenHandler {
|
||||
// Drop inputs only, output is virtual/ghost in some sense or just cleared
|
||||
let stack = self.inventory.remove_stack(i).await;
|
||||
if !stack.is_empty() {
|
||||
player.drop_item(stack, false).await;
|
||||
offer_or_drop_stack(player, stack).await;
|
||||
}
|
||||
}
|
||||
// Clear output slot
|
||||
|
||||
@ -142,6 +142,12 @@ pub trait InventoryPlayer: Send + Sync {
|
||||
/// Adds or removes experience levels.
|
||||
fn add_experience_levels(&self, levels: i32) -> PlayerFuture<'_, ()>;
|
||||
|
||||
/// Gets the player's enchantment seed.
|
||||
fn enchantment_seed(&self) -> i32;
|
||||
|
||||
/// Sets the player's enchantment seed.
|
||||
fn set_enchantment_seed(&self, seed: i32) -> PlayerFuture<'_, ()>;
|
||||
|
||||
/// Sends a full container content packet.
|
||||
fn enqueue_inventory_packet<'a>(
|
||||
&'a self,
|
||||
@ -673,6 +679,15 @@ pub trait ScreenHandler: Send + Sync {
|
||||
slot_index: i32,
|
||||
) -> ItemStackFuture<'a>;
|
||||
|
||||
/// Handles a button click event (e.g., enchantment selection, beacon effects).
|
||||
fn on_button_click<'a>(
|
||||
&'a mut self,
|
||||
_player: &'a dyn InventoryPlayer,
|
||||
_button_id: i32,
|
||||
) -> ScreenHandlerFuture<'a, bool> {
|
||||
Box::pin(async { false })
|
||||
}
|
||||
|
||||
/// Inserts an item into a range of slots.
|
||||
///
|
||||
/// First tries to stack with existing items, then fills empty slots.
|
||||
|
||||
@ -13,11 +13,12 @@ use pumpkin_protocol::java::{
|
||||
play::{
|
||||
SChangeGameMode, SChatCommand, SChatMessage, SChunkBatch, SClickSlot, SClientCommand,
|
||||
SClientInformationPlay, SCloseContainer, SCommandSuggestion, SConfirmTeleport,
|
||||
SCookieResponse, SCustomPayload, SInteract, SKeepAlive, SMoveVehicle, SPaddleBoat,
|
||||
SPickItemFromBlock, SPlayPingRequest, SPlayerAbilities, SPlayerAction, SPlayerCommand,
|
||||
SPlayerInput, SPlayerLoaded, SPlayerPosition, SPlayerPositionRotation, SPlayerRotation,
|
||||
SPlayerSession, SSetCommandBlock, SSetCreativeSlot, SSetHeldItem, SSetPlayerGround,
|
||||
SSwingArm, SUpdateSign, SUseItem, SUseItemOn,
|
||||
SContainerButtonClick, SCookieResponse, SCustomPayload, SInteract, SKeepAlive,
|
||||
SMoveVehicle, SPaddleBoat, SPickItemFromBlock, SPlayPingRequest, SPlayerAbilities,
|
||||
SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerLoaded, SPlayerPosition,
|
||||
SPlayerPositionRotation, SPlayerRotation, SPlayerSession, SSetCommandBlock,
|
||||
SSetCreativeSlot, SSetHeldItem, SSetPlayerGround, SSwingArm, SUpdateSign, SUseItem,
|
||||
SUseItemOn,
|
||||
},
|
||||
status::SStatusPingRequest,
|
||||
},
|
||||
@ -84,6 +85,7 @@ fn fuzz_all_deserializers(payload: &[u8]) {
|
||||
SPlayerLoaded,
|
||||
SPlayPingRequest,
|
||||
SClickSlot,
|
||||
SContainerButtonClick,
|
||||
SSetHeldItem,
|
||||
SSetCreativeSlot,
|
||||
SSwingArm,
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
use crate::VarInt;
|
||||
use pumpkin_data::packet::serverbound::PLAY_CONTAINER_BUTTON_CLICK;
|
||||
use pumpkin_macros::java_packet;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[java_packet(PLAY_CONTAINER_BUTTON_CLICK)]
|
||||
pub struct SContainerButtonClick {
|
||||
pub window_id: VarInt,
|
||||
pub button_id: VarInt,
|
||||
}
|
||||
@ -10,6 +10,7 @@ mod client_tick_end;
|
||||
mod close_container;
|
||||
mod command_suggestion;
|
||||
mod confirm_teleport;
|
||||
mod container_button_click;
|
||||
mod cookie_response;
|
||||
mod custom_payload;
|
||||
mod interact;
|
||||
@ -53,6 +54,7 @@ pub use client_tick_end::*;
|
||||
pub use close_container::*;
|
||||
pub use command_suggestion::*;
|
||||
pub use confirm_teleport::*;
|
||||
pub use container_button_click::*;
|
||||
pub use cookie_response::*;
|
||||
pub use custom_payload::*;
|
||||
pub use interact::*;
|
||||
|
||||
123
pumpkin/src/block/blocks/enchanting_table.rs
Normal file
123
pumpkin/src/block/blocks/enchanting_table.rs
Normal file
@ -0,0 +1,123 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::block::registry::BlockActionResult;
|
||||
use crate::block::{BlockBehaviour, BlockFuture, NormalUseArgs};
|
||||
use pumpkin_data::translation;
|
||||
use pumpkin_inventory::enchanting::enchanting_screen_handler::EnchantingTableScreenHandler;
|
||||
use pumpkin_inventory::player::player_inventory::PlayerInventory;
|
||||
use pumpkin_inventory::screen_handler::{
|
||||
BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
|
||||
};
|
||||
use pumpkin_macros::pumpkin_block;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::text::TextComponent;
|
||||
use pumpkin_world::inventory::{Inventory, SimpleInventory};
|
||||
use tokio::sync::Mutex;
|
||||
|
||||
#[pumpkin_block("minecraft:enchanting_table")]
|
||||
pub struct EnchantingTableBlock;
|
||||
|
||||
impl BlockBehaviour for EnchantingTableBlock {
|
||||
fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> {
|
||||
Box::pin(async move {
|
||||
let mut bookshelf_count = 0;
|
||||
|
||||
for off_z in -1..=1 {
|
||||
for off_x in -1..=1 {
|
||||
if (off_z != 0 || off_x != 0)
|
||||
&& args.world.get_block_state(&args.position.add(off_x, 0, off_z)).await.id == 0 // Air
|
||||
&& args.world.get_block_state(&args.position.add(off_x, 1, off_z)).await.id == 0
|
||||
// Air
|
||||
{
|
||||
for off_y in 0..=1 {
|
||||
if self
|
||||
.is_bookshelf(
|
||||
args.world,
|
||||
&args.position.add(off_x * 2, off_y, off_z * 2),
|
||||
)
|
||||
.await
|
||||
{
|
||||
bookshelf_count += 1;
|
||||
}
|
||||
if off_x != 0 && off_z != 0 {
|
||||
if self
|
||||
.is_bookshelf(
|
||||
args.world,
|
||||
&args.position.add(off_x * 2, off_y, off_z),
|
||||
)
|
||||
.await
|
||||
{
|
||||
bookshelf_count += 1;
|
||||
}
|
||||
if self
|
||||
.is_bookshelf(
|
||||
args.world,
|
||||
&args.position.add(off_x, off_y, off_z * 2),
|
||||
)
|
||||
.await
|
||||
{
|
||||
bookshelf_count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let bookshelf_count = bookshelf_count.min(15);
|
||||
|
||||
args.player
|
||||
.open_handled_screen(
|
||||
&EnchantingTableScreenFactory {
|
||||
bookshelf_count,
|
||||
seed: args.player.enchantment_seed(),
|
||||
},
|
||||
Some(*args.position),
|
||||
)
|
||||
.await;
|
||||
BlockActionResult::Success
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl EnchantingTableBlock {
|
||||
async fn is_bookshelf(&self, world: &Arc<crate::world::World>, pos: &BlockPos) -> bool {
|
||||
let state = world.get_block_state(pos).await;
|
||||
let block = pumpkin_data::Block::from_state_id(state.id);
|
||||
block.name == "bookshelf"
|
||||
}
|
||||
}
|
||||
|
||||
struct EnchantingTableScreenFactory {
|
||||
bookshelf_count: i32,
|
||||
seed: i32,
|
||||
}
|
||||
|
||||
impl ScreenHandlerFactory for EnchantingTableScreenFactory {
|
||||
fn create_screen_handler<'a>(
|
||||
&'a self,
|
||||
sync_id: u8,
|
||||
player_inventory: &'a Arc<PlayerInventory>,
|
||||
_player: &'a dyn InventoryPlayer,
|
||||
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
|
||||
Box::pin(async move {
|
||||
let inventory: Arc<dyn Inventory> = Arc::new(SimpleInventory::new(2));
|
||||
let handler = EnchantingTableScreenHandler::new(
|
||||
sync_id,
|
||||
player_inventory,
|
||||
&inventory,
|
||||
self.seed,
|
||||
self.bookshelf_count,
|
||||
);
|
||||
let screen_handler_arc = Arc::new(Mutex::new(handler));
|
||||
Some(screen_handler_arc as SharedScreenHandler)
|
||||
})
|
||||
}
|
||||
|
||||
fn get_display_name(&self) -> TextComponent {
|
||||
TextComponent::translate_cross(
|
||||
translation::java::CONTAINER_ENCHANT,
|
||||
translation::bedrock::CONTAINER_ENCHANT,
|
||||
&[],
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -15,6 +15,7 @@ pub mod shulker_box;
|
||||
pub mod blast_furnace;
|
||||
pub mod brewing_stand;
|
||||
pub mod crafting_table;
|
||||
pub mod enchanting_table;
|
||||
pub mod furnace;
|
||||
pub mod grindstone;
|
||||
pub mod smoker;
|
||||
|
||||
@ -216,7 +216,7 @@ pub struct NormalUseArgs<'a> {
|
||||
pub world: &'a Arc<World>,
|
||||
pub block: &'a Block,
|
||||
pub position: &'a BlockPos,
|
||||
pub player: &'a Player,
|
||||
pub player: &'a Arc<Player>,
|
||||
pub hit: &'a BlockHitResult<'a>,
|
||||
}
|
||||
|
||||
@ -225,7 +225,7 @@ pub struct UseWithItemArgs<'a> {
|
||||
pub world: &'a Arc<World>,
|
||||
pub block: &'a Block,
|
||||
pub position: &'a BlockPos,
|
||||
pub player: &'a Player,
|
||||
pub player: &'a Arc<Player>,
|
||||
pub hit: &'a BlockHitResult<'a>,
|
||||
pub item_stack: &'a Arc<Mutex<ItemStack>>,
|
||||
}
|
||||
|
||||
@ -160,6 +160,7 @@ use crate::block::blocks::chain::ChainBlock;
|
||||
use crate::block::blocks::cobweb::CobwebBlock;
|
||||
use crate::block::blocks::crafting_table::CraftingTableBlock;
|
||||
use crate::block::blocks::dragon_egg::DragonEggBlock;
|
||||
use crate::block::blocks::enchanting_table::EnchantingTableBlock;
|
||||
use crate::block::blocks::end_rod::EndRodBlock;
|
||||
use crate::block::blocks::ender_chest::EnderChestBlock;
|
||||
use crate::block::blocks::hopper::HopperBlock;
|
||||
@ -194,6 +195,7 @@ pub fn default_registry() -> Arc<BlockRegistry> {
|
||||
manager.register(CopperChestBlock);
|
||||
manager.register(EnderChestBlock);
|
||||
manager.register(CraftingTableBlock);
|
||||
manager.register(EnchantingTableBlock);
|
||||
manager.register(DirtPathBlock);
|
||||
manager.register(DoorBlock);
|
||||
manager.register(FarmlandBlock);
|
||||
@ -491,7 +493,7 @@ impl BlockRegistry {
|
||||
pub async fn on_use(
|
||||
&self,
|
||||
block: &Block,
|
||||
player: &Player,
|
||||
player: &Arc<Player>,
|
||||
position: &BlockPos,
|
||||
hit: &BlockHitResult<'_>,
|
||||
server: &Server,
|
||||
@ -530,7 +532,7 @@ impl BlockRegistry {
|
||||
pub async fn use_with_item(
|
||||
&self,
|
||||
block: &Block,
|
||||
player: &Player,
|
||||
player: &Arc<Player>,
|
||||
position: &BlockPos,
|
||||
hit: &BlockHitResult<'_>,
|
||||
item_stack: &Arc<Mutex<ItemStack>>,
|
||||
@ -557,7 +559,7 @@ impl BlockRegistry {
|
||||
pub async fn use_with_item_fluid(
|
||||
&self,
|
||||
fluid: &Fluid,
|
||||
player: &Player,
|
||||
player: &Arc<Player>,
|
||||
position: BlockPos,
|
||||
item: &Item,
|
||||
server: &Server,
|
||||
|
||||
@ -44,12 +44,12 @@ impl ServerPlayerData {
|
||||
/// # Returns
|
||||
///
|
||||
/// A Result indicating success or the error that occurred.
|
||||
pub async fn handle_player_leave(&self, player: &Player) -> Result<(), PlayerDataError> {
|
||||
pub async fn handle_player_leave(&self, player: &Arc<Player>) -> Result<(), PlayerDataError> {
|
||||
player
|
||||
.player_screen_handler
|
||||
.lock()
|
||||
.await
|
||||
.on_closed(player)
|
||||
.on_closed(player.as_ref())
|
||||
.await;
|
||||
player.on_handled_screen_closed().await;
|
||||
|
||||
|
||||
@ -194,7 +194,7 @@ impl Mob for CreeperEntity {
|
||||
|
||||
fn mob_interact<'a>(
|
||||
&'a self,
|
||||
player: &'a Player,
|
||||
player: &'a Arc<Player>,
|
||||
item_stack: &'a mut ItemStack,
|
||||
) -> EntityBaseFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
|
||||
@ -384,7 +384,7 @@ pub trait Mob: EntityBase + Send + Sync {
|
||||
|
||||
fn mob_interact<'a>(
|
||||
&'a self,
|
||||
_player: &'a Player,
|
||||
_player: &'a Arc<Player>,
|
||||
_item_stack: &'a mut ItemStack,
|
||||
) -> EntityBaseFuture<'a, bool> {
|
||||
Box::pin(async { false })
|
||||
@ -526,7 +526,7 @@ impl<T: Mob + Send + 'static> EntityBase for T {
|
||||
|
||||
fn interact<'a>(
|
||||
&'a self,
|
||||
player: &'a Player,
|
||||
player: &'a Arc<Player>,
|
||||
item_stack: &'a mut ItemStack,
|
||||
) -> EntityBaseFuture<'a, bool> {
|
||||
Box::pin(async move { self.mob_interact(player, item_stack).await })
|
||||
|
||||
@ -264,7 +264,7 @@ pub trait EntityBase: Send + Sync + NBTStorage + std::any::Any {
|
||||
/// Returns true if the interaction was handled.
|
||||
fn interact<'a>(
|
||||
&'a self,
|
||||
_player: &'a Player,
|
||||
_player: &'a Arc<Player>,
|
||||
_item_stack: &'a mut ItemStack,
|
||||
) -> EntityBaseFuture<'a, bool> {
|
||||
Box::pin(async { false })
|
||||
|
||||
@ -113,7 +113,7 @@ impl Mob for ChickenEntity {
|
||||
|
||||
fn mob_interact<'a>(
|
||||
&'a self,
|
||||
player: &'a Player,
|
||||
player: &'a Arc<Player>,
|
||||
item_stack: &'a mut ItemStack,
|
||||
) -> EntityBaseFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
|
||||
@ -74,7 +74,7 @@ impl Mob for CowEntity {
|
||||
|
||||
fn mob_interact<'a>(
|
||||
&'a self,
|
||||
player: &'a Player,
|
||||
player: &'a Arc<Player>,
|
||||
item_stack: &'a mut ItemStack,
|
||||
) -> EntityBaseFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
|
||||
@ -79,7 +79,7 @@ impl Mob for PigEntity {
|
||||
|
||||
fn mob_interact<'a>(
|
||||
&'a self,
|
||||
player: &'a Player,
|
||||
player: &'a Arc<Player>,
|
||||
item_stack: &'a mut ItemStack,
|
||||
) -> EntityBaseFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
|
||||
@ -140,7 +140,7 @@ impl Mob for SheepEntity {
|
||||
|
||||
fn mob_interact<'a>(
|
||||
&'a self,
|
||||
player: &'a Player,
|
||||
player: &'a Arc<Player>,
|
||||
item_stack: &'a mut ItemStack,
|
||||
) -> EntityBaseFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
|
||||
@ -268,7 +268,7 @@ impl VillagerEntity {
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn open_trading_screen(&self, player: &Player) {
|
||||
pub async fn open_trading_screen(&self, player: &Arc<Player>) {
|
||||
let self_weak = self.self_arc.lock().await;
|
||||
if let Some(self_arc) = self_weak.as_ref().and_then(std::sync::Weak::upgrade) {
|
||||
player.open_handled_screen(&*self_arc, None).await;
|
||||
@ -435,9 +435,10 @@ impl Mob for VillagerEntity {
|
||||
|
||||
fn mob_interact<'a>(
|
||||
&'a self,
|
||||
player: &'a crate::entity::player::Player,
|
||||
player: &'a Arc<Player>,
|
||||
_item_stack: &'a mut pumpkin_data::item_stack::ItemStack,
|
||||
) -> crate::entity::EntityBaseFuture<'a, bool> {
|
||||
let player = player.clone();
|
||||
Box::pin(async move {
|
||||
if self.get_entity().age.load(Ordering::Relaxed) < 0 {
|
||||
self.set_unhappy().await;
|
||||
@ -451,7 +452,7 @@ impl Mob for VillagerEntity {
|
||||
}
|
||||
drop(offers);
|
||||
|
||||
self.open_trading_screen(player).await;
|
||||
self.open_trading_screen(&player).await;
|
||||
|
||||
true
|
||||
})
|
||||
|
||||
@ -68,7 +68,9 @@ use pumpkin_protocol::java::client::play::{
|
||||
CTitleAnimation, CTitleText, CUnloadChunk, CUpdateMobEffect, CUpdateTime, GameEvent, Metadata,
|
||||
PlayerAction, PlayerInfoFlags, PreviousMessage,
|
||||
};
|
||||
use pumpkin_protocol::java::server::play::{SClickSlot, SRenameItem, SlotActionType};
|
||||
use pumpkin_protocol::java::server::play::{
|
||||
SClickSlot, SContainerButtonClick, SRenameItem, SlotActionType,
|
||||
};
|
||||
use pumpkin_util::math::{
|
||||
boundingbox::BoundingBox, experience, position::BlockPos, vector2::Vector2, vector3::Vector3,
|
||||
};
|
||||
@ -493,6 +495,7 @@ pub struct Player {
|
||||
pub tab_list_order: AtomicI32,
|
||||
pub tab_list_latency: AtomicI32,
|
||||
pub tab_list_listed: AtomicBool,
|
||||
pub enchantment_seed: AtomicI32,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
@ -548,6 +551,7 @@ impl Player {
|
||||
// TODO: Load this from previous instance
|
||||
hunger_manager: HungerManager::default(),
|
||||
current_block_destroy_stage: AtomicI32::new(-1),
|
||||
enchantment_seed: AtomicI32::new(rand::random()),
|
||||
open_container: AtomicCell::new(None),
|
||||
open_container_pos: AtomicCell::new(None),
|
||||
tick_counter: AtomicI32::new(0),
|
||||
@ -3063,7 +3067,7 @@ impl Player {
|
||||
.store(current_id % 100 + 1, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
pub async fn close_handled_screen(&self) {
|
||||
pub async fn close_handled_screen(self: &Arc<Self>) {
|
||||
self.client
|
||||
.enqueue_packet(&CCloseContainer::new(
|
||||
self.current_screen_handler
|
||||
@ -3078,27 +3082,31 @@ impl Player {
|
||||
self.on_handled_screen_closed().await;
|
||||
}
|
||||
|
||||
pub async fn on_handled_screen_closed(&self) {
|
||||
// let window_type = {
|
||||
// let handler_lock = self.current_screen_handler.lock().await;
|
||||
// let mut handler = handler_lock.lock().await;
|
||||
// let wt = handler.window_type();
|
||||
// handler.on_closed(self).await;
|
||||
// wt
|
||||
// };
|
||||
pub async fn on_handled_screen_closed(self: &Arc<Self>) {
|
||||
let current_screen_handler: Arc<Mutex<dyn ScreenHandler>> =
|
||||
self.current_screen_handler.lock().await.clone();
|
||||
|
||||
// TODO
|
||||
// if let Some(server) = self.living_entity.entity.world.load().server.upgrade() {
|
||||
// server
|
||||
// .plugin_manager
|
||||
// .fire(InventoryCloseEvent::new(&self, window_type))
|
||||
// .await;
|
||||
// }
|
||||
let window_type = {
|
||||
let mut handler = current_screen_handler.lock().await;
|
||||
let wt = handler.window_type();
|
||||
handler.on_closed(self.as_ref()).await;
|
||||
wt
|
||||
};
|
||||
|
||||
if let Some(server) = self.living_entity.entity.world.load().server.upgrade() {
|
||||
server
|
||||
.plugin_manager
|
||||
.fire(
|
||||
crate::plugin::api::events::player::inventory_close::InventoryCloseEvent::new(
|
||||
self,
|
||||
window_type,
|
||||
),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
let player_screen_handler: Arc<Mutex<dyn ScreenHandler>> =
|
||||
self.player_screen_handler.clone();
|
||||
let current_screen_handler: Arc<Mutex<dyn ScreenHandler>> =
|
||||
self.current_screen_handler.lock().await.clone();
|
||||
|
||||
if !Arc::ptr_eq(&player_screen_handler, ¤t_screen_handler) {
|
||||
player_screen_handler
|
||||
@ -3138,7 +3146,7 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn open_handled_screen(
|
||||
&self,
|
||||
self: &Arc<Self>,
|
||||
screen_handler_factory: &dyn ScreenHandlerFactory,
|
||||
block_pos: Option<BlockPos>,
|
||||
) -> Option<u8> {
|
||||
@ -3160,7 +3168,7 @@ impl Player {
|
||||
.create_screen_handler(
|
||||
self.screen_handler_sync_id.load(Ordering::Relaxed),
|
||||
&self.inventory,
|
||||
self,
|
||||
self.as_ref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
@ -3188,7 +3196,7 @@ impl Player {
|
||||
}
|
||||
|
||||
pub async fn open_handled_screen_direct(
|
||||
&self,
|
||||
self: &Arc<Self>,
|
||||
screen_handler: Arc<Mutex<dyn ScreenHandler>>,
|
||||
title: TextComponent,
|
||||
) {
|
||||
@ -3432,7 +3440,20 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if the player has a specific permission
|
||||
/// Handles when the player clicks a button in a container (e.g. Enchantment Table)
|
||||
pub async fn on_container_button_click(self: &Arc<Self>, packet: SContainerButtonClick) {
|
||||
let screen_handler = self.current_screen_handler.lock().await.clone();
|
||||
let mut screen_handler = screen_handler.lock().await;
|
||||
|
||||
if i32::from(screen_handler.sync_id()) != packet.window_id.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
screen_handler
|
||||
.on_button_click(self.as_ref(), packet.button_id.0)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn has_permission(self: &Arc<Self>, server: &Server, node: &str) -> bool {
|
||||
let perm_manager = server.permission_manager.read().await;
|
||||
let result = perm_manager
|
||||
@ -3670,6 +3691,7 @@ impl NBTStorage for Player {
|
||||
} else {
|
||||
nbt.put_bool(false);
|
||||
}
|
||||
nbt.put_int(self.enchantment_seed.load(Ordering::Relaxed));
|
||||
})
|
||||
}
|
||||
|
||||
@ -3721,6 +3743,8 @@ impl NBTStorage for Player {
|
||||
force,
|
||||
}));
|
||||
}
|
||||
self.enchantment_seed
|
||||
.store(nbt.get_int().unwrap_or(rand::random()), Ordering::Relaxed);
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -4285,6 +4309,16 @@ impl InventoryPlayer for Player {
|
||||
})
|
||||
}
|
||||
|
||||
fn enchantment_seed(&self) -> i32 {
|
||||
self.enchantment_seed.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
fn set_enchantment_seed(&self, seed: i32) -> PlayerFuture<'_, ()> {
|
||||
Box::pin(async move {
|
||||
self.enchantment_seed.store(seed, Ordering::Relaxed);
|
||||
})
|
||||
}
|
||||
|
||||
fn get_inventory(&self) -> Arc<PlayerInventory> {
|
||||
self.inventory.clone()
|
||||
}
|
||||
|
||||
@ -185,7 +185,7 @@ impl EntityBase for BoatEntity {
|
||||
|
||||
fn interact<'a>(
|
||||
&'a self,
|
||||
player: &'a Player,
|
||||
player: &'a Arc<Player>,
|
||||
_item_stack: &'a mut ItemStack,
|
||||
) -> EntityBaseFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
|
||||
@ -8,9 +8,9 @@ use pumpkin_data::packet::CURRENT_MC_VERSION;
|
||||
use pumpkin_protocol::java::server::play::{
|
||||
SAttack, SChangeGameMode, SChatCommand, SChatMessage, SChunkBatch, SClickSlot, SClientCommand,
|
||||
SClientInformationPlay, SClientTickEnd, SCloseContainer, SCommandSuggestion, SConfirmTeleport,
|
||||
SCookieResponse as SPCookieResponse, SCustomPayload, SInteract, SKeepAlive, SMoveVehicle,
|
||||
SPaddleBoat, SPickItemFromBlock, SPlaceRecipe, SPlayPingRequest, SPlayerAbilities,
|
||||
SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerLoaded, SPlayerPosition,
|
||||
SContainerButtonClick, SCookieResponse as SPCookieResponse, SCustomPayload, SInteract,
|
||||
SKeepAlive, SMoveVehicle, SPaddleBoat, SPickItemFromBlock, SPlaceRecipe, SPlayPingRequest,
|
||||
SPlayerAbilities, SPlayerAction, SPlayerCommand, SPlayerInput, SPlayerLoaded, SPlayerPosition,
|
||||
SPlayerPositionRotation, SPlayerRotation, SPlayerSession, SRecipeBookChangeSettings,
|
||||
SRecipeBookSeenRecipe, SRenameItem, SSelectTrade, SSetCommandBlock, SSetCreativeSlot,
|
||||
SSetHeldItem, SSetPlayerGround, SSwingArm, SUpdateSign, SUseItem, SUseItemOn,
|
||||
@ -817,6 +817,11 @@ impl JavaClient {
|
||||
.on_slot_click(SClickSlot::read(payload, &version)?, server)
|
||||
.await;
|
||||
}
|
||||
id if id == SContainerButtonClick::to_id(version) => {
|
||||
player
|
||||
.on_container_button_click(SContainerButtonClick::read(payload, &version)?)
|
||||
.await;
|
||||
}
|
||||
id if id == SSetHeldItem::to_id(version) => {
|
||||
self.handle_set_held_item(player, SSetHeldItem::read(payload, &version)?)
|
||||
.await;
|
||||
|
||||
@ -2166,7 +2166,7 @@ impl JavaClient {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
async fn call_use_item_on(
|
||||
&self,
|
||||
player: &Player,
|
||||
player: &Arc<Player>,
|
||||
position: &BlockPos,
|
||||
cursor_pos: &Vector3<f32>,
|
||||
face: &BlockDirection,
|
||||
@ -2509,7 +2509,7 @@ impl JavaClient {
|
||||
|
||||
pub async fn handle_close_container(
|
||||
&self,
|
||||
player: &Player,
|
||||
player: &Arc<Player>,
|
||||
_server: &Server,
|
||||
_packet: SCloseContainer,
|
||||
) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user