mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
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:
17
Cargo.lock
generated
17
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
98
assets/data_component.json
Normal file
98
assets/data_component.json
Normal 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
|
||||
}
|
||||
54
pumpkin-data/build/attributes.rs
Normal file
54
pumpkin-data/build/attributes.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"),
|
||||
];
|
||||
|
||||
|
||||
58
pumpkin-data/build/data_component.rs
Normal file
58
pumpkin-data/build/data_component.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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."]
|
||||
|
||||
479
pumpkin-data/src/data_component_impl.rs
Normal file
479
pumpkin-data/src/data_component_impl.rs
Normal 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;
|
||||
@@ -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::*;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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!
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user