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
This commit is contained in:
spr-equinox
2025-07-28 18:24:40 +08:00
committed by GitHub
parent eaf5959f2b
commit 8ce65cc42d
48 changed files with 1409 additions and 544 deletions

17
Cargo.lock generated
View File

@@ -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",

View File

@@ -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
}
}

View File

@@ -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
}

View File

@@ -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<String, Attributes> =
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::<Vec<_>>();
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<H: std::hash::Hasher>(&self, state: &mut H) {
self.id.hash(state);
}
}
impl Attributes {
#consts
}
}
}

View File

@@ -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"),
];

View File

@@ -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<String, u8> =
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::<Vec<_>>();
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
}
}
}
}
}

View File

@@ -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,

View File

@@ -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<ToolRule>,
default_mining_speed: Option<f32>,
damage_per_block: Option<u32>,
#[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<bool>,
#[serde(default = "return_false")]
can_always_eat: bool,
}
#[derive(Deserialize, Clone, Debug)]
@@ -203,14 +259,14 @@ pub struct ToolRule {
correct_for_drops: Option<bool>,
}
#[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<u16>,
pub max_damage: Option<u16>,
pub attribute_modifiers: Option<&'static [Modifier]>,
pub tool: Option<ToolComponent>,
pub food: Option<FoodComponent>
}
#[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<f32>,
pub damage_per_block: Option<u32>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ToolRule {
pub blocks: &'static [&'static str],
pub speed: Option<f32>,
pub correct_for_drops: Option<bool>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct FoodComponent {
pub nutrition: u8,
pub saturation: f32,
pub can_always_eat: Option<bool>,
}
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::<ItemNameImpl>().unwrap().name)
} else {
None
}
).unwrap(),
&[],
)
}
#[doc = "Try to parse an item from a resource location string."]

View File

@@ -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<dyn DataComponentImpl>;
fn as_any(&self) -> &dyn Any;
fn as_mut_any(&mut self) -> &mut dyn Any;
}
impl Clone for Box<dyn DataComponentImpl> {
fn clone(&self) -> Self {
self.clone_dyn()
}
}
pub fn get<T: DataComponentImpl + 'static>(value: &dyn DataComponentImpl) -> &T {
value.as_any().downcast_ref::<T>().unwrap()
}
pub fn get_mut<T: DataComponentImpl + 'static>(value: &mut dyn DataComponentImpl) -> &mut T {
value.as_mut_any().downcast_mut::<T>().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<dyn DataComponentImpl> {
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<dyn DataComponentImpl> {
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<dyn DataComponentImpl> {
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<dyn DataComponentImpl> {
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<dyn DataComponentImpl> {
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<H: std::hash::Hasher>(&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<dyn DataComponentImpl> {
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<dyn DataComponentImpl> {
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<H: std::hash::Hasher>(&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<f32>,
pub correct_for_drops: Option<bool>,
}
impl Hash for ToolRule {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.blocks.hash(state);
if let Some(val) = self.speed {
true.hash(state);
unsafe { (*(&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<dyn DataComponentImpl> {
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<H: std::hash::Hasher>(&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<dyn DataComponentImpl> {
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;

View File

@@ -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::*;

View File

@@ -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();
}
}
}

View File

@@ -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()
}
}

View File

@@ -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<Mutex<ItemStack>> {
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 {

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -30,7 +30,7 @@ impl PlayerInventory {
pub fn new(entity_equipment: Arc<Mutex<EntityEquipment>>) -> 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
}
}

View File

@@ -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()
}
}

View File

@@ -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),

View File

@@ -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;
}

View File

@@ -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::<Vec<_>>()
.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;
}
}

View File

@@ -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::<VarInt>()?
@@ -107,7 +107,7 @@ impl From<Option<ItemStack>> for ItemStackSerializer<'_> {
fn from(item: Option<ItemStack>) -> Self {
match item {
Some(item) => ItemStackSerializer::from(item),
None => ItemStackSerializer(Cow::Borrowed(&ItemStack::EMPTY)),
None => ItemStackSerializer(Cow::Borrowed(ItemStack::EMPTY)),
}
}
}

View File

@@ -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"] }

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -15,6 +15,6 @@ pub async fn split_stack(stacks: &[Arc<Mutex<ItemStack>>], slot: usize, amount:
if slot < stacks.len() && !stack.is_empty() && amount > 0 {
stack.split(amount)
} else {
ItemStack::EMPTY
ItemStack::EMPTY.clone()
}
}

View File

@@ -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<Box<dyn DataComponentImpl>>)>,
}
impl Hash for ItemStack {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.item_count.hash(state);
self.item.id.hash(state);
}
}
// impl Hash for ItemStack {
// fn hash<H: std::hash::Hasher>(&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<T: DataComponentImpl + 'static>(&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::<T>(component.as_ref()))
} else {
None
};
}
}
for (id, component) in self.item.components {
if id == to_get_id {
return Some(get::<T>(*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::<MaxStackSizeImpl>() {
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::<ToolImpl>() {
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::<ToolImpl>() {
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(),
}
}
}

View File

@@ -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!

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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;

View File

@@ -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::<JukeboxPlayableImpl>()
.map(|i| i.song);
let Some(jukebox_playable) = jukebox_playable else {
return BlockActionResult::Pass;
};

View File

@@ -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;

View File

@@ -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

View File

@@ -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};

View File

@@ -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;

View File

@@ -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)]

View File

@@ -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

View File

@@ -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::<MaxStackSizeImpl>(*component).size)
})
.unwrap(),
);
let mut remaining = item_count;
while remaining > 0 {

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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::<AttributeModifiersImpl>()
{
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());
}
}
}

View File

@@ -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;

View File

@@ -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],