From 8ce65cc42d2f1fa74fd038bb73aacbb769c73868 Mon Sep 17 00:00:00 2001 From: spr-equinox <66425054+spr-equinox@users.noreply.github.com> Date: Mon, 28 Jul 2025 18:24:40 +0800 Subject: [PATCH] Refactor ItemStack to Support Vanilla-Like Data Components (#1059) * improve tag v1 * improve tag v2 * fix typo and make BlockPredicate better * improve get state from state id * impl DataComponent * make tag pub * make component work * apply clippy * apply clippy * remove unused crate * apply to itemstack and no lifetime mark anymore * Update tag.rs * use Cow * remove unnecessary lazy_static and Cow * Update mod.rs * Update mod.rs * make DataComponent dyn * remove unused crate * fix error * Update Cargo.toml * Update Cargo.lock * fix * Update composter.rs * update rust-version, remove unused crate, and merge * Update furnace.rs --- Cargo.lock | 17 - assets/attributes.json | 175 +++++-- assets/data_component.json | 98 ++++ pumpkin-data/build/attributes.rs | 54 ++ pumpkin-data/build/build.rs | 6 +- pumpkin-data/build/data_component.rs | 58 +++ pumpkin-data/build/enchantments.rs | 5 +- pumpkin-data/build/item.rs | 438 ++++++++-------- pumpkin-data/src/data_component_impl.rs | 479 ++++++++++++++++++ pumpkin-data/src/lib.rs | 9 + .../src/crafting/crafting_inventory.rs | 6 +- .../src/crafting/crafting_screen_handler.rs | 36 +- pumpkin-inventory/src/entity_equipment.rs | 8 +- .../src/furnace/furnace_screen_handler.rs | 8 +- .../src/generic_container_screen_handler.rs | 10 +- .../src/player/player_inventory.rs | 34 +- .../src/player/player_screen_handler.rs | 26 +- pumpkin-inventory/src/screen_handler.rs | 67 +-- pumpkin-inventory/src/slot.rs | 9 +- pumpkin-inventory/src/sync_handler.rs | 12 +- .../src/codec/item_stack_seralizer.rs | 6 +- pumpkin-world/Cargo.toml | 2 +- pumpkin-world/src/block/entities/barrel.rs | 8 +- .../src/block/entities/chiseled_bookshelf.rs | 8 +- pumpkin-world/src/block/entities/dropper.rs | 8 +- pumpkin-world/src/block/entities/furnace.rs | 12 +- pumpkin-world/src/block/entities/hopper.rs | 14 +- .../src/block/entities/shulker_box.rs | 8 +- pumpkin-world/src/inventory/mod.rs | 2 +- pumpkin-world/src/item/mod.rs | 151 +++--- pumpkin/Cargo.toml | 2 +- pumpkin/src/block/blocks/cake.rs | 23 +- pumpkin/src/block/blocks/candles.rs | 2 +- .../src/block/blocks/chiseled_bookshelf.rs | 27 +- pumpkin/src/block/blocks/composter.rs | 23 +- pumpkin/src/block/blocks/jukebox.rs | 11 +- pumpkin/src/block/blocks/pumpkin.rs | 1 + pumpkin/src/block/blocks/redstone/dropper.rs | 2 +- pumpkin/src/block/blocks/redstone/tripwire.rs | 2 +- pumpkin/src/block/blocks/tnt.rs | 2 +- pumpkin/src/block/mod.rs | 10 +- pumpkin/src/command/commands/clear.rs | 4 +- pumpkin/src/command/commands/give.rs | 11 +- pumpkin/src/entity/item.rs | 2 +- pumpkin/src/entity/living.rs | 7 +- pumpkin/src/entity/player.rs | 36 +- pumpkin/src/net/java/play.rs | 8 +- pumpkin/src/world/mod.rs | 6 +- 48 files changed, 1409 insertions(+), 544 deletions(-) create mode 100644 assets/data_component.json create mode 100644 pumpkin-data/build/attributes.rs create mode 100644 pumpkin-data/build/data_component.rs create mode 100644 pumpkin-data/src/data_component_impl.rs diff --git a/Cargo.lock b/Cargo.lock index fdf74ff09..db9509eb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1115,12 +1115,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - [[package]] name = "hmac" version = "0.13.0-rc.0" @@ -1676,16 +1670,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "num_threads" version = "0.1.7" @@ -2196,7 +2180,6 @@ dependencies = [ "lz4-java-wrc", "num-derive", "num-traits", - "num_cpus", "pumpkin-config", "pumpkin-data", "pumpkin-nbt", diff --git a/assets/attributes.json b/assets/attributes.json index eb8bfdca8..71e4fa464 100644 --- a/assets/attributes.json +++ b/assets/attributes.json @@ -1,37 +1,142 @@ { - "armor": 0.0, - "armor_toughness": 0.0, - "attack_damage": 2.0, - "attack_knockback": 0.0, - "attack_speed": 4.0, - "block_break_speed": 1.0, - "block_interaction_range": 4.5, - "burning_time": 1.0, - "camera_distance": 4.0, - "explosion_knockback_resistance": 0.0, - "entity_interaction_range": 3.0, - "fall_damage_multiplier": 1.0, - "flying_speed": 0.4, - "follow_range": 32.0, - "gravity": 0.08, - "jump_strength": 0.41999998688697815, - "knockback_resistance": 0.0, - "luck": 0.0, - "max_absorption": 0.0, - "max_health": 20.0, - "mining_efficiency": 0.0, - "movement_efficiency": 0.0, - "movement_speed": 0.7, - "oxygen_bonus": 0.0, - "safe_fall_distance": 3.0, - "scale": 1.0, - "sneaking_speed": 0.3, - "spawn_reinforcements": 0.0, - "step_height": 0.6, - "submerged_mining_speed": 0.2, - "sweeping_damage_ratio": 0.0, - "tempt_range": 10.0, - "water_movement_efficiency": 0.0, - "waypoint_transmit_range": 0.0, - "waypoint_receive_range": 0.0 + "armor": { + "id": 0, + "default_value": 0.0 + }, + "armor_toughness": { + "id": 1, + "default_value": 0.0 + }, + "attack_damage": { + "id": 2, + "default_value": 2.0 + }, + "attack_knockback": { + "id": 3, + "default_value": 0.0 + }, + "attack_speed": { + "id": 4, + "default_value": 4.0 + }, + "block_break_speed": { + "id": 5, + "default_value": 1.0 + }, + "block_interaction_range": { + "id": 6, + "default_value": 4.5 + }, + "burning_time": { + "id": 7, + "default_value": 1.0 + }, + "camera_distance": { + "id": 8, + "default_value": 4.0 + }, + "explosion_knockback_resistance": { + "id": 9, + "default_value": 0.0 + }, + "entity_interaction_range": { + "id": 10, + "default_value": 3.0 + }, + "fall_damage_multiplier": { + "id": 11, + "default_value": 1.0 + }, + "flying_speed": { + "id": 12, + "default_value": 0.4 + }, + "follow_range": { + "id": 13, + "default_value": 32.0 + }, + "gravity": { + "id": 14, + "default_value": 0.08 + }, + "jump_strength": { + "id": 15, + "default_value": 0.41999998688697815 + }, + "knockback_resistance": { + "id": 16, + "default_value": 0.0 + }, + "luck": { + "id": 17, + "default_value": 0.0 + }, + "max_absorption": { + "id": 18, + "default_value": 0.0 + }, + "max_health": { + "id": 19, + "default_value": 20.0 + }, + "mining_efficiency": { + "id": 20, + "default_value": 0.0 + }, + "movement_efficiency": { + "id": 21, + "default_value": 0.0 + }, + "movement_speed": { + "id": 22, + "default_value": 0.7 + }, + "oxygen_bonus": { + "id": 23, + "default_value": 0.0 + }, + "safe_fall_distance": { + "id": 24, + "default_value": 3.0 + }, + "scale": { + "id": 25, + "default_value": 1.0 + }, + "sneaking_speed": { + "id": 26, + "default_value": 0.3 + }, + "spawn_reinforcements": { + "id": 27, + "default_value": 0.0 + }, + "step_height": { + "id": 28, + "default_value": 0.6 + }, + "submerged_mining_speed": { + "id": 29, + "default_value": 0.2 + }, + "sweeping_damage_ratio": { + "id": 30, + "default_value": 0.0 + }, + "tempt_range": { + "id": 31, + "default_value": 10.0 + }, + "water_movement_efficiency": { + "id": 32, + "default_value": 0.0 + }, + "waypoint_transmit_range": { + "id": 33, + "default_value": 0.0 + }, + "waypoint_receive_range": { + "id": 34, + "default_value": 0.0 + } } \ No newline at end of file diff --git a/assets/data_component.json b/assets/data_component.json new file mode 100644 index 000000000..e2abc6dce --- /dev/null +++ b/assets/data_component.json @@ -0,0 +1,98 @@ +{ + "minecraft:custom_data": 0, + "minecraft:max_stack_size": 1, + "minecraft:max_damage": 2, + "minecraft:damage": 3, + "minecraft:unbreakable": 4, + "minecraft:custom_name": 5, + "minecraft:item_name": 6, + "minecraft:item_model": 7, + "minecraft:lore": 8, + "minecraft:rarity": 9, + "minecraft:enchantments": 10, + "minecraft:can_place_on": 11, + "minecraft:can_break": 12, + "minecraft:attribute_modifiers": 13, + "minecraft:custom_model_data": 14, + "minecraft:tooltip_display": 15, + "minecraft:repair_cost": 16, + "minecraft:creative_slot_lock": 17, + "minecraft:enchantment_glint_override": 18, + "minecraft:intangible_projectile": 19, + "minecraft:food": 20, + "minecraft:consumable": 21, + "minecraft:use_remainder": 22, + "minecraft:use_cooldown": 23, + "minecraft:damage_resistant": 24, + "minecraft:tool": 25, + "minecraft:weapon": 26, + "minecraft:enchantable": 27, + "minecraft:equippable": 28, + "minecraft:repairable": 29, + "minecraft:glider": 30, + "minecraft:tooltip_style": 31, + "minecraft:death_protection": 32, + "minecraft:blocks_attacks": 33, + "minecraft:stored_enchantments": 34, + "minecraft:dyed_color": 35, + "minecraft:map_color": 36, + "minecraft:map_id": 37, + "minecraft:map_decorations": 38, + "minecraft:map_post_processing": 39, + "minecraft:charged_projectiles": 40, + "minecraft:bundle_contents": 41, + "minecraft:potion_contents": 42, + "minecraft:potion_duration_scale": 43, + "minecraft:suspicious_stew_effects": 44, + "minecraft:writable_book_content": 45, + "minecraft:written_book_content": 46, + "minecraft:trim": 47, + "minecraft:debug_stick_state": 48, + "minecraft:entity_data": 49, + "minecraft:bucket_entity_data": 50, + "minecraft:block_entity_data": 51, + "minecraft:instrument": 52, + "minecraft:provides_trim_material": 53, + "minecraft:ominous_bottle_amplifier": 54, + "minecraft:jukebox_playable": 55, + "minecraft:provides_banner_patterns": 56, + "minecraft:recipes": 57, + "minecraft:lodestone_tracker": 58, + "minecraft:firework_explosion": 59, + "minecraft:fireworks": 60, + "minecraft:profile": 61, + "minecraft:note_block_sound": 62, + "minecraft:banner_patterns": 63, + "minecraft:base_color": 64, + "minecraft:pot_decorations": 65, + "minecraft:container": 66, + "minecraft:block_state": 67, + "minecraft:bees": 68, + "minecraft:lock": 69, + "minecraft:container_loot": 70, + "minecraft:break_sound": 71, + "minecraft:villager/variant": 72, + "minecraft:wolf/variant": 73, + "minecraft:wolf/sound_variant": 74, + "minecraft:wolf/collar": 75, + "minecraft:fox/variant": 76, + "minecraft:salmon/size": 77, + "minecraft:parrot/variant": 78, + "minecraft:tropical_fish/pattern": 79, + "minecraft:tropical_fish/base_color": 80, + "minecraft:tropical_fish/pattern_color": 81, + "minecraft:mooshroom/variant": 82, + "minecraft:rabbit/variant": 83, + "minecraft:pig/variant": 84, + "minecraft:cow/variant": 85, + "minecraft:chicken/variant": 86, + "minecraft:frog/variant": 87, + "minecraft:horse/variant": 88, + "minecraft:painting/variant": 89, + "minecraft:llama/variant": 90, + "minecraft:axolotl/variant": 91, + "minecraft:cat/variant": 92, + "minecraft:cat/collar": 93, + "minecraft:sheep/color": 94, + "minecraft:shulker/color": 95 +} \ No newline at end of file diff --git a/pumpkin-data/build/attributes.rs b/pumpkin-data/build/attributes.rs new file mode 100644 index 000000000..2357acd9c --- /dev/null +++ b/pumpkin-data/build/attributes.rs @@ -0,0 +1,54 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use serde::Deserialize; +use std::collections::HashMap; +use std::fs; + +#[derive(Deserialize)] +struct Attributes { + id: u8, + default_value: f64, +} + +pub(crate) fn build() -> TokenStream { + println!("cargo:rerun-if-changed=../assets/attributes.json"); + + let attributes: HashMap = + serde_json::from_str(&fs::read_to_string("../assets/attributes.json").unwrap()) + .expect("Failed to parse attributes.json"); + + let mut consts = TokenStream::new(); + + let mut data_component_vec = attributes.iter().collect::>(); + data_component_vec.sort_by_key(|(_, i)| i.id); + + for (raw_name, raw_value) in &data_component_vec { + let pascal_case = format_ident!("{}", raw_name.to_uppercase()); + + let id = raw_value.id; + let default_value = raw_value.default_value; + consts.extend(quote! { + pub const #pascal_case: Self = Self { + id: #id, + default_value: #default_value, + }; + }); + } + + quote! { + use std::hash::Hash; + #[derive(Clone, Debug)] + pub struct Attributes { + pub id: u8, + pub default_value: f64, + } + impl Hash for Attributes { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } + } + impl Attributes { + #consts + } + } +} diff --git a/pumpkin-data/build/build.rs b/pumpkin-data/build/build.rs index 8e37c1331..474f06554 100644 --- a/pumpkin-data/build/build.rs +++ b/pumpkin-data/build/build.rs @@ -4,11 +4,13 @@ use quote::{format_ident, quote}; use rayon::prelude::*; use std::{fs, io::Write, path::Path, process::Command}; +mod attributes; mod biome; mod block; mod chunk_status; mod composter_increase_chance; mod damage_type; +mod data_component; mod enchantments; mod entity_pose; mod entity_status; @@ -62,11 +64,11 @@ pub fn main() { (damage_type::build, "damage_type.rs"), (message_type::build, "message_type.rs"), (spawn_egg::build, "spawn_egg.rs"), + (block::build, "block.rs"), (item::build, "item.rs"), (fluid::build, "fluid.rs"), (status_effect::build, "status_effect.rs"), (entity_status::build, "entity_status.rs"), - (block::build, "block.rs"), (tag::build, "tag.rs"), (noise_router::build, "noise_router.rs"), ( @@ -80,6 +82,8 @@ pub fn main() { (recipes::build, "recipes.rs"), (enchantments::build, "enchantment.rs"), (fuels::build, "fuels.rs"), + (data_component::build, "data_component.rs"), + (attributes::build, "attributes.rs"), (recipe_remainder::build, "recipe_remainder.rs"), ]; diff --git a/pumpkin-data/build/data_component.rs b/pumpkin-data/build/data_component.rs new file mode 100644 index 000000000..647281f42 --- /dev/null +++ b/pumpkin-data/build/data_component.rs @@ -0,0 +1,58 @@ +use heck::ToPascalCase; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; +use std::collections::HashMap; +use std::fs; + +pub(crate) fn build() -> TokenStream { + println!("cargo:rerun-if-changed=../assets/data_component.json"); + + let data_component: HashMap = + serde_json::from_str(&fs::read_to_string("../assets/data_component.json").unwrap()) + .expect("Failed to parse data_component.json"); + + let mut enum_variants = TokenStream::new(); + let mut enum_to_name = TokenStream::new(); + let mut data_component_vec = data_component.iter().collect::>(); + data_component_vec.sort_by_key(|(_, i)| **i); + + for (raw_name, raw_value) in &data_component_vec { + let strip_name = raw_name + .strip_prefix("minecraft:") + .unwrap() + .replace("/", "_"); + let pascal_case = format_ident!("{}", strip_name.to_pascal_case()); + + // Enum variant + + enum_variants.extend(quote! { + #pascal_case = #raw_value, + }); + + // Enum -> &str + enum_to_name.extend(quote! { + Self::#pascal_case => #raw_name, + }); + } + + quote! { + use crate::data_component_impl::*; + + #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] + #[repr(u8)] + pub enum DataComponent { + #enum_variants + } + + impl DataComponent { + pub const fn to_id(self) -> u8 { + self as u8 + } + pub const fn to_name(self) -> &'static str { + match self { + #enum_to_name + } + } + } + } +} diff --git a/pumpkin-data/build/enchantments.rs b/pumpkin-data/build/enchantments.rs index 59266f388..ed2caba9b 100644 --- a/pumpkin-data/build/enchantments.rs +++ b/pumpkin-data/build/enchantments.rs @@ -30,7 +30,7 @@ pub enum AttributeModifierSlot { } impl AttributeModifierSlot { - fn to_tokens(&self) -> TokenStream { + pub fn to_tokens(&self) -> TokenStream { match self { AttributeModifierSlot::Any => quote! { AttributeModifierSlot::Any }, AttributeModifierSlot::MainHand => quote! { AttributeModifierSlot::MainHand }, @@ -80,6 +80,7 @@ pub(crate) fn build() -> TokenStream { } quote! { + use std::hash::Hash; #[derive(Debug, Clone)] pub struct Enchantment { pub name: &'static str, @@ -90,7 +91,7 @@ pub(crate) fn build() -> TokenStream { // TODO: add more } - #[derive(Debug, Clone)] + #[derive(Debug, Clone, Hash)] pub enum AttributeModifierSlot { Any, MainHand, diff --git a/pumpkin-data/build/item.rs b/pumpkin-data/build/item.rs index 61c03e9fd..ecf53ff2b 100644 --- a/pumpkin-data/build/item.rs +++ b/pumpkin-data/build/item.rs @@ -1,19 +1,21 @@ -use std::{collections::HashMap, fs}; - +use crate::enchantments::AttributeModifierSlot; use heck::ToShoutySnakeCase; use proc_macro2::{Span, TokenStream}; +use pumpkin_util::registry::TagType; +use pumpkin_util::text::TextContent; use pumpkin_util::{registry::RegistryEntryList, text::TextComponent}; use quote::{ToTokens, format_ident, quote}; use serde::Deserialize; +use std::{collections::HashMap, fs}; use syn::{Ident, LitBool, LitFloat, LitInt, LitStr}; -#[derive(Deserialize, Clone, Debug)] +#[derive(Deserialize, Clone)] pub struct Item { pub id: u16, pub components: ItemComponents, } -#[derive(Deserialize, Clone, Debug)] +#[derive(Deserialize, Clone)] pub struct ItemComponents { #[serde(rename = "minecraft:item_name")] pub item_name: TextComponent, @@ -36,164 +38,218 @@ pub struct ItemComponents { impl ToTokens for ItemComponents { fn to_tokens(&self, tokens: &mut TokenStream) { let max_stack_size = LitInt::new(&self.max_stack_size.to_string(), Span::call_site()); - let jukebox_playable = match &self.jukebox_playable { - Some(playable) => { - let song = LitStr::new(playable, Span::call_site()); - quote! { Some(#song) } - } - None => quote! { None }, - }; - - let item_name = { - let text = self.item_name.clone().get_text(); - let item_name = LitStr::new(&text, Span::call_site()); - quote! { #item_name } - }; - - let damage = match self.damage { - Some(d) => { - let damage_lit = LitInt::new(&d.to_string(), Span::call_site()); - quote! { Some(#damage_lit) } - } - None => quote! { None }, - }; - - let max_damage = match self.max_damage { - Some(md) => { - let max_damage_lit = LitInt::new(&md.to_string(), Span::call_site()); - quote! { Some(#max_damage_lit) } - } - None => quote! { None }, - }; - - let attribute_modifiers = match &self.attribute_modifiers { - Some(modifiers) => { - let modifier_code = modifiers.iter().map(|modifier| { - let r#type = LitStr::new(&modifier.r#type, Span::call_site()); - let id = LitStr::new(&modifier.id, Span::call_site()); - let amount = modifier.amount; - let operation = - Ident::new(&format!("{:?}", modifier.operation), Span::call_site()); - let slot = LitStr::new(&modifier.slot, Span::call_site()); - - quote! { - Modifier { - r#type: #r#type, - id: #id, - amount: #amount, - operation: Operation::#operation, - slot: #slot, - } - } - }); - quote! { Some(&[#(#modifier_code),*]) } - } - None => quote! { None }, - }; - - let tool = match &self.tool { - Some(tool) => { - let rules_code = tool.rules.iter().map(|rule| { - let mut block_array = Vec::new(); - - // TODO: According to the wiki, this can be a string or a list. - // I dont think there'll be any issues with always using a list, but we can - // probably save bandwidth by doing single strings. - for reg in rule.blocks.get_values() { - let tag_string = reg.serialize(); - // The client knows what tags are; just send them the tag instead of all the - // blocks that are a part of the tag. - block_array.extend(quote! { #tag_string }); - } - - let speed = match rule.speed { - Some(speed) => { - quote! { Some(#speed) } - } - None => quote! { None }, - }; - let correct_for_drops = match rule.correct_for_drops { - Some(correct_for_drops) => { - let correct_for_drops = - LitBool::new(correct_for_drops, Span::call_site()); - quote! { Some(#correct_for_drops) } - } - None => quote! { None }, - }; - quote! { - ToolRule { - blocks: &[#(#block_array),*], - speed: #speed, - correct_for_drops: #correct_for_drops - } - } - }); - let damage_per_block = match tool.damage_per_block { - Some(speed) => { - let speed = LitInt::new(&speed.to_string(), Span::call_site()); - quote! { Some(#speed) } - } - None => quote! { None }, - }; - let default_mining_speed = match tool.default_mining_speed { - Some(speed) => { - let speed = LitFloat::new(&speed.to_string(), Span::call_site()); - quote! { Some(#speed) } - } - None => quote! { None }, - }; - quote! { Some(ToolComponent { rules: &[#(#rules_code),*], damage_per_block: #damage_per_block, default_mining_speed: #default_mining_speed }) } - } - None => quote! { None }, - }; - - let food = match &self.food { - Some(food) => { - let nutrition = LitInt::new(&food.nutrition.to_string(), Span::call_site()); - let saturation = - LitFloat::new(&format!("{:.1}", food.saturation), Span::call_site()); - let can_always_eat = match food.can_always_eat { - Some(can) => { - let can = LitBool::new(can, Span::call_site()); - quote! { Some(#can) } - } - None => quote! { None }, - }; - quote! { Some(FoodComponent { - nutrition: #nutrition, - saturation: #saturation, - can_always_eat: #can_always_eat, - } ) } - } - None => quote! { None }, - }; - tokens.extend(quote! { - ItemComponents { - item_name: #item_name, - max_stack_size: #max_stack_size, - jukebox_playable: #jukebox_playable, - damage: #damage, - max_damage: #max_damage, - attribute_modifiers: #attribute_modifiers, - tool: #tool, - food: #food - } + (MaxStackSize, &MaxStackSizeImpl { + size: #max_stack_size, + }), }); + if let Some(playable) = &self.jukebox_playable { + let song = LitStr::new(playable, Span::call_site()); + tokens.extend(quote! { + (JukeboxPlayable, &JukeboxPlayableImpl{ + song: #song, + }), + }); + } + + let TextContent::Translate { + translate: text, + with: _, + } = self.item_name.clone().0.content + else { + unreachable!() + }; + let item_name = LitStr::new(&text, Span::call_site()); + tokens.extend(quote! { + (ItemName, &ItemNameImpl { + name: #item_name, + }), + }); + + if let Some(d) = self.damage { + let damage_lit = LitInt::new(&d.to_string(), Span::call_site()); + tokens.extend(quote! { + (Damage, &DamageImpl { + damage: #damage_lit, + }), + }); + }; + + if let Some(md) = self.max_damage { + let max_damage_lit = LitInt::new(&md.to_string(), Span::call_site()); + tokens.extend(quote! { + (MaxDamage, &MaxDamageImpl { + max_damage: #max_damage_lit, + }), + }); + }; + + if let Some(modifiers) = &self.attribute_modifiers { + let modifier_code = modifiers.iter().map(|modifier| { + let r#type = format_ident!( + "{}", + modifier + .r#type + .strip_prefix("minecraft:") + .unwrap() + .to_uppercase() + ); + let id = LitStr::new(&modifier.id, Span::call_site()); + let amount = modifier.amount; + let operation = Ident::new(&format!("{:?}", modifier.operation), Span::call_site()); + let slot = modifier.slot.to_tokens(); + + quote! { + Modifier { + r#type: &Attributes::#r#type, + id: #id, + amount: #amount, + operation: Operation::#operation, + slot: #slot, + } + } + }); + tokens.extend(quote! { + (AttributeModifiers, &AttributeModifiersImpl { + attribute_modifiers: Cow::Borrowed(&[#(#modifier_code),*]) + }), + }); + }; + + if let Some(tool) = &self.tool { + let rules_code = tool.rules.iter().map(|rule| { + let block_array; + + if let RegistryEntryList::Single(t) = &rule.blocks { + if let TagType::Item(str) = t { + let ident = format_ident!( + "{}", + str.strip_prefix("minecraft:").unwrap().to_uppercase() + ); + block_array = quote! { + Blocks(Cow::Borrowed(&[&Block::#ident])) + } + } else if let TagType::Tag(str) = t { + let ident = format_ident!( + "{}", + str.replace(":", "_").replace("/", "_").to_uppercase() + ); + block_array = quote! { + Tag(&tag::Block::#ident) + } + } else { + unreachable!(); + } + } else if let RegistryEntryList::Many(t) = &rule.blocks { + let mut array = vec![]; + for i in t { + let TagType::Item(str) = i else { + unreachable!(); + }; + let ident = format_ident!( + "{}", + str.strip_prefix("minecraft:").unwrap().to_uppercase() + ); + array.push(quote! { + &Block::#ident + }); + } + block_array = quote! { + Blocks(Cow::Borrowed(&[#(#array),*])) + } + } else { + unreachable!(); + } + let speed = match rule.speed { + Some(speed) => { + quote! { Some(#speed) } + } + None => quote! { None }, + }; + let correct_for_drops = match rule.correct_for_drops { + Some(correct_for_drops) => { + let correct_for_drops = LitBool::new(correct_for_drops, Span::call_site()); + quote! { Some(#correct_for_drops) } + } + None => quote! { None }, + }; + quote! { + ToolRule { + blocks: #block_array, + speed: #speed, + correct_for_drops: #correct_for_drops + } + } + }); + let damage_per_block = { + let speed = LitInt::new(&tool.damage_per_block.to_string(), Span::call_site()); + quote! { #speed } + }; + let default_mining_speed = { + let speed = LitFloat::new( + &format!("{:.1}", tool.default_mining_speed), + Span::call_site(), + ); + quote! { #speed } + }; + let can_destroy_blocks_in_creative = + LitBool::new(tool.can_destroy_blocks_in_creative, Span::call_site()); + tokens.extend(quote! { (Tool, &ToolImpl { + rules: Cow::Borrowed(&[#(#rules_code),*]), + default_mining_speed: #default_mining_speed, + damage_per_block: #damage_per_block, + can_destroy_blocks_in_creative: #can_destroy_blocks_in_creative + }), }); + }; + + if let Some(food) = &self.food { + let nutrition = LitInt::new(&food.nutrition.to_string(), Span::call_site()); + let saturation = LitFloat::new(&format!("{:.1}", food.saturation), Span::call_site()); + let can_always_eat = { + let can = LitBool::new(food.can_always_eat, Span::call_site()); + quote! { #can } + }; + tokens.extend(quote! { (Food, &FoodImpl { + nutrition: #nutrition, + saturation: #saturation, + can_always_eat: #can_always_eat, + }), }); + }; } } + +fn return_1u32() -> u32 { + 1 +} + +fn return_1f32() -> f32 { + 1. +} + +fn return_true() -> bool { + true +} #[derive(Deserialize, Clone, Debug)] pub struct ToolComponent { rules: Vec, - default_mining_speed: Option, - damage_per_block: Option, + #[serde(default = "return_1f32")] + default_mining_speed: f32, + #[serde(default = "return_1u32")] + damage_per_block: u32, + #[serde(default = "return_true")] + can_destroy_blocks_in_creative: bool, +} + +fn return_false() -> bool { + false } #[derive(Deserialize, Copy, Clone, Debug)] pub struct FoodComponent { nutrition: u8, saturation: f32, - can_always_eat: Option, + #[serde(default = "return_false")] + can_always_eat: bool, } #[derive(Deserialize, Clone, Debug)] @@ -203,14 +259,14 @@ pub struct ToolRule { correct_for_drops: Option, } -#[derive(Deserialize, Clone, Debug)] +#[derive(Deserialize, Clone)] pub struct Modifier { pub r#type: String, pub id: String, pub amount: f64, pub operation: Operation, // TODO: Make this an enum - pub slot: String, + pub slot: AttributeModifierSlot, } #[derive(Deserialize, Clone, Debug, PartialEq)] @@ -240,13 +296,13 @@ pub(crate) fn build() -> TokenStream { let components = &item.components; let components_tokens = components.to_token_stream(); - let id_lit = LitInt::new(&item.id.to_string(), proc_macro2::Span::call_site()); + let id_lit = LitInt::new(&item.id.to_string(), Span::call_site()); constants.extend(quote! { pub const #const_ident: Item = Item { id: #id_lit, registry_key: #name, - components: #components_tokens + components: &[#components_tokens], }; }); @@ -260,15 +316,23 @@ pub(crate) fn build() -> TokenStream { } quote! { - use std::hash::{Hash, Hasher}; + use crate::data_component::DataComponent::*; + use crate::data_component_impl::*; + use crate::tag::{RegistryKey, Taggable}; use pumpkin_util::text::TextComponent; - use crate::tag::{Taggable, RegistryKey}; + use std::borrow::Cow; + use std::hash::{Hash, Hasher}; + use crate::{tag, AttributeModifierSlot}; + use crate::attributes::Attributes; + use crate::data_component_impl::IDSet::{Blocks, Tag}; + use crate::data_component::DataComponent; + use crate::Block; #[derive(Clone, Debug)] pub struct Item { pub id: u16, pub registry_key: &'static str, - pub components: ItemComponents, + pub components: &'static [(DataComponent, &'static dyn DataComponentImpl)], } impl PartialEq for Item { @@ -285,61 +349,21 @@ pub(crate) fn build() -> TokenStream { } } - #[derive(Clone, Copy, Debug)] - pub struct ItemComponents { - pub item_name: &'static str, - pub max_stack_size: u8, - pub jukebox_playable: Option<&'static str>, - pub damage: Option, - pub max_damage: Option, - pub attribute_modifiers: Option<&'static [Modifier]>, - pub tool: Option, - pub food: Option - } - - #[derive(Clone, Copy, Debug)] - pub struct Modifier { - pub r#type: &'static str, - pub id: &'static str, - pub amount: f64, - pub operation: Operation, - // TODO: Make this an enum - pub slot: &'static str, - } - - #[derive(Clone, Copy, Debug, PartialEq)] - pub enum Operation { - AddValue, - AddMultipliedBase, - AddMultipliedTotal, - } - - #[derive(Clone, Copy, Debug, PartialEq)] - pub struct ToolComponent { - pub rules: &'static [ToolRule], - pub default_mining_speed: Option, - pub damage_per_block: Option, - } - - #[derive(Clone, Copy, Debug, PartialEq)] - pub struct ToolRule { - pub blocks: &'static [&'static str], - pub speed: Option, - pub correct_for_drops: Option, - } - - #[derive(Clone, Copy, Debug, PartialEq)] - pub struct FoodComponent { - pub nutrition: u8, - pub saturation: f32, - pub can_always_eat: Option, - } - impl Item { #constants pub fn translated_name(&self) -> TextComponent { - TextComponent::text(self.components.item_name) + TextComponent::translate( + self.components + .iter() + .find_map(|(id, data)| if id == &ItemName { + Some(data.as_any().downcast_ref::().unwrap().name) + } else { + None + } + ).unwrap(), + &[], + ) } #[doc = "Try to parse an item from a resource location string."] diff --git a/pumpkin-data/src/data_component_impl.rs b/pumpkin-data/src/data_component_impl.rs new file mode 100644 index 000000000..1cade1f15 --- /dev/null +++ b/pumpkin-data/src/data_component_impl.rs @@ -0,0 +1,479 @@ +#![allow(dead_code)] + +use crate::attributes::Attributes; +use crate::data_component::DataComponent; +use crate::data_component::DataComponent::*; +use crate::tag::Tag; +use crate::{AttributeModifierSlot, Block}; +use pumpkin_util::text::TextComponent; +use std::any::Any; +use std::borrow::Cow; +use std::fmt::Debug; +use std::hash::Hash; + +pub trait DataComponentImpl: Send + Sync + Debug { + fn write_nbt(&self) { + todo!() + } + fn read_nbt(&self) { + todo!() + } + fn deserialize(&self) { + todo!() + } + fn serialize(&self) { + todo!() + } + fn get_enum() -> DataComponent + where + Self: Sized; + fn clone_dyn(&self) -> Box; + fn as_any(&self) -> &dyn Any; + fn as_mut_any(&mut self) -> &mut dyn Any; +} + +impl Clone for Box { + fn clone(&self) -> Self { + self.clone_dyn() + } +} + +pub fn get(value: &dyn DataComponentImpl) -> &T { + value.as_any().downcast_ref::().unwrap() +} +pub fn get_mut(value: &mut dyn DataComponentImpl) -> &mut T { + value.as_mut_any().downcast_mut::().unwrap() +} +#[derive(Clone, Debug, Hash)] +pub struct CustomDataImpl; +impl DataComponentImpl for CustomDataImpl { + fn get_enum() -> DataComponent + where + Self: Sized, + { + CustomData + } + + fn clone_dyn(&self) -> Box { + Box::new(self.clone()) + } + fn as_any(&self) -> &dyn Any { + self + } + fn as_mut_any(&mut self) -> &mut dyn Any { + self + } +} + +#[derive(Clone, Debug, Hash)] +pub struct MaxStackSizeImpl { + pub size: u8, +} +impl DataComponentImpl for MaxStackSizeImpl { + fn get_enum() -> DataComponent + where + Self: Sized, + { + MaxStackSize + } + fn clone_dyn(&self) -> Box { + Box::new(self.clone()) + } + fn as_any(&self) -> &dyn Any { + self + } + fn as_mut_any(&mut self) -> &mut dyn Any { + self + } +} +#[derive(Clone, Debug, Hash)] +pub struct MaxDamageImpl { + pub max_damage: i32, +} +impl DataComponentImpl for MaxDamageImpl { + fn get_enum() -> DataComponent + where + Self: Sized, + { + MaxDamage + } + fn clone_dyn(&self) -> Box { + Box::new(self.clone()) + } + fn as_any(&self) -> &dyn Any { + self + } + fn as_mut_any(&mut self) -> &mut dyn Any { + self + } +} +#[derive(Clone, Debug, Hash)] +pub struct DamageImpl { + pub damage: i32, +} +impl DataComponentImpl for DamageImpl { + fn get_enum() -> DataComponent + where + Self: Sized, + { + Damage + } + fn clone_dyn(&self) -> Box { + Box::new(self.clone()) + } + fn as_any(&self) -> &dyn Any { + self + } + fn as_mut_any(&mut self) -> &mut dyn Any { + self + } +} +#[derive(Clone, Debug, Hash)] +pub struct UnbreakableImpl; +#[derive(Clone, Debug, Hash)] +pub struct CustomNameImpl; +#[derive(Clone, Debug, Hash)] +pub struct ItemNameImpl { + // TODO make TextComponent const + pub name: &'static str, +} +impl DataComponentImpl for ItemNameImpl { + fn get_enum() -> DataComponent + where + Self: Sized, + { + ItemName + } + fn clone_dyn(&self) -> Box { + Box::new(self.clone()) + } + fn as_any(&self) -> &dyn Any { + self + } + fn as_mut_any(&mut self) -> &mut dyn Any { + self + } +} +#[derive(Clone, Debug, Hash)] +pub struct ItemModelImpl; +#[derive(Clone, Debug, Hash)] +pub struct LoreImpl; +#[derive(Clone, Debug, Hash)] +pub struct RarityImpl; +#[derive(Clone, Debug, Hash)] +pub struct EnchantmentsImpl; +#[derive(Clone, Debug, Hash)] +pub struct CanPlaceOnImpl; +#[derive(Clone, Debug, Hash)] +pub struct CanBreakImpl; + +#[derive(Clone, Copy, Debug, PartialEq, Hash)] +pub enum Operation { + AddValue, + AddMultipliedBase, + AddMultipliedTotal, +} +#[derive(Clone, Debug)] +pub struct Modifier { + pub r#type: &'static Attributes, + pub id: &'static str, + pub amount: f64, + pub operation: Operation, + pub slot: AttributeModifierSlot, +} +impl Hash for Modifier { + fn hash(&self, state: &mut H) { + self.r#type.hash(state); + self.id.hash(state); + unsafe { (*(&self.amount as *const f64 as *const u64)).hash(state) }; + self.operation.hash(state); + self.slot.hash(state); + } +} +#[derive(Clone, Debug, Hash)] +pub struct AttributeModifiersImpl { + pub attribute_modifiers: Cow<'static, [Modifier]>, +} +impl DataComponentImpl for AttributeModifiersImpl { + fn get_enum() -> DataComponent + where + Self: Sized, + { + AttributeModifiers + } + fn clone_dyn(&self) -> Box { + Box::new(self.clone()) + } + fn as_any(&self) -> &dyn Any { + self + } + fn as_mut_any(&mut self) -> &mut dyn Any { + self + } +} +#[derive(Clone, Debug, Hash)] +pub struct CustomModelDataImpl; +#[derive(Clone, Debug, Hash)] +pub struct TooltipDisplayImpl; +#[derive(Clone, Debug, Hash)] +pub struct RepairCostImpl; +#[derive(Clone, Debug, Hash)] +pub struct CreativeSlotLockImpl; +#[derive(Clone, Debug, Hash)] +pub struct EnchantmentGlintOverrideImpl; +#[derive(Clone, Debug, Hash)] +pub struct IntangibleProjectileImpl; +#[derive(Clone, Debug)] +pub struct FoodImpl { + pub nutrition: i32, + pub saturation: f32, + pub can_always_eat: bool, +} +impl DataComponentImpl for FoodImpl { + fn get_enum() -> DataComponent + where + Self: Sized, + { + Food + } + fn clone_dyn(&self) -> Box { + Box::new(self.clone()) + } + fn as_any(&self) -> &dyn Any { + self + } + fn as_mut_any(&mut self) -> &mut dyn Any { + self + } +} +impl Hash for FoodImpl { + fn hash(&self, state: &mut H) { + self.nutrition.hash(state); + unsafe { (*(&self.saturation as *const f32 as *const u32)).hash(state) }; + self.can_always_eat.hash(state); + } +} +#[derive(Clone, Debug, Hash)] +pub struct ConsumableImpl; +#[derive(Clone, Debug, Hash)] +pub struct UseRemainderImpl; +#[derive(Clone, Debug, Hash)] +pub struct UseCooldownImpl; +#[derive(Clone, Debug, Hash)] +pub struct DamageResistantImpl; + +#[derive(Clone, Debug, Hash)] +pub enum IDSet { + Tag(&'static Tag), + Blocks(Cow<'static, [&'static Block]>), +} + +#[derive(Clone, Debug)] +pub struct ToolRule { + pub blocks: IDSet, + pub speed: Option, + pub correct_for_drops: Option, +} +impl Hash for ToolRule { + fn hash(&self, state: &mut H) { + self.blocks.hash(state); + if let Some(val) = self.speed { + true.hash(state); + unsafe { (*(&val as *const f32 as *const u32)).hash(state) }; + } else { + false.hash(state); + } + self.correct_for_drops.hash(state); + } +} +#[derive(Clone, Debug)] +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 { + fn get_enum() -> DataComponent + where + Self: Sized, + { + Tool + } + fn clone_dyn(&self) -> Box { + Box::new(self.clone()) + } + fn as_any(&self) -> &dyn Any { + self + } + fn as_mut_any(&mut self) -> &mut dyn Any { + self + } +} +impl Hash for ToolImpl { + fn hash(&self, state: &mut H) { + self.rules.hash(state); + unsafe { (*(&self.default_mining_speed as *const f32 as *const u32)).hash(state) }; + self.damage_per_block.hash(state); + self.can_destroy_blocks_in_creative.hash(state); + } +} +#[derive(Clone, Debug, Hash)] +pub struct WeaponImpl; +#[derive(Clone, Debug, Hash)] +pub struct EnchantableImpl; +#[derive(Clone, Debug, Hash)] +pub struct EquippableImpl; +#[derive(Clone, Debug, Hash)] +pub struct RepairableImpl; +#[derive(Clone, Debug, Hash)] +pub struct GliderImpl; +#[derive(Clone, Debug, Hash)] +pub struct TooltipStyleImpl; +#[derive(Clone, Debug, Hash)] +pub struct DeathProtectionImpl; +#[derive(Clone, Debug, Hash)] +pub struct BlocksAttacksImpl; +#[derive(Clone, Debug, Hash)] +pub struct StoredEnchantmentsImpl; +#[derive(Clone, Debug, Hash)] +pub struct DyedColorImpl; +#[derive(Clone, Debug, Hash)] +pub struct MapColorImpl; +#[derive(Clone, Debug, Hash)] +pub struct MapIdImpl; +#[derive(Clone, Debug, Hash)] +pub struct MapDecorationsImpl; +#[derive(Clone, Debug, Hash)] +pub struct MapPostProcessingImpl; +#[derive(Clone, Debug, Hash)] +pub struct ChargedProjectilesImpl; +#[derive(Clone, Debug, Hash)] +pub struct BundleContentsImpl; +#[derive(Clone, Debug, Hash)] +pub struct PotionContentsImpl; +#[derive(Clone, Debug, Hash)] +pub struct PotionDurationScaleImpl; +#[derive(Clone, Debug, Hash)] +pub struct SuspiciousStewEffectsImpl; +#[derive(Clone, Debug, Hash)] +pub struct WritableBookContentImpl; +#[derive(Clone, Debug, Hash)] +pub struct WrittenBookContentImpl; +#[derive(Clone, Debug, Hash)] +pub struct TrimImpl; +#[derive(Clone, Debug, Hash)] +pub struct DebugStickStateImpl; +#[derive(Clone, Debug, Hash)] +pub struct EntityDataImpl; +#[derive(Clone, Debug, Hash)] +pub struct BucketEntityDataImpl; +#[derive(Clone, Debug, Hash)] +pub struct BlockEntityDataImpl; +#[derive(Clone, Debug, Hash)] +pub struct InstrumentImpl; +#[derive(Clone, Debug, Hash)] +pub struct ProvidesTrimMaterialImpl; +#[derive(Clone, Debug, Hash)] +pub struct OminousBottleAmplifierImpl; +#[derive(Clone, Debug, Hash)] +pub struct JukeboxPlayableImpl { + pub song: &'static str, +} +impl DataComponentImpl for JukeboxPlayableImpl { + fn get_enum() -> DataComponent + where + Self: Sized, + { + JukeboxPlayable + } + fn clone_dyn(&self) -> Box { + Box::new(self.clone()) + } + fn as_any(&self) -> &dyn Any { + self + } + fn as_mut_any(&mut self) -> &mut dyn Any { + self + } +} +#[derive(Clone, Debug, Hash)] +pub struct ProvidesBannerPatternsImpl; +#[derive(Clone, Debug, Hash)] +pub struct RecipesImpl; +#[derive(Clone, Debug, Hash)] +pub struct LodestoneTrackerImpl; +#[derive(Clone, Debug, Hash)] +pub struct FireworkExplosionImpl; +#[derive(Clone, Debug, Hash)] +pub struct FireworksImpl; +#[derive(Clone, Debug, Hash)] +pub struct ProfileImpl; +#[derive(Clone, Debug, Hash)] +pub struct NoteBlockSoundImpl; +#[derive(Clone, Debug, Hash)] +pub struct BannerPatternsImpl; +#[derive(Clone, Debug, Hash)] +pub struct BaseColorImpl; +#[derive(Clone, Debug, Hash)] +pub struct PotDecorationsImpl; +#[derive(Clone, Debug, Hash)] +pub struct ContainerImpl; +#[derive(Clone, Debug, Hash)] +pub struct BlockStateImpl; +#[derive(Clone, Debug, Hash)] +pub struct BeesImpl; +#[derive(Clone, Debug, Hash)] +pub struct LockImpl; +#[derive(Clone, Debug, Hash)] +pub struct ContainerLootImpl; +#[derive(Clone, Debug, Hash)] +pub struct BreakSoundImpl; +#[derive(Clone, Debug, Hash)] +pub struct VillagerVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct WolfVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct WolfSoundVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct WolfCollarImpl; +#[derive(Clone, Debug, Hash)] +pub struct FoxVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct SalmonSizeImpl; +#[derive(Clone, Debug, Hash)] +pub struct ParrotVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct TropicalFishPatternImpl; +#[derive(Clone, Debug, Hash)] +pub struct TropicalFishBaseColorImpl; +#[derive(Clone, Debug, Hash)] +pub struct TropicalFishPatternColorImpl; +#[derive(Clone, Debug, Hash)] +pub struct MooshroomVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct RabbitVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct PigVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct CowVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct ChickenVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct FrogVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct HorseVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct PaintingVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct LlamaVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct AxolotlVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct CatVariantImpl; +#[derive(Clone, Debug, Hash)] +pub struct CatCollarImpl; +#[derive(Clone, Debug, Hash)] +pub struct SheepColorImpl; +#[derive(Clone, Debug, Hash)] +pub struct ShulkerColorImpl; diff --git a/pumpkin-data/src/lib.rs b/pumpkin-data/src/lib.rs index 017be9b5f..27dfb21ca 100644 --- a/pumpkin-data/src/lib.rs +++ b/pumpkin-data/src/lib.rs @@ -28,6 +28,15 @@ mod sound_enum; #[path = "generated/recipes.rs"] pub mod recipes; +#[rustfmt::skip] +#[path = "generated/data_component.rs"] +pub mod data_component; +pub mod data_component_impl; + +#[rustfmt::skip] +#[path = "generated/attributes.rs"] +pub mod attributes; + pub mod sound { pub use crate::sound_category::*; pub use crate::sound_enum::*; diff --git a/pumpkin-inventory/src/crafting/crafting_inventory.rs b/pumpkin-inventory/src/crafting/crafting_inventory.rs index bbdaedb82..b1ad9ad52 100644 --- a/pumpkin-inventory/src/crafting/crafting_inventory.rs +++ b/pumpkin-inventory/src/crafting/crafting_inventory.rs @@ -25,7 +25,7 @@ impl CraftingInventory { // Creates a Vec with different Mutexes for each slot let mut v = Vec::with_capacity(width as usize * height as usize); (0..width as usize * height as usize) - .for_each(|_| v.push(Arc::new(Mutex::new(ItemStack::EMPTY)))); + .for_each(|_| v.push(Arc::new(Mutex::new(ItemStack::EMPTY.clone())))); v }, } @@ -53,7 +53,7 @@ impl Inventory for CraftingInventory { } async fn remove_stack(&self, slot: usize) -> ItemStack { - let mut removed = ItemStack::EMPTY; + let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.items[slot].lock().await; std::mem::swap(&mut removed, &mut *guard); removed @@ -86,7 +86,7 @@ impl RecipeInputInventory for CraftingInventory { impl Clearable for CraftingInventory { async fn clear(&self) { for slot in self.items.iter() { - *slot.lock().await = ItemStack::EMPTY; + *slot.lock().await = ItemStack::EMPTY.clone(); } } } diff --git a/pumpkin-inventory/src/crafting/crafting_screen_handler.rs b/pumpkin-inventory/src/crafting/crafting_screen_handler.rs index 8af13fb82..34c617430 100644 --- a/pumpkin-inventory/src/crafting/crafting_screen_handler.rs +++ b/pumpkin-inventory/src/crafting/crafting_screen_handler.rs @@ -239,7 +239,7 @@ impl ResultSlot { Self { inventory, id: AtomicU8::new(0), - result: Arc::new(Mutex::new(ItemStack::EMPTY)), + result: Arc::new(Mutex::new(ItemStack::EMPTY.clone())), recipe_cache: AtomicCell::new(None), } } @@ -316,8 +316,8 @@ impl ResultSlot { .match_recipe() .await .map(|x| ItemStack::from(x.0)) - .unwrap_or(ItemStack::EMPTY); - *self.result.lock().await = result; + .unwrap_or(ItemStack::EMPTY.clone()); + *self.result.lock().await = result.clone(); result } } @@ -363,7 +363,7 @@ impl Slot for ResultSlot { } async fn get_cloned_stack(&self) -> ItemStack { - *self.result.lock().await + self.result.lock().await.clone() } async fn has_stack(&self) -> bool { @@ -399,9 +399,9 @@ impl Slot for ResultSlot { let stack = self.result.lock().await; // Vanilla: net.minecraft.world.inventory.ResultContainer#removeItem // Regardless of the amount, we always return the full stack - *stack + stack.clone() } else { - ItemStack::EMPTY + ItemStack::EMPTY.clone() } } } @@ -508,17 +508,17 @@ impl ScreenHandler for CraftingTableScreenHandler { if slot.has_stack().await { let slot_stack = slot.get_stack().await; let mut slot_stack = slot_stack.lock().await; - let stack_prev = *slot_stack; + let stack_prev = slot_stack.clone(); if slot_index == 0 { // From crafting result slot - move to player inventory (slots 10-46) if !self.insert_item(&mut slot_stack, 10, 46, true).await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } } else if (1..=9).contains(&slot_index) { // From crafting input slots - try to move to player inventory (slots 10-46) if !self.insert_item(&mut slot_stack, 10, 46, false).await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } } else if (10..46).contains(&slot_index) { // From player inventory - try to move to crafting input slots first (1-9) @@ -527,40 +527,42 @@ impl ScreenHandler for CraftingTableScreenHandler { if slot_index < 37 { // From main inventory to hotbar if !self.insert_item(&mut slot_stack, 37, 46, false).await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } } else { // From hotbar to main inventory if !self.insert_item(&mut slot_stack, 10, 37, false).await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } } } } else { // Any other slot - try to move to player inventory if !self.insert_item(&mut slot_stack, 10, 46, false).await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } } - let stack = *slot_stack; + let stack = slot_stack.clone(); drop(slot_stack); // release the lock before calling other methods if stack.is_empty() { - slot.set_stack_prev(ItemStack::EMPTY, stack_prev).await; + slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone()) + .await; } else { slot.mark_dirty().await; } if stack.item_count == stack_prev.item_count { // Nothing changed - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } slot.on_take_item(player, &stack).await; if slot_index == 0 { - slot.on_quick_move_crafted(stack, stack_prev).await; + slot.on_quick_move_crafted(stack.clone(), stack_prev.clone()) + .await; // For crafting result slot, drop any remaining items if !stack.is_empty() { player.drop_item(stack, false).await; @@ -570,7 +572,7 @@ impl ScreenHandler for CraftingTableScreenHandler { return stack_prev; } - ItemStack::EMPTY + ItemStack::EMPTY.clone() } } diff --git a/pumpkin-inventory/src/entity_equipment.rs b/pumpkin-inventory/src/entity_equipment.rs index 34050070a..ef9ae3964 100644 --- a/pumpkin-inventory/src/entity_equipment.rs +++ b/pumpkin-inventory/src/entity_equipment.rs @@ -25,19 +25,19 @@ impl EntityEquipment { } pub async fn put(&mut self, slot: &EquipmentSlot, stack: ItemStack) -> ItemStack { - *self - .equipment + self.equipment .insert(slot.clone(), Arc::new(Mutex::new(stack))) - .unwrap_or(Arc::new(Mutex::new(ItemStack::EMPTY))) + .unwrap_or(Arc::new(Mutex::new(ItemStack::EMPTY.clone()))) .lock() .await + .clone() } pub fn get(&self, slot: &EquipmentSlot) -> Arc> { self.equipment .get(slot) .cloned() - .unwrap_or(Arc::new(Mutex::new(ItemStack::EMPTY))) + .unwrap_or(Arc::new(Mutex::new(ItemStack::EMPTY.clone()))) } pub async fn is_empty(&self) -> bool { diff --git a/pumpkin-inventory/src/furnace/furnace_screen_handler.rs b/pumpkin-inventory/src/furnace/furnace_screen_handler.rs index 0fd11addb..3c9ede651 100644 --- a/pumpkin-inventory/src/furnace/furnace_screen_handler.rs +++ b/pumpkin-inventory/src/furnace/furnace_screen_handler.rs @@ -106,7 +106,7 @@ impl ScreenHandler for FurnaceScreenHandler { async fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { const FUEL_SLOT: i32 = 1; - let mut stack_left = ItemStack::EMPTY; + let mut stack_left = ItemStack::EMPTY.clone(); let slot = self.get_behaviour().slots[slot_index as usize].clone(); if !slot.has_stack().await { @@ -115,7 +115,7 @@ impl ScreenHandler for FurnaceScreenHandler { let slot_stack = slot.get_stack().await; let mut stack = slot_stack.lock().await; - stack_left = *stack; + stack_left = stack.clone(); let success = if slot_index < 3 { self.insert_item(&mut stack, 3, self.get_behaviour().slots.len() as i32, true) @@ -127,12 +127,12 @@ impl ScreenHandler for FurnaceScreenHandler { }; if !success { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } if stack.is_empty() { drop(stack); - slot.set_stack(ItemStack::EMPTY).await; + slot.set_stack(ItemStack::EMPTY.clone()).await; } else { slot.mark_dirty().await; } diff --git a/pumpkin-inventory/src/generic_container_screen_handler.rs b/pumpkin-inventory/src/generic_container_screen_handler.rs index 7d4bf4590..736a2e381 100644 --- a/pumpkin-inventory/src/generic_container_screen_handler.rs +++ b/pumpkin-inventory/src/generic_container_screen_handler.rs @@ -118,12 +118,12 @@ impl ScreenHandler for GenericContainerScreenHandler { } async fn quick_move(&mut self, _player: &dyn InventoryPlayer, slot_index: i32) -> ItemStack { - let mut stack_left = ItemStack::EMPTY; + 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 = slot.get_stack().await; - stack_left = *slot_stack.lock().await; + stack_left = slot_stack.lock().await.clone(); if slot_index < (self.rows * 9) as i32 { if !self @@ -135,7 +135,7 @@ impl ScreenHandler for GenericContainerScreenHandler { ) .await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } } else if !self .insert_item( @@ -146,11 +146,11 @@ impl ScreenHandler for GenericContainerScreenHandler { ) .await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } if stack_left.is_empty() { - slot.set_stack(ItemStack::EMPTY).await; + slot.set_stack(ItemStack::EMPTY.clone()).await; } else { slot.mark_dirty().await; } diff --git a/pumpkin-inventory/src/player/player_inventory.rs b/pumpkin-inventory/src/player/player_inventory.rs index b67b5c388..6c3d96abe 100644 --- a/pumpkin-inventory/src/player/player_inventory.rs +++ b/pumpkin-inventory/src/player/player_inventory.rs @@ -30,7 +30,7 @@ impl PlayerInventory { pub fn new(entity_equipment: Arc>) -> Self { Self { // Normal syntax can't be used here because Arc doesn't implement Copy - main_inventory: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + main_inventory: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), equipment_slots: Self::build_equipment_slots(), selected_slot: AtomicU8::new(0), entity_equipment, @@ -62,9 +62,9 @@ impl PlayerInventory { let mut equipment = self.entity_equipment.lock().await; let binding = self.held_item(); let mut main_hand_item = binding.lock().await; - let off_hand_item = *main_hand_item; - *main_hand_item = equipment.put(slot, off_hand_item).await; - (*main_hand_item, off_hand_item) + let off_hand_item = main_hand_item.clone(); + *main_hand_item = equipment.put(slot, off_hand_item.clone()).await; + (main_hand_item.clone(), off_hand_item) } pub fn is_valid_hotbar_index(slot: usize) -> bool { @@ -188,9 +188,9 @@ impl PlayerInventory { loop { i = stack.item_count; if slot == -1 { - stack.set_count(self.add_stack(*stack).await as u8); + stack.set_count(self.add_stack(stack.clone()).await as u8); } else { - stack.set_count(self.add_stack_to_slot(slot as usize, *stack).await as u8); + stack.set_count(self.add_stack_to_slot(slot as usize, stack.clone()).await as u8); } if stack.is_empty() || stack.item_count >= i { @@ -251,9 +251,10 @@ impl PlayerInventory { if empty_slot != -1 { self.set_stack( empty_slot as usize, - *self.main_inventory[self.get_selected_slot() as usize] + self.main_inventory[self.get_selected_slot() as usize] .lock() - .await, + .await + .clone(), ) .await; } @@ -265,12 +266,13 @@ impl PlayerInventory { pub async fn swap_slot_with_hotbar(&self, slot: usize) { self.set_selected_slot(self.get_swappable_hotbar_slot().await as u8); - let stack = *self.main_inventory[self.get_selected_slot() as usize] + let stack = self.main_inventory[self.get_selected_slot() as usize] .lock() - .await; + .await + .clone(); self.set_stack( self.get_selected_slot() as usize, - *self.main_inventory[slot].lock().await, + self.main_inventory[slot].lock().await.clone(), ) .await; self.set_stack(slot, stack).await; @@ -308,7 +310,7 @@ impl PlayerInventory { player .enqueue_slot_set_packet(&CSetPlayerInventory::new( (room_for_stack as i32).into(), - &stack.into(), + &stack.clone().into(), )) .await; } @@ -320,7 +322,7 @@ impl PlayerInventory { impl Clearable for PlayerInventory { async fn clear(&self) { for item in self.main_inventory.iter() { - *item.lock().await = ItemStack::EMPTY; + *item.lock().await = ItemStack::EMPTY.clone(); } self.entity_equipment.lock().await.clear(); @@ -379,13 +381,13 @@ impl Inventory for PlayerInventory { return stack.split(amount); } - ItemStack::EMPTY + ItemStack::EMPTY.clone() } } async fn remove_stack(&self, slot: usize) -> ItemStack { if slot < self.main_inventory.len() { - let mut removed = ItemStack::EMPTY; + let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.main_inventory[slot].lock().await; std::mem::swap(&mut removed, &mut *guard); removed @@ -394,7 +396,7 @@ impl Inventory for PlayerInventory { self.entity_equipment .lock() .await - .put(slot, ItemStack::EMPTY) + .put(slot, ItemStack::EMPTY.clone()) .await } } diff --git a/pumpkin-inventory/src/player/player_screen_handler.rs b/pumpkin-inventory/src/player/player_screen_handler.rs index 1960d54b7..ee2474036 100644 --- a/pumpkin-inventory/src/player/player_screen_handler.rs +++ b/pumpkin-inventory/src/player/player_screen_handler.rs @@ -97,7 +97,7 @@ impl PlayerScreenHandler { if !slot_stack.is_empty() && self.try_move_one_to_armor_slot(slot_stack).await { slot_stack.item_count -= 1; if slot_stack.item_count == 0 { - *slot_stack = ItemStack::EMPTY; + *slot_stack = ItemStack::EMPTY.clone(); } } @@ -145,45 +145,46 @@ impl ScreenHandler for PlayerScreenHandler { if slot.has_stack().await { let slot_stack = slot.get_stack().await; let mut slot_stack = slot_stack.lock().await; - let stack_prev = *slot_stack; + let stack_prev = slot_stack.clone(); #[allow(clippy::if_same_then_else)] if slot_index == 0 { // From crafting result slot if !self.insert_item(&mut slot_stack, 9, 45, true).await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } } else if (1..5).contains(&slot_index) { // From craft ingredient slots if !self.insert_item(&mut slot_stack, 9, 45, false).await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } } else if (5..9).contains(&slot_index) { // From armour slots if !self.insert_item(&mut slot_stack, 9, 45, false).await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } } else if (9..45).contains(&slot_index) { if !self .handle_inventory_move(slot_index, &mut slot_stack) .await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } } else if !self.insert_item(&mut slot_stack, 9, 45, false).await { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } - let stack = *slot_stack; + let stack = slot_stack.clone(); drop(slot_stack); // release the lock before calling other methods if stack.is_empty() { - slot.set_stack_prev(ItemStack::EMPTY, stack_prev).await; + slot.set_stack_prev(ItemStack::EMPTY.clone(), stack_prev.clone()) + .await; } else { slot.mark_dirty().await; } if stack.item_count == stack_prev.item_count { - return ItemStack::EMPTY; + return ItemStack::EMPTY.clone(); } slot.on_take_item(player, &stack).await; @@ -191,7 +192,8 @@ impl ScreenHandler for PlayerScreenHandler { if slot_index == 0 { // From crafting result slot // Notify the result slot to refill - slot.on_quick_move_crafted(stack, stack_prev).await; + slot.on_quick_move_crafted(stack.clone(), stack_prev.clone()) + .await; // For crafting result slot, drop any remaining items if !stack.is_empty() { player.drop_item(stack, false).await; @@ -202,6 +204,6 @@ impl ScreenHandler for PlayerScreenHandler { } // Nothing changed - ItemStack::EMPTY + ItemStack::EMPTY.clone() } } diff --git a/pumpkin-inventory/src/screen_handler.rs b/pumpkin-inventory/src/screen_handler.rs index 680edc089..464ee66d4 100644 --- a/pumpkin-inventory/src/screen_handler.rs +++ b/pumpkin-inventory/src/screen_handler.rs @@ -104,8 +104,8 @@ pub trait ScreenHandler: Send + Sync { async fn default_on_closed(&mut self, player: &dyn InventoryPlayer) { let behaviour = self.get_behaviour_mut(); if !behaviour.cursor_stack.lock().await.is_empty() { - offer_or_drop_stack(player, *behaviour.cursor_stack.lock().await).await; - *behaviour.cursor_stack.lock().await = ItemStack::EMPTY; + offer_or_drop_stack(player, behaviour.cursor_stack.lock().await.clone()).await; + *behaviour.cursor_stack.lock().await = ItemStack::EMPTY.clone(); } } @@ -127,7 +127,7 @@ pub trait ScreenHandler: Send + Sync { let behaviour = self.get_behaviour_mut(); slot.set_id(behaviour.slots.len()); behaviour.slots.push(slot.clone()); - behaviour.tracked_stacks.push(ItemStack::EMPTY); + behaviour.tracked_stacks.push(ItemStack::EMPTY.clone()); behaviour.previous_tracked_stacks.push(TrackedStack::EMPTY); slot @@ -178,7 +178,7 @@ pub trait ScreenHandler: Send + Sync { if let Some(hash_map) = table.get(&ComparableInventory(inventory.clone())) { if let Some(other_index) = hash_map.get(&index) { self.get_behaviour_mut().tracked_stacks[i] = - other_behaviour.tracked_stacks[*other_index]; + other_behaviour.tracked_stacks[*other_index].clone(); self.get_behaviour_mut().previous_tracked_stacks[i] = other_behaviour.previous_tracked_stacks[*other_index].clone(); } @@ -215,14 +215,14 @@ pub trait ScreenHandler: Send + Sync { for i in 0..behaviour.slots.len() { let stack = behaviour.slots[i].get_cloned_stack().await; - previous_tracked_stacks.push(stack); + previous_tracked_stacks.push(stack.clone()); behaviour.previous_tracked_stacks[i].set_received_stack(stack); } - let cursor_stack = *behaviour.cursor_stack.lock().await; + let cursor_stack = behaviour.cursor_stack.lock().await.clone(); behaviour .previous_cursor_stack - .set_received_stack(cursor_stack); + .set_received_stack(cursor_stack.clone()); for i in 0..behaviour.properties.len() { let property_val = behaviour.properties[i].get(); @@ -325,10 +325,12 @@ pub trait ScreenHandler: Send + Sync { let behaviour = self.get_behaviour_mut(); let other_stack = &behaviour.tracked_stacks[slot]; if !other_stack.are_equal(&stack) { - behaviour.tracked_stacks[slot] = stack; + behaviour.tracked_stacks[slot] = stack.clone(); for listener in behaviour.listeners.iter() { - listener.on_slot_update(behaviour, slot as u8, stack).await; + listener + .on_slot_update(behaviour, slot as u8, stack.clone()) + .await; } } } @@ -339,7 +341,7 @@ pub trait ScreenHandler: Send + Sync { let prev_stack = &mut behaviour.previous_tracked_stacks[slot]; if !prev_stack.is_in_sync(&stack) { - prev_stack.set_received_stack(stack); + prev_stack.set_received_stack(stack.clone()); let next_revision = behaviour.next_revision(); if let Some(sync_handler) = behaviour.sync_handler.as_ref() { sync_handler @@ -357,7 +359,7 @@ pub trait ScreenHandler: Send + Sync { if !behaviour.previous_cursor_stack.is_in_sync(&cursor_stack) { behaviour .previous_cursor_stack - .set_received_stack(*cursor_stack); + .set_received_stack(cursor_stack.clone()); if let Some(sync_handler) = behaviour.sync_handler.as_ref() { sync_handler .update_cursor_stack(behaviour, &cursor_stack) @@ -374,7 +376,7 @@ pub trait ScreenHandler: Send + Sync { let slot = self.get_behaviour().slots[i].clone(); let stack = slot.get_cloned_stack().await; - self.update_tracked_slot(i, stack).await; + self.update_tracked_slot(i, stack.clone()).await; self.check_slot_updates(i, stack).await; } @@ -625,7 +627,7 @@ pub trait ScreenHandler: Send + Sync { )) .min(cursor_stack.item_count); if inserting_count > 0 { - let mut stack_clone = *stack; + let mut stack_clone = stack.clone(); drop(stack); if stack_clone.is_empty() { stack_clone = cursor_stack.copy_with_count(0); @@ -694,8 +696,8 @@ pub trait ScreenHandler: Send + Sync { let mut cursor_stack = self.get_behaviour().cursor_stack.lock().await; if !cursor_stack.is_empty() { if click_type == MouseClick::Left { - player.drop_item(*cursor_stack, true).await; - *cursor_stack = ItemStack::EMPTY; + player.drop_item(cursor_stack.clone(), true).await; + *cursor_stack = ItemStack::EMPTY.clone(); } else { player.drop_item(cursor_stack.split(1), true).await; } @@ -741,8 +743,8 @@ pub trait ScreenHandler: Send + Sync { player, click_type.clone(), slot.clone(), - slot_stack, - *cursor_stack, + slot_stack.clone(), + cursor_stack.clone(), ) .await { @@ -756,8 +758,9 @@ pub trait ScreenHandler: Send + Sync { } else { 1 }; - *cursor_stack = - slot.insert_stack_count(*cursor_stack, transfer_count).await; + *cursor_stack = slot + .insert_stack_count(cursor_stack.clone(), transfer_count) + .await; } } else if slot.can_take_items(player).await { if cursor_stack.is_empty() { @@ -769,7 +772,7 @@ pub trait ScreenHandler: Send + Sync { let taken = slot.try_take_stack_range(take_count, u8::MAX, player).await; if let Some(taken) = taken { // Reverse order of operations, shouldn't affect anything - *cursor_stack = taken; + *cursor_stack = taken.clone(); slot.on_take_item(player, &taken).await; } } else if slot.can_insert(&cursor_stack).await { @@ -779,13 +782,14 @@ pub trait ScreenHandler: Send + Sync { } else { 1 }; - *cursor_stack = - slot.insert_stack_count(*cursor_stack, insert_count).await; + *cursor_stack = slot + .insert_stack_count(cursor_stack.clone(), insert_count) + .await; } else if cursor_stack.item_count <= slot.get_max_item_count_for_stack(&cursor_stack).await { - let old_cursor_stack = *cursor_stack; - *cursor_stack = slot_stack; + let old_cursor_stack = cursor_stack.clone(); + *cursor_stack = slot_stack.clone(); slot.set_stack(old_cursor_stack).await; } } else if ItemStack::are_items_and_components_equal(&slot_stack, &cursor_stack) @@ -810,12 +814,13 @@ pub trait ScreenHandler: Send + Sync { slot.mark_dirty().await; } } else if action_type == SlotActionType::Swap && (0..9).contains(&button) || button == 40 { - let mut button_stack = *player + let mut button_stack = player .get_inventory() .get_stack(button as usize) .await .lock() - .await; + .await + .clone(); let source_slot = self.get_behaviour().slots[slot_index as usize].clone(); let source_stack = source_slot.get_cloned_stack().await; @@ -824,9 +829,9 @@ pub trait ScreenHandler: Send + Sync { if source_slot.can_take_items(player).await { player .get_inventory() - .set_stack(button as usize, source_stack) + .set_stack(button as usize, source_stack.clone()) .await; - source_slot.set_stack(ItemStack::EMPTY).await; + source_slot.set_stack(ItemStack::EMPTY.clone()).await; source_slot.on_take_item(player, &source_stack).await; } } else if source_stack.is_empty() { @@ -840,7 +845,7 @@ pub trait ScreenHandler: Send + Sync { } else { player .get_inventory() - .set_stack(button as usize, ItemStack::EMPTY) + .set_stack(button as usize, ItemStack::EMPTY.clone()) .await; source_slot.set_stack(button_stack).await; } @@ -866,7 +871,7 @@ pub trait ScreenHandler: Send + Sync { .get_inventory() .set_stack(button as usize, source_stack) .await; - source_slot.set_stack(button_stack).await; + source_slot.set_stack(button_stack.clone()).await; source_slot.on_take_item(player, &button_stack).await; } } @@ -940,7 +945,7 @@ impl ScreenHandlerBehaviour { listeners: Vec::new(), sync_handler: None, tracked_stacks: Vec::new(), - cursor_stack: Arc::new(Mutex::new(ItemStack::EMPTY)), + cursor_stack: Arc::new(Mutex::new(ItemStack::EMPTY.clone())), previous_tracked_stacks: Vec::new(), previous_cursor_stack: TrackedStack::EMPTY, revision: AtomicU32::new(0), diff --git a/pumpkin-inventory/src/slot.rs b/pumpkin-inventory/src/slot.rs index a29f3cc60..dbce55abf 100644 --- a/pumpkin-inventory/src/slot.rs +++ b/pumpkin-inventory/src/slot.rs @@ -57,7 +57,7 @@ pub trait Slot: Send + Sync + Debug { .await .expect("Timed out while trying to acquire lock"); - *lock + lock.clone() } async fn has_stack(&self) -> bool { @@ -138,7 +138,8 @@ pub trait Slot: Send + Sync + Debug { None } else { if self.get_cloned_stack().await.is_empty() { - self.set_stack_prev(ItemStack::EMPTY, stack).await; + self.set_stack_prev(ItemStack::EMPTY.clone(), stack.clone()) + .await; } Some(stack) @@ -156,7 +157,7 @@ pub trait Slot: Send + Sync + Debug { self.on_take_item(player, stack).await; } - stack.unwrap_or(ItemStack::EMPTY) + stack.unwrap_or(ItemStack::EMPTY.clone()) } async fn insert_stack(&self, stack: ItemStack) -> ItemStack { @@ -181,7 +182,7 @@ pub trait Slot: Send + Sync + Debug { } else if stack.are_items_and_components_equal(&stack_self) { stack.decrement(min_count); stack_self.increment(min_count); - let cloned_stack = *stack_self; + let cloned_stack = stack_self.clone(); drop(stack_self); self.set_stack(cloned_stack).await; } diff --git a/pumpkin-inventory/src/sync_handler.rs b/pumpkin-inventory/src/sync_handler.rs index 945aba5c0..1507d6127 100644 --- a/pumpkin-inventory/src/sync_handler.rs +++ b/pumpkin-inventory/src/sync_handler.rs @@ -50,10 +50,10 @@ impl SyncHandler { VarInt(next_revision as i32), stacks .iter() - .map(|stack| ItemStackSerializer::from(*stack)) + .map(|stack| ItemStackSerializer::from(stack.clone())) .collect::>() .as_slice(), - &ItemStackSerializer::from(*cursor_stack), + &ItemStackSerializer::from(cursor_stack.clone()), )) .await; @@ -82,7 +82,7 @@ impl SyncHandler { screen_handler.sync_id as i8, next_revision as i32, slot as i16, - &ItemStackSerializer::from(*stack), + &ItemStackSerializer::from(stack.clone()), )) .await; } @@ -95,7 +95,9 @@ impl SyncHandler { ) { if let Some(player) = self.player.lock().await.as_ref() { player - .enqueue_cursor_packet(&CSetCursorItem::new(&ItemStackSerializer::from(*stack))) + .enqueue_cursor_packet(&CSetCursorItem::new(&ItemStackSerializer::from( + stack.clone(), + ))) .await; } } @@ -146,7 +148,7 @@ impl TrackedStack { return stack.are_equal(actual_stack); } else if let Some(hash) = &self.received_hash { if hash.hash_equals(actual_stack) { - self.received_stack = Some(*actual_stack); + self.received_stack = Some(actual_stack.clone()); return true; } } diff --git a/pumpkin-protocol/src/codec/item_stack_seralizer.rs b/pumpkin-protocol/src/codec/item_stack_seralizer.rs index 02dacaa17..c6363bfa1 100644 --- a/pumpkin-protocol/src/codec/item_stack_seralizer.rs +++ b/pumpkin-protocol/src/codec/item_stack_seralizer.rs @@ -1,7 +1,7 @@ +use pumpkin_data::item::Item; use std::borrow::Cow; use crate::VarInt; -use pumpkin_data::item::Item; use pumpkin_world::item::ItemStack; use serde::{ Deserialize, Serialize, Serializer, @@ -27,7 +27,7 @@ impl<'de> Deserialize<'de> for ItemStackSerializer<'static> { .ok_or(de::Error::custom("Failed to decode VarInt"))?; let slot = if item_count.0 == 0 { - ItemStackSerializer(Cow::Borrowed(&ItemStack::EMPTY)) + ItemStackSerializer(Cow::Borrowed(ItemStack::EMPTY)) } else { let item_id = seq .next_element::()? @@ -107,7 +107,7 @@ impl From> for ItemStackSerializer<'_> { fn from(item: Option) -> Self { match item { Some(item) => ItemStackSerializer::from(item), - None => ItemStackSerializer(Cow::Borrowed(&ItemStack::EMPTY)), + None => ItemStackSerializer(Cow::Borrowed(ItemStack::EMPTY)), } } } diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index 5ea806279..c27288218 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -2,6 +2,7 @@ name = "pumpkin-world" version.workspace = true edition.workspace = true +rust-version = "1.88" [dependencies] pumpkin-nbt = { path = "../pumpkin-nbt" } @@ -46,7 +47,6 @@ lru = "0.16.0" tokio-util = { version = "0.7.15", features = ["rt"] } rand = "0.9.2" -num_cpus = "1.17.0" [dev-dependencies] criterion = { version = "0.7", default-features = false, features = ["html_reports", "async_tokio"] } diff --git a/pumpkin-world/src/block/entities/barrel.rs b/pumpkin-world/src/block/entities/barrel.rs index bc0aabed0..ad1764d41 100644 --- a/pumpkin-world/src/block/entities/barrel.rs +++ b/pumpkin-world/src/block/entities/barrel.rs @@ -42,7 +42,7 @@ impl BlockEntity for BarrelBlockEntity { { let barrel = Self { position, - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), dirty: AtomicBool::new(false), }; @@ -75,7 +75,7 @@ impl BarrelBlockEntity { pub fn new(position: BlockPos) -> Self { Self { position, - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), dirty: AtomicBool::new(false), } } @@ -102,7 +102,7 @@ impl Inventory for BarrelBlockEntity { } async fn remove_stack(&self, slot: usize) -> ItemStack { - let mut removed = ItemStack::EMPTY; + let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.items[slot].lock().await; std::mem::swap(&mut removed, &mut *guard); removed @@ -129,7 +129,7 @@ impl Inventory for BarrelBlockEntity { impl Clearable for BarrelBlockEntity { async fn clear(&self) { for slot in self.items.iter() { - *slot.lock().await = ItemStack::EMPTY; + *slot.lock().await = ItemStack::EMPTY.clone(); } } } diff --git a/pumpkin-world/src/block/entities/chiseled_bookshelf.rs b/pumpkin-world/src/block/entities/chiseled_bookshelf.rs index 2a72e8fa3..884a7ac67 100644 --- a/pumpkin-world/src/block/entities/chiseled_bookshelf.rs +++ b/pumpkin-world/src/block/entities/chiseled_bookshelf.rs @@ -46,7 +46,7 @@ impl BlockEntity for ChiseledBookshelfBlockEntity { { let chiseled_bookshelf = Self { position, - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), last_interacted_slot: AtomicI8::new( nbt.get_int(LAST_INTERACTED_SLOT).unwrap_or(-1) as i8 ), @@ -77,7 +77,7 @@ impl ChiseledBookshelfBlockEntity { pub fn new(position: BlockPos) -> Self { Self { position, - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), last_interacted_slot: AtomicI8::new(-1), dirty: AtomicBool::new(false), } @@ -138,7 +138,7 @@ impl Inventory for ChiseledBookshelfBlockEntity { } async fn remove_stack(&self, slot: usize) -> ItemStack { - let mut removed = ItemStack::EMPTY; + let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.items[slot].lock().await; std::mem::swap(&mut removed, &mut *guard); removed @@ -165,7 +165,7 @@ impl Inventory for ChiseledBookshelfBlockEntity { impl Clearable for ChiseledBookshelfBlockEntity { async fn clear(&self) { for slot in self.items.iter() { - *slot.lock().await = ItemStack::EMPTY; + *slot.lock().await = ItemStack::EMPTY.clone(); } } } diff --git a/pumpkin-world/src/block/entities/dropper.rs b/pumpkin-world/src/block/entities/dropper.rs index 4c337b73d..32c8d28d4 100644 --- a/pumpkin-world/src/block/entities/dropper.rs +++ b/pumpkin-world/src/block/entities/dropper.rs @@ -31,7 +31,7 @@ impl BlockEntity for DropperBlockEntity { { let dropper = Self { position, - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), dirty: AtomicBool::new(false), }; @@ -66,7 +66,7 @@ impl DropperBlockEntity { pub fn new(position: BlockPos) -> Self { Self { position, - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), dirty: AtomicBool::new(false), } } @@ -108,7 +108,7 @@ impl Inventory for DropperBlockEntity { } async fn remove_stack(&self, slot: usize) -> ItemStack { - let mut removed = ItemStack::EMPTY; + let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.items[slot].lock().await; std::mem::swap(&mut removed, &mut *guard); removed @@ -135,7 +135,7 @@ impl Inventory for DropperBlockEntity { impl Clearable for DropperBlockEntity { async fn clear(&self) { for slot in self.items.iter() { - *slot.lock().await = ItemStack::EMPTY; + *slot.lock().await = ItemStack::EMPTY.clone(); } } } diff --git a/pumpkin-world/src/block/entities/furnace.rs b/pumpkin-world/src/block/entities/furnace.rs index db21b0f6a..0e8c1714a 100644 --- a/pumpkin-world/src/block/entities/furnace.rs +++ b/pumpkin-world/src/block/entities/furnace.rs @@ -112,7 +112,7 @@ impl FurnaceBlockEntity { }; let output_item_stack = ItemStack::new(recipe.result.count, output_item); - if side_items.are_equal(&ItemStack::EMPTY) { + if side_items.are_equal(ItemStack::EMPTY) { drop(side_items); self.set_stack(2, output_item_stack).await; } else if side_items.are_items_and_components_equal(&output_item_stack) { @@ -298,7 +298,7 @@ impl BlockEntity for FurnaceBlockEntity { let furnace = Self { position, dirty: AtomicBool::new(false), - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), cooking_total_time, cooking_time_spent, lit_total_time, @@ -354,7 +354,7 @@ impl FurnaceBlockEntity { Self { position, dirty: AtomicBool::new(false), - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), cooking_total_time: AtomicU16::new(0), cooking_time_spent: AtomicU16::new(0), lit_total_time: AtomicU16::new(0), @@ -384,7 +384,7 @@ impl Inventory for FurnaceBlockEntity { } async fn remove_stack(&self, slot: usize) -> ItemStack { - let mut removed = ItemStack::EMPTY; + let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.items[slot].lock().await; std::mem::swap(&mut removed, &mut *guard); removed @@ -401,7 +401,7 @@ impl Inventory for FurnaceBlockEntity { let is_same_item = !stack.is_empty() && ItemStack::are_items_and_components_equal(&furnace_stack, &stack); - *furnace_stack = stack; + *furnace_stack = stack.clone(); drop(furnace_stack); if slot == 0 && !is_same_item { @@ -429,7 +429,7 @@ impl Inventory for FurnaceBlockEntity { impl Clearable for FurnaceBlockEntity { async fn clear(&self) { for slot in self.items.iter() { - *slot.lock().await = ItemStack::EMPTY; + *slot.lock().await = ItemStack::EMPTY.clone(); } } } diff --git a/pumpkin-world/src/block/entities/hopper.rs b/pumpkin-world/src/block/entities/hopper.rs index a383eb3c9..3682a223f 100644 --- a/pumpkin-world/src/block/entities/hopper.rs +++ b/pumpkin-world/src/block/entities/hopper.rs @@ -58,7 +58,7 @@ impl BlockEntity for HopperBlockEntity { { let hopper = Self { position, - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), dirty: AtomicBool::new(false), facing: HopperFacing::Down, cooldown_time: AtomicI32::from(nbt.get_int("TransferCooldown").unwrap_or(-1)), @@ -121,7 +121,7 @@ impl HopperBlockEntity { pub fn new(position: BlockPos, facing: HopperFacing) -> Self { Self { position, - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), dirty: AtomicBool::new(false), facing, cooldown_time: AtomicI32::new(-1), @@ -171,7 +171,7 @@ impl HopperBlockEntity { let mut item = bind.lock().await; if !item.is_empty() && container.can_transfer_to(self, i, &item) { //TODO WorldlyContainer - let backup = *item; + let backup = item.clone(); let one_item = item.split(1); if Self::add_one_item(container.as_ref(), self, one_item).await { return true; @@ -219,7 +219,7 @@ impl HopperBlockEntity { let mut item = i.lock().await; if !item.is_empty() { //TODO WorldlyContainer - let backup = *item; + let backup = item.clone(); let one_item = item.split(1); if Self::add_one_item(self, container.as_ref(), one_item).await { return true; @@ -239,7 +239,7 @@ impl HopperBlockEntity { let bind = to.get_stack(j).await; let mut dst = bind.lock().await; if dst.is_empty() { - *dst = item; + *dst = item.clone(); success = true; } else if dst.item_count < dst.get_max_stack_size() && dst.item == item.item { // TODO check Components equal @@ -310,7 +310,7 @@ impl Inventory for HopperBlockEntity { } async fn remove_stack(&self, slot: usize) -> ItemStack { - let mut removed = ItemStack::EMPTY; + let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.items[slot].lock().await; std::mem::swap(&mut removed, &mut *guard); removed @@ -337,7 +337,7 @@ impl Inventory for HopperBlockEntity { impl Clearable for HopperBlockEntity { async fn clear(&self) { for slot in self.items.iter() { - *slot.lock().await = ItemStack::EMPTY; + *slot.lock().await = ItemStack::EMPTY.clone(); } } } diff --git a/pumpkin-world/src/block/entities/shulker_box.rs b/pumpkin-world/src/block/entities/shulker_box.rs index 16d38b93b..3f0107a6b 100644 --- a/pumpkin-world/src/block/entities/shulker_box.rs +++ b/pumpkin-world/src/block/entities/shulker_box.rs @@ -39,7 +39,7 @@ impl BlockEntity for ShulkerBoxBlockEntity { { let barrel = Self { position, - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), dirty: AtomicBool::new(false), }; @@ -72,7 +72,7 @@ impl ShulkerBoxBlockEntity { pub fn new(position: BlockPos) -> Self { Self { position, - items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY))), + items: from_fn(|_| Arc::new(Mutex::new(ItemStack::EMPTY.clone()))), dirty: AtomicBool::new(false), } } @@ -99,7 +99,7 @@ impl Inventory for ShulkerBoxBlockEntity { } async fn remove_stack(&self, slot: usize) -> ItemStack { - let mut removed = ItemStack::EMPTY; + let mut removed = ItemStack::EMPTY.clone(); let mut guard = self.items[slot].lock().await; std::mem::swap(&mut removed, &mut *guard); removed @@ -126,7 +126,7 @@ impl Inventory for ShulkerBoxBlockEntity { impl Clearable for ShulkerBoxBlockEntity { async fn clear(&self) { for slot in self.items.iter() { - *slot.lock().await = ItemStack::EMPTY; + *slot.lock().await = ItemStack::EMPTY.clone(); } } } diff --git a/pumpkin-world/src/inventory/mod.rs b/pumpkin-world/src/inventory/mod.rs index 4a833c691..b676c7750 100644 --- a/pumpkin-world/src/inventory/mod.rs +++ b/pumpkin-world/src/inventory/mod.rs @@ -15,6 +15,6 @@ pub async fn split_stack(stacks: &[Arc>], slot: usize, amount: if slot < stacks.len() && !stack.is_empty() && amount > 0 { stack.split(amount) } else { - ItemStack::EMPTY + ItemStack::EMPTY.clone() } } diff --git a/pumpkin-world/src/item/mod.rs b/pumpkin-world/src/item/mod.rs index a9232c889..562dd1467 100644 --- a/pumpkin-world/src/item/mod.rs +++ b/pumpkin-world/src/item/mod.rs @@ -1,9 +1,13 @@ +use pumpkin_data::Block; +use pumpkin_data::data_component::DataComponent; +use pumpkin_data::data_component_impl::{ + DataComponentImpl, IDSet, MaxStackSizeImpl, ToolImpl, get, +}; use pumpkin_data::item::Item; use pumpkin_data::recipes::RecipeResultStruct; -use pumpkin_data::tag::{RegistryKey, get_tag_values}; +use pumpkin_data::tag::Taggable; use pumpkin_nbt::compound::NbtCompound; use pumpkin_util::GameMode; -use std::hash::Hash; mod categories; @@ -17,18 +21,20 @@ pub enum Rarity { Epic, } -#[derive(Clone, Debug, Copy)] +#[derive(Clone, Debug)] pub struct ItemStack { pub item_count: u8, pub item: &'static Item, + pub patch: Vec<(DataComponent, Option>)>, } -impl Hash for ItemStack { - fn hash(&self, state: &mut H) { - self.item_count.hash(state); - self.item.id.hash(state); - } -} +// impl Hash for ItemStack { +// fn hash(&self, state: &mut H) { +// self.item_count.hash(state); +// self.item.id.hash(state); +// self.patch.hash(state); +// } +// } /* impl PartialEq for ItemStack { @@ -38,17 +44,45 @@ impl PartialEq for ItemStack { } */ impl ItemStack { - pub const EMPTY: ItemStack = ItemStack { - item_count: 0, - item: &Item::AIR, - }; - pub fn new(item_count: u8, item: &'static Item) -> Self { - Self { item_count, item } + Self { + item_count, + item, + patch: Vec::new(), + } } + pub fn get_data_component(&self) -> Option<&T> { + let to_get_id = &T::get_enum(); + for (id, component) in &self.patch { + if id == to_get_id { + return if let Some(component) = component { + Some(get::(component.as_ref())) + } else { + None + }; + } + } + for (id, component) in self.item.components { + if id == to_get_id { + return Some(get::(*component)); + } + } + None + } + + pub const EMPTY: &'static ItemStack = &ItemStack { + item_count: 0, + item: &Item::AIR, + patch: Vec::new(), + }; + pub fn get_max_stack_size(&self) -> u8 { - self.item.components.max_stack_size + if let Some(value) = self.get_data_component::() { + value.size + } else { + 1 + } } pub fn get_item(&self) -> &Item { @@ -84,7 +118,7 @@ impl ItemStack { } pub fn copy_with_count(&self, count: u8) -> Self { - let mut stack = *self; + let mut stack = self.clone(); stack.item_count = count; stack } @@ -112,71 +146,59 @@ impl ItemStack { /// Determines the mining speed for a block based on tool rules. /// Direct matches return immediately, tagged blocks are checked separately. /// If no match is found, returns the tool's default mining speed or `1.0`. - pub fn get_speed(&self, block: &str) -> f32 { + pub fn get_speed(&self, block: &'static Block) -> f32 { // No tool? Use default speed - let Some(tool) = &self.item.components.tool else { - return 1.0; - }; - - for rule in tool.rules { - // Skip if speed is not set - let Some(speed) = rule.speed else { - continue; - }; - - for entry in rule.blocks { - if entry.eq(&block) { - return speed; - } - - if entry.starts_with('#') { - // Check if block is in the tag group - if let Some(blocks) = - get_tag_values(RegistryKey::Block, entry.strip_prefix('#').unwrap()) - { + if let Some(tool) = self.get_data_component::() { + for rule in tool.rules.iter() { + // Skip if speed is not set + let Some(speed) = rule.speed else { + continue; + }; + match &rule.blocks { + IDSet::Tag(tag) => { + if block.is_tagged_with_by_tag(tag) { + return speed; + } + } + IDSet::Blocks(blocks) => { if blocks.contains(&block) { return speed; } } } } + tool.default_mining_speed + } else { + 1.0 } - // Return default mining speed if no match is found - tool.default_mining_speed.unwrap_or(1.0) } /// Determines if a tool is valid for block drops based on tool rules. /// Direct matches return immediately, while tagged blocks are checked separately. - pub fn is_correct_for_drops(&self, block: &str) -> bool { - // Return false if no tool component exists - let Some(tool) = &self.item.components.tool else { - return false; - }; - - for rule in tool.rules { - // Skip rules without a drop condition - let Some(correct_for_drops) = rule.correct_for_drops else { - continue; - }; - - for entry in rule.blocks { - if entry.eq(&block) { - return correct_for_drops; - } - - if entry.starts_with('#') { - // Check if block exists within the tag group - if let Some(blocks) = - get_tag_values(RegistryKey::Block, entry.strip_prefix('#').unwrap()) - { + pub fn is_correct_for_drops(&self, block: &'static Block) -> bool { + if let Some(tool) = self.get_data_component::() { + for rule in tool.rules.iter() { + // Skip if speed is not set + let Some(correct) = rule.correct_for_drops else { + continue; + }; + match &rule.blocks { + IDSet::Tag(tag) => { + if block.is_tagged_with_by_tag(tag) { + return correct; + } + } + IDSet::Blocks(blocks) => { if blocks.contains(&block) { - return correct_for_drops; + return correct; } } } } + false + } else { + false } - false } pub fn write_item_stack(&self, compound: &mut NbtCompound) { @@ -223,6 +245,7 @@ impl From<&RecipeResultStruct> for ItemStack { item_count: value.count, item: Item::from_registry_key(value.id.strip_prefix("minecraft:").unwrap_or(value.id)) .expect("Crafting recipe gives invalid item"), + patch: Vec::new(), } } } diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index f8384ab87..6be214f91 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -3,7 +3,7 @@ name = "pumpkin" version.workspace = true description = "Empowering everyone to host fast and efficient Minecraft servers." edition.workspace = true -rust-version = "1.87" +rust-version = "1.88" [package.metadata.tauri-winres] # FileDescription is handled as the Program name by Windows! diff --git a/pumpkin/src/block/blocks/cake.rs b/pumpkin/src/block/blocks/cake.rs index 297cee9bc..c64d7d638 100644 --- a/pumpkin/src/block/blocks/cake.rs +++ b/pumpkin/src/block/blocks/cake.rs @@ -1,17 +1,5 @@ use std::sync::Arc; -use async_trait::async_trait; -use pumpkin_data::{ - Block, - block_properties::{BlockProperties, CakeLikeProperties, EnumVariants, Integer0To6}, - item::Item, - sound::{Sound, SoundCategory}, -}; -use pumpkin_macros::pumpkin_block; -use pumpkin_util::{GameMode, math::position::BlockPos}; -use pumpkin_world::world::BlockFlags; -use rand::{Rng, rng}; - use crate::{ block::{ blocks::candle_cakes::cake_from_candle, @@ -21,6 +9,17 @@ use crate::{ entity::player::Player, world::World, }; +use async_trait::async_trait; +use pumpkin_data::item::Item; +use pumpkin_data::{ + Block, + block_properties::{BlockProperties, CakeLikeProperties, EnumVariants, Integer0To6}, + sound::{Sound, SoundCategory}, +}; +use pumpkin_macros::pumpkin_block; +use pumpkin_util::{GameMode, math::position::BlockPos}; +use pumpkin_world::world::BlockFlags; +use rand::{Rng, rng}; #[pumpkin_block("minecraft:cake")] pub struct CakeBlock; diff --git a/pumpkin/src/block/blocks/candles.rs b/pumpkin/src/block/blocks/candles.rs index ca45122b4..510440efd 100644 --- a/pumpkin/src/block/blocks/candles.rs +++ b/pumpkin/src/block/blocks/candles.rs @@ -1,9 +1,9 @@ use async_trait::async_trait; +use pumpkin_data::item::Item; use pumpkin_data::{ BlockDirection, block_properties::{BlockProperties, CandleLikeProperties, EnumVariants, Integer1To4}, entity::EntityPose, - item::Item, tag::{RegistryKey, get_tag_values}, }; use pumpkin_macros::pumpkin_block_from_tag; diff --git a/pumpkin/src/block/blocks/chiseled_bookshelf.rs b/pumpkin/src/block/blocks/chiseled_bookshelf.rs index fc9775ec6..e0e5c599a 100644 --- a/pumpkin/src/block/blocks/chiseled_bookshelf.rs +++ b/pumpkin/src/block/blocks/chiseled_bookshelf.rs @@ -1,5 +1,16 @@ use std::sync::{Arc, atomic::Ordering}; +use crate::{ + block::{ + pumpkin_block::{ + BlockHitResult, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, + OnStateReplacedArgs, PlacedArgs, PumpkinBlock, UseWithItemArgs, + }, + registry::BlockActionResult, + }, + entity::{EntityBase, player::Player}, + world::World, +}; use async_trait::async_trait; use pumpkin_data::{ block_properties::{BlockProperties, ChiseledBookshelfLikeProperties, HorizontalFacing}, @@ -17,18 +28,6 @@ use pumpkin_world::{ }; use tokio::sync::Mutex; -use crate::{ - block::{ - pumpkin_block::{ - BlockHitResult, GetComparatorOutputArgs, NormalUseArgs, OnPlaceArgs, - OnStateReplacedArgs, PlacedArgs, PumpkinBlock, UseWithItemArgs, - }, - registry::BlockActionResult, - }, - entity::{EntityBase, player::Player}, - world::World, -}; - #[pumpkin_block("minecraft:chiseled_bookshelf")] pub struct ChiseledBookshelfBlock; @@ -147,7 +146,7 @@ impl ChiseledBookshelfBlock { // TODO: Increment used stats for chiseled bookshelf on the player let mut item = item.lock().await; - let sound = if *item.get_item() == Item::ENCHANTED_BOOK { + let sound = if item.get_item() == &Item::ENCHANTED_BOOK { Sound::BlockChiseledBookshelfPickupEnchanted } else { Sound::BlockChiseledBookshelfPickup @@ -176,7 +175,7 @@ impl ChiseledBookshelfBlock { ) { let mut stack = entity.remove_stack_specific(slot as usize, 1).await; - let sound = if *stack.get_item() == Item::ENCHANTED_BOOK { + let sound = if stack.get_item() == &Item::ENCHANTED_BOOK { Sound::BlockChiseledBookshelfPickupEnchanted } else { Sound::BlockChiseledBookshelfPickup diff --git a/pumpkin/src/block/blocks/composter.rs b/pumpkin/src/block/blocks/composter.rs index 3ea4fcf1c..8b2def83e 100644 --- a/pumpkin/src/block/blocks/composter.rs +++ b/pumpkin/src/block/blocks/composter.rs @@ -1,5 +1,16 @@ use std::sync::Arc; +use crate::{ + block::{ + pumpkin_block::{ + GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs, PumpkinBlock, + UseWithItemArgs, + }, + registry::BlockActionResult, + }, + entity::{Entity, item::ItemEntity}, + world::World, +}; use async_trait::async_trait; use pumpkin_data::{ Block, @@ -15,18 +26,6 @@ use pumpkin_world::{BlockStateId, item::ItemStack, tick::TickPriority, world::Bl use rand::Rng; use uuid::Uuid; -use crate::{ - block::{ - pumpkin_block::{ - GetComparatorOutputArgs, NormalUseArgs, OnScheduledTickArgs, PumpkinBlock, - UseWithItemArgs, - }, - registry::BlockActionResult, - }, - entity::{Entity, item::ItemEntity}, - world::World, -}; - #[pumpkin_block("minecraft:composter")] pub struct ComposterBlock; diff --git a/pumpkin/src/block/blocks/jukebox.rs b/pumpkin/src/block/blocks/jukebox.rs index c20497d35..241f15678 100644 --- a/pumpkin/src/block/blocks/jukebox.rs +++ b/pumpkin/src/block/blocks/jukebox.rs @@ -4,6 +4,7 @@ use crate::block::pumpkin_block::{BrokenArgs, PumpkinBlock, UseWithItemArgs}; use crate::block::registry::BlockActionResult; use crate::world::World; use async_trait::async_trait; +use pumpkin_data::data_component_impl::JukeboxPlayableImpl; use pumpkin_data::world::WorldEvent; use pumpkin_data::{ Block, @@ -55,14 +56,14 @@ impl PumpkinBlock for JukeboxBlock { return BlockActionResult::Success; } - let Some(jukebox_playable) = &args + let jukebox_playable = args .item_stack .lock() .await - .item - .components - .jukebox_playable - else { + .get_data_component::() + .map(|i| i.song); + + let Some(jukebox_playable) = jukebox_playable else { return BlockActionResult::Pass; }; diff --git a/pumpkin/src/block/blocks/pumpkin.rs b/pumpkin/src/block/blocks/pumpkin.rs index 7991fb1e8..4090fad36 100644 --- a/pumpkin/src/block/blocks/pumpkin.rs +++ b/pumpkin/src/block/blocks/pumpkin.rs @@ -11,6 +11,7 @@ use pumpkin_world::item::ItemStack; use pumpkin_world::world::BlockFlags; use std::sync::Arc; use uuid::Uuid; + #[pumpkin_block("minecraft:pumpkin")] pub struct PumpkinBlock; diff --git a/pumpkin/src/block/blocks/redstone/dropper.rs b/pumpkin/src/block/blocks/redstone/dropper.rs index 7b33f3452..66e04d876 100644 --- a/pumpkin/src/block/blocks/redstone/dropper.rs +++ b/pumpkin/src/block/blocks/redstone/dropper.rs @@ -177,7 +177,7 @@ impl PumpkinBlock for DropperBlock { return; } //TODO WorldlyContainer - let backup = *item; + let backup = item.clone(); let one_item = item.split(1); if HopperBlockEntity::add_one_item(dropper, container.as_ref(), one_item) .await diff --git a/pumpkin/src/block/blocks/redstone/tripwire.rs b/pumpkin/src/block/blocks/redstone/tripwire.rs index 22e7e34df..2687f8d26 100644 --- a/pumpkin/src/block/blocks/redstone/tripwire.rs +++ b/pumpkin/src/block/blocks/redstone/tripwire.rs @@ -1,10 +1,10 @@ use std::sync::Arc; use async_trait::async_trait; +use pumpkin_data::item::Item; use pumpkin_data::{ Block, BlockDirection, block_properties::{BlockProperties, HorizontalFacing}, - item::Item, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::{boundingbox::BoundingBox, position::BlockPos}; diff --git a/pumpkin/src/block/blocks/tnt.rs b/pumpkin/src/block/blocks/tnt.rs index 3ce9a2eb8..ffaae40b6 100644 --- a/pumpkin/src/block/blocks/tnt.rs +++ b/pumpkin/src/block/blocks/tnt.rs @@ -1,3 +1,4 @@ +use pumpkin_data::item::Item; use std::sync::Arc; use crate::block::pumpkin_block::{ @@ -9,7 +10,6 @@ use crate::entity::tnt::TNTEntity; use crate::world::World; use async_trait::async_trait; use pumpkin_data::entity::EntityType; -use pumpkin_data::item::Item; use pumpkin_data::sound::SoundCategory; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; diff --git a/pumpkin/src/block/mod.rs b/pumpkin/src/block/mod.rs index 5c104100c..15871cbcb 100644 --- a/pumpkin/src/block/mod.rs +++ b/pumpkin/src/block/mod.rs @@ -266,20 +266,24 @@ pub async fn drop_loot( } } -pub async fn calc_block_breaking(player: &Player, state: &BlockState, block_name: &str) -> f32 { +pub async fn calc_block_breaking( + player: &Player, + state: &BlockState, + block: &'static Block, +) -> f32 { let hardness = state.hardness; #[expect(clippy::float_cmp)] if hardness == -1.0 { // unbreakable return 0.0; } - let i = if player.can_harvest(state, block_name).await { + let i = if player.can_harvest(state, block).await { 30 } else { 100 }; - player.get_mining_speed(block_name).await / hardness / i as f32 + player.get_mining_speed(block).await / hardness / i as f32 } #[derive(PartialEq)] diff --git a/pumpkin/src/command/commands/clear.rs b/pumpkin/src/command/commands/clear.rs index bcfac7e17..3ee188ad9 100644 --- a/pumpkin/src/command/commands/clear.rs +++ b/pumpkin/src/command/commands/clear.rs @@ -26,7 +26,7 @@ async fn clear_player(target: &Player) -> u64 { for slot in &inventory.main_inventory { let mut slot_lock = slot.lock().await; count += u64::from(slot_lock.item_count); - *slot_lock = ItemStack::EMPTY; + *slot_lock = ItemStack::EMPTY.clone(); } let entity_equipment_lock = inventory.entity_equipment.lock().await; @@ -36,7 +36,7 @@ async fn clear_player(target: &Player) -> u64 { continue; } count += 1u64; - *slot_lock = ItemStack::EMPTY; + *slot_lock = ItemStack::EMPTY.clone(); } count diff --git a/pumpkin/src/command/commands/give.rs b/pumpkin/src/command/commands/give.rs index 9b449bbeb..ede87f7e5 100644 --- a/pumpkin/src/command/commands/give.rs +++ b/pumpkin/src/command/commands/give.rs @@ -1,4 +1,6 @@ use async_trait::async_trait; +use pumpkin_data::data_component::DataComponent::MaxStackSize; +use pumpkin_data::data_component_impl::{MaxStackSizeImpl, get}; use pumpkin_util::text::TextComponent; use pumpkin_util::text::click::ClickEvent; use pumpkin_util::text::color::{Color, NamedColor}; @@ -61,7 +63,14 @@ impl CommandExecutor for Executor { }; for target in targets { - let max_stack = i32::from(item.components.max_stack_size); + let max_stack = i32::from( + item.components + .iter() + .find_map(|(id, component)| { + (id == &MaxStackSize).then(|| get::(*component).size) + }) + .unwrap(), + ); let mut remaining = item_count; while remaining > 0 { diff --git a/pumpkin/src/entity/item.rs b/pumpkin/src/entity/item.rs index 3ff251746..8c93d3534 100644 --- a/pumpkin/src/entity/item.rs +++ b/pumpkin/src/entity/item.rs @@ -82,7 +82,7 @@ impl EntityBase for ItemEntity { .send_meta_data(&[Metadata::new( 8, MetaDataType::ItemStack, - &ItemStackSerializer::from(*self.item_stack.lock().await), + &ItemStackSerializer::from(self.item_stack.lock().await.clone()), )]) .await; } diff --git a/pumpkin/src/entity/living.rs b/pumpkin/src/entity/living.rs index 0b6dbce56..5de0337eb 100644 --- a/pumpkin/src/entity/living.rs +++ b/pumpkin/src/entity/living.rs @@ -65,7 +65,12 @@ impl LivingEntity { pub async fn send_equipment_changes(&self, equipment: &[(EquipmentSlot, ItemStack)]) { let equipment: Vec<(i8, ItemStackSerializer)> = equipment .iter() - .map(|(slot, stack)| (slot.discriminant(), ItemStackSerializer::from(*stack))) + .map(|(slot, stack)| { + ( + slot.discriminant(), + ItemStackSerializer::from(stack.clone()), + ) + }) .collect(); self.entity .world diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index dcece2031..8b1913c76 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -19,8 +19,8 @@ use uuid::Uuid; use pumpkin_config::{BASIC_CONFIG, advanced_config}; use pumpkin_data::damage::DamageType; +use pumpkin_data::data_component_impl::{AttributeModifiersImpl, Operation}; use pumpkin_data::entity::{EffectType, EntityPose, EntityStatus, EntityType}; -use pumpkin_data::item::Operation; use pumpkin_data::particle::Particle; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::Taggable; @@ -448,13 +448,16 @@ impl Player { // Get the attack damage // TODO: this should be cached in memory, we shouldn't just use default here either - if let Some(modifiers) = item_stack.lock().await.item.components.attribute_modifiers { - for item_mod in modifiers { + if let Some(modifiers) = item_stack + .lock() + .await + .get_data_component::() + { + for item_mod in modifiers.attribute_modifiers.iter() { if item_mod.operation == Operation::AddValue { if item_mod.id == "minecraft:base_attack_damage" { add_damage = item_mod.amount; - } - if item_mod.id == "minecraft:base_attack_speed" { + } else if item_mod.id == "minecraft:base_attack_speed" { add_speed = item_mod.amount; } } @@ -772,7 +775,6 @@ impl Player { if self.mining.load(Ordering::Relaxed) { let pos = self.mining_pos.lock().await; let world = self.world().await; - let block = world.get_block(&pos).await; let state = world.get_block_state(&pos).await; // Is the block broken? if state.is_air() { @@ -787,7 +789,6 @@ impl Player { *pos, &world, state, - block.name, self.start_mining_time.load(Ordering::Relaxed), ) .await; @@ -834,11 +835,11 @@ impl Player { location: BlockPos, world: &World, state: &BlockState, - block_name: &str, starting_time: i32, ) { let time = self.tick_counter.load(Ordering::Relaxed) - starting_time; - let speed = block::calc_block_breaking(self, state, block_name).await * (time + 1) as f32; + let speed = block::calc_block_breaking(self, state, Block::from_state_id(state.id)).await + * (time + 1) as f32; let progress = (speed * 10.0) as i32; if progress != self.current_block_destroy_stage.load(Ordering::Relaxed) { world @@ -1359,23 +1360,18 @@ impl Player { .await; } - pub async fn can_harvest(&self, block: &BlockState, block_name: &str) -> bool { - !block.tool_required() + pub async fn can_harvest(&self, state: &BlockState, block: &'static Block) -> bool { + !state.tool_required() || self .inventory .held_item() .lock() .await - .is_correct_for_drops(block_name) + .is_correct_for_drops(block) } - pub async fn get_mining_speed(&self, block_name: &str) -> f32 { - let mut speed = self - .inventory - .held_item() - .lock() - .await - .get_speed(block_name); + pub async fn get_mining_speed(&self, block: &'static Block) -> f32 { + let mut speed = self.inventory.held_item().lock().await.get_speed(block); // Haste if self.living_entity.has_effect(EffectType::Haste).await || self @@ -1490,7 +1486,7 @@ impl Player { .await; if let Some(slot_index) = slot_index { - screen_handler.set_received_stack(slot_index, *item_stack); + screen_handler.set_received_stack(slot_index, item_stack.clone()); } } } diff --git a/pumpkin/src/net/java/play.rs b/pumpkin/src/net/java/play.rs index f7331f9c6..72116df01 100644 --- a/pumpkin/src/net/java/play.rs +++ b/pumpkin/src/net/java/play.rs @@ -1208,7 +1208,7 @@ impl JavaClient { Ordering::Relaxed, ); if !state.is_air() { - let speed = block::calc_block_breaking(player, state, block.name).await; + let speed = block::calc_block_breaking(player, state, block).await; // Instant break if speed >= 1.0 { let broken_state = world.get_block_state(&position).await; @@ -1273,7 +1273,7 @@ impl JavaClient { let (block, state) = world.get_block_and_state(&location).await; let drop = player.gamemode.load() != GameMode::Creative - && player.can_harvest(state, block.name).await; + && player.can_harvest(state, block).await; world .break_block( @@ -1616,7 +1616,7 @@ impl JavaClient { } let inv = player.inventory(); inv.set_selected_slot(slot as u8); - let stack = *inv.held_item().lock().await; + let stack = inv.held_item().lock().await.clone(); let equipment = &[(EquipmentSlot::MAIN_HAND, stack)]; player.living_entity.send_equipment_changes(equipment).await; } @@ -1640,7 +1640,7 @@ impl JavaClient { player_screen_handler .get_slot(packet.slot as usize) .await - .set_stack(item_stack) + .set_stack(item_stack.clone()) .await; player_screen_handler.set_received_stack(packet.slot as usize, item_stack); player_screen_handler.send_content_updates().await; diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 4d673a66f..dc96e5e4f 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -1191,18 +1191,18 @@ impl World { equipment_list.push(( EquipmentSlot::MAIN_HAND.discriminant(), - *from.inventory.held_item().lock().await, + from.inventory.held_item().lock().await.clone(), )); for (slot, item_arc_mutex) in &from.inventory.entity_equipment.lock().await.equipment { let item_guard = item_arc_mutex.lock().await; - let item_stack = *item_guard; + let item_stack = item_guard.clone(); equipment_list.push((slot.discriminant(), item_stack)); } let equipment: Vec<(i8, ItemStackSerializer)> = equipment_list .iter() - .map(|(slot, stack)| (*slot, ItemStackSerializer::from(*stack))) + .map(|(slot, stack)| (*slot, ItemStackSerializer::from(stack.clone()))) .collect(); self.broadcast_packet_except( &[from.get_entity().entity_uuid],