diff --git a/assets/spawn_egg.json b/assets/spawn_egg.json new file mode 100644 index 000000000..d586b92e5 --- /dev/null +++ b/assets/spawn_egg.json @@ -0,0 +1 @@ +{"1130":"wither_skeleton","1071":"creeper","1094":"ocelot","1114":"snow_golem","1125":"vindicator","1090":"llama","1129":"wither","1134":"zombie","1080":"fox","1085":"guardian","1076":"ender_dragon","1102":"polar_bear","1081":"frog","1131":"wolf","1070":"cow","1111":"skeleton_horse","1122":"turtle","1096":"parrot","1104":"rabbit","1093":"mule","1092":"mooshroom","1106":"salmon","1082":"ghast","1126":"wandering_trader","1109":"silverfish","1132":"zoglin","1065":"cat","1062":"blaze","1068":"chicken","1072":"dolphin","1103":"pufferfish","1108":"shulker","1119":"tadpole","1059":"axolotl","1069":"cod","1112":"slime","1113":"sniffer","1137":"zombified_piglin","1087":"horse","1105":"ravager","1110":"skeleton","1083":"glow_squid","1066":"camel","1067":"cave_spider","1057":"armadillo","1086":"hoglin","1118":"strider","1077":"enderman","1124":"villager","1128":"witch","1107":"sheep","1063":"bogged","1120":"trader_llama","1074":"drowned","1064":"breeze","1075":"elder_guardian","1115":"spider","1097":"phantom","1116":"squid","1101":"pillager","1127":"warden","1078":"endermite","1061":"bee","1089":"iron_golem","1098":"pig","1135":"zombie_horse","1117":"stray","1133":"creaking","1073":"donkey","1095":"panda","1136":"zombie_villager","1091":"magma_cube","1099":"piglin","1100":"piglin_brute","1084":"goat","1058":"allay","1121":"tropical_fish","1060":"bat","1079":"evoker","1088":"husk","1123":"vex"} \ No newline at end of file diff --git a/pumpkin-data/Cargo.toml b/pumpkin-data/Cargo.toml index b7d1b229c..aff7a9fb4 100644 --- a/pumpkin-data/Cargo.toml +++ b/pumpkin-data/Cargo.toml @@ -5,7 +5,10 @@ edition.workspace = true build = "build/build.rs" [dependencies] +pumpkin-util = { path = "../pumpkin-util" } + serde.workspace = true +serde_json.workspace = true [build-dependencies] pumpkin-util = { path = "../pumpkin-util" } diff --git a/pumpkin-data/build/biome.rs b/pumpkin-data/build/biome.rs index 12977b57f..190e044cb 100644 --- a/pumpkin-data/build/biome.rs +++ b/pumpkin-data/build/biome.rs @@ -7,8 +7,8 @@ pub(crate) fn build() -> TokenStream { println!("cargo:rerun-if-changed=../assets/biome.json"); let biomes: Vec = serde_json::from_str(include_str!("../../assets/biome.json")) - .expect("Failed to parse entity_pose.json"); - let variants = array_to_tokenstream(biomes); + .expect("Failed to parse biome.json"); + let variants = array_to_tokenstream(&biomes); quote! { #[derive(Clone, Copy)] diff --git a/pumpkin-data/build/build.rs b/pumpkin-data/build/build.rs index 6173c8ff1..83726f77e 100644 --- a/pumpkin-data/build/build.rs +++ b/pumpkin-data/build/build.rs @@ -10,6 +10,7 @@ mod damage_type; mod entity_pose; mod entity_type; mod game_event; +mod item; mod message_type; mod noise_parameter; mod packet; @@ -18,6 +19,7 @@ mod scoreboard_slot; mod screen; mod sound; mod sound_category; +mod spawn_egg; mod world_event; pub fn main() { @@ -36,9 +38,11 @@ pub fn main() { write_generated_file(biome::build(), "biome.rs"); write_generated_file(damage_type::build(), "damage_type.rs"); write_generated_file(message_type::build(), "message_type.rs"); + write_generated_file(spawn_egg::build(), "spawn_egg.rs"); + write_generated_file(item::build(), "item.rs"); } -pub fn array_to_tokenstream(array: Vec) -> TokenStream { +pub fn array_to_tokenstream(array: &[String]) -> TokenStream { let mut variants = TokenStream::new(); for item in array.iter() { diff --git a/pumpkin-data/build/damage_type.rs b/pumpkin-data/build/damage_type.rs index aadf5554e..6f0b51f8a 100644 --- a/pumpkin-data/build/damage_type.rs +++ b/pumpkin-data/build/damage_type.rs @@ -3,7 +3,7 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote}; use serde::Deserialize; use std::collections::HashMap; -use syn::LitInt; +use syn::{Ident, LitInt}; #[derive(Deserialize)] struct DamageTypeEntry { @@ -13,10 +13,26 @@ struct DamageTypeEntry { #[derive(Deserialize)] pub struct DamageTypeData { - death_message_type: Option, + death_message_type: Option, exhaustion: f32, message_id: String, - scaling: String, + scaling: DamageScaling, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum DamageScaling { + Never, + WhenCausedByLivingNonPlayer, + Always, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "snake_case")] +pub enum DeathMessageType { + Default, + FallVariants, + IntentionalGameDesign, } pub(crate) fn build() -> TokenStream { @@ -36,13 +52,20 @@ pub(crate) fn build() -> TokenStream { let data = &entry.components; let death_message_type = match &data.death_message_type { - Some(msg) => quote! { Some(#msg) }, + Some(msg) => { + let msg_ident = Ident::new(&format!("{:?}", msg), proc_macro2::Span::call_site()); + quote! { Some(DeathMessageType::#msg_ident) } + } None => quote! { None }, }; let exhaustion = data.exhaustion; let message_id = &data.message_id; - let scaling = &data.scaling; + let scaling_ident = Ident::new( + &format!("{:?}", data.scaling), + proc_macro2::Span::call_site(), + ); + let scaling = quote! {DamageScaling::#scaling_ident}; let id_lit = LitInt::new(&entry.id.to_string(), proc_macro2::Span::call_site()); constants.push(quote! { @@ -68,13 +91,27 @@ pub(crate) fn build() -> TokenStream { quote! { #[derive(Clone, Copy, Debug, PartialEq)] pub struct DamageType { - pub death_message_type: Option<&'static str>, + pub death_message_type: Option, pub exhaustion: f32, pub message_id: &'static str, - pub scaling: &'static str, + pub scaling: DamageScaling, pub id: u32, } + #[derive(Clone, Copy, Debug, PartialEq)] + pub enum DeathMessageType { + Default, + FallVariants, + IntentionalGameDesign, + } + + #[derive(Clone, Copy, Debug, PartialEq)] + pub enum DamageScaling { + Never, + WhenCausedByLivingNonPlayer, + Always, + } + impl DamageType { #(#constants)* diff --git a/pumpkin-data/build/entity_pose.rs b/pumpkin-data/build/entity_pose.rs index c7a9b8b3a..e07edd6da 100644 --- a/pumpkin-data/build/entity_pose.rs +++ b/pumpkin-data/build/entity_pose.rs @@ -8,7 +8,7 @@ pub(crate) fn build() -> TokenStream { let poses: Vec = serde_json::from_str(include_str!("../../assets/entity_pose.json")) .expect("Failed to parse entity_pose.json"); - let variants = array_to_tokenstream(poses); + let variants = array_to_tokenstream(&poses); quote! { #[derive(Clone, Copy)] diff --git a/pumpkin-data/build/game_event.rs b/pumpkin-data/build/game_event.rs index d3b6c68c0..098aa2fd9 100644 --- a/pumpkin-data/build/game_event.rs +++ b/pumpkin-data/build/game_event.rs @@ -9,7 +9,7 @@ pub(crate) fn build() -> TokenStream { let game_events: Vec = serde_json::from_str(include_str!("../../assets/game_event.json")) .expect("Failed to parse game_event.json"); - let variants = array_to_tokenstream(game_events); + let variants = array_to_tokenstream(&game_events); quote! { pub enum GameEvent { diff --git a/pumpkin-data/build/item.rs b/pumpkin-data/build/item.rs new file mode 100644 index 000000000..195d8d954 --- /dev/null +++ b/pumpkin-data/build/item.rs @@ -0,0 +1,239 @@ +use heck::ToShoutySnakeCase; +use proc_macro2::{Span, TokenStream}; +use quote::{format_ident, quote, ToTokens}; +use serde::Deserialize; +use std::collections::HashMap; +use syn::{Ident, LitInt, LitStr}; + +#[derive(Deserialize, Clone, Debug)] +pub struct Item { + pub id: u16, + pub components: ItemComponents, +} + +#[derive(Deserialize, Clone, Debug)] +pub struct ItemComponents { + #[serde(rename = "minecraft:item_name")] + // TODO: TextComponent + pub item_name: Option, + #[serde(rename = "minecraft:max_stack_size")] + pub max_stack_size: u8, + #[serde(rename = "minecraft:jukebox_playable")] + pub jukebox_playable: Option, + #[serde(rename = "minecraft:damage")] + pub damage: Option, + #[serde(rename = "minecraft:max_damage")] + pub max_damage: Option, + #[serde(rename = "minecraft:attribute_modifiers")] + pub attribute_modifiers: Option, +} + +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.song, Span::call_site()); + quote! { Some(JukeboxPlayable { song: #song }) } + } + None => quote! { None }, + }; + + let item_name = match &self.item_name { + Some(d) => { + let item_name = LitStr::new(d, Span::call_site()); + quote! { Some(#item_name) } + } + None => quote! { None }, + }; + + 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.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(AttributeModifiers { modifiers: &[#(#modifier_code),*] }) } + } + 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, + } + }); + } +} + +#[derive(Deserialize, Clone, Debug)] +pub struct JukeboxPlayable { + pub song: String, +} + +#[derive(Deserialize, Clone, Debug)] +pub struct AttributeModifiers { + pub modifiers: Vec, +} + +#[derive(Deserialize, Clone, Debug)] +pub struct Modifier { + pub r#type: String, + pub id: String, + pub amount: f64, + pub operation: Operation, + // TODO: Make this an enum + pub slot: String, +} + +#[derive(Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "snake_case")] +#[allow(clippy::enum_variant_names)] +pub enum Operation { + AddValue, + AddMultipliedBase, + AddMultipliedTotal, +} + +pub(crate) fn build() -> TokenStream { + println!("cargo:rerun-if-changed=../assets/items.json"); + + let items: HashMap = + serde_json::from_str(include_str!("../../assets/items.json")) + .expect("Failed to parse items.json"); + + let mut type_from_raw_id_arms = TokenStream::new(); + let mut type_from_name = TokenStream::new(); + + let mut constants = TokenStream::new(); + + for (name, item) in items { + let const_ident = format_ident!("{}", name.to_shouty_snake_case()); + + 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()); + + constants.extend(quote! { + pub const #const_ident: Item = Item { + id: #id_lit, + components: #components_tokens + }; + }); + + type_from_raw_id_arms.extend(quote! { + #id_lit => Some(Self::#const_ident), + }); + + type_from_name.extend(quote! { + #name => Some(Self::#const_ident), + }); + } + + quote! { + use pumpkin_util::text::TextComponent; + + #[derive(Clone, Copy, Debug)] + pub struct Item { + pub id: u16, + pub components: ItemComponents, + } + + #[derive(Clone, Copy, Debug)] + pub struct ItemComponents { + pub item_name: Option<&'static str>, + pub max_stack_size: u8, + pub jukebox_playable: Option, + pub damage: Option, + pub max_damage: Option, + pub attribute_modifiers: Option, + } + + #[derive(Clone, Copy, Debug)] + pub struct JukeboxPlayable { + pub song: &'static str, + } + + #[derive(Clone, Copy, Debug)] + pub struct AttributeModifiers { + pub modifiers: &'static [Modifier], + } + + #[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, + } + + impl Item { + #constants + + pub fn translated_name(&self) -> TextComponent { + serde_json::from_str(self.components.item_name.unwrap()).expect("Could not parse item name.") + } + + #[doc = r" Try to parse a Item from a resource location string"] + pub fn from_name(name: &str) -> Option { + match name { + #type_from_name + _ => None + } + } + #[doc = r" Try to parse a Item from a raw id"] + pub const fn from_id(id: u16) -> Option { + match id { + #type_from_raw_id_arms + _ => None + } + } + + } + } +} diff --git a/pumpkin-data/build/particle.rs b/pumpkin-data/build/particle.rs index fb22ba1b1..d94475b60 100644 --- a/pumpkin-data/build/particle.rs +++ b/pumpkin-data/build/particle.rs @@ -8,7 +8,7 @@ pub(crate) fn build() -> TokenStream { let particle: Vec = serde_json::from_str(include_str!("../../assets/particles.json")) .expect("Failed to parse particles.json"); - let variants = array_to_tokenstream(particle); + let variants = array_to_tokenstream(&particle); quote! { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/pumpkin-data/build/scoreboard_slot.rs b/pumpkin-data/build/scoreboard_slot.rs index b8146d7ee..426b235bb 100644 --- a/pumpkin-data/build/scoreboard_slot.rs +++ b/pumpkin-data/build/scoreboard_slot.rs @@ -9,7 +9,7 @@ pub(crate) fn build() -> TokenStream { let sound_categories: Vec = serde_json::from_str(include_str!("../../assets/scoreboard_display_slot.json")) .expect("Failed to parse scoreboard_display_slot.json"); - let variants = array_to_tokenstream(sound_categories); + let variants = array_to_tokenstream(&sound_categories); quote! { pub enum ScoreboardDisplaySlot { diff --git a/pumpkin-data/build/screen.rs b/pumpkin-data/build/screen.rs index 5fb826dc0..7c55075c2 100644 --- a/pumpkin-data/build/screen.rs +++ b/pumpkin-data/build/screen.rs @@ -8,7 +8,7 @@ pub(crate) fn build() -> TokenStream { let screens: Vec = serde_json::from_str(include_str!("../../assets/screens.json")) .expect("Failed to parse screens.json"); - let variants = array_to_tokenstream(screens); + let variants = array_to_tokenstream(&screens); quote! { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] diff --git a/pumpkin-data/build/sound.rs b/pumpkin-data/build/sound.rs index dfb74001f..43aed5952 100644 --- a/pumpkin-data/build/sound.rs +++ b/pumpkin-data/build/sound.rs @@ -9,7 +9,7 @@ pub(crate) fn build() -> TokenStream { let sound: Vec = serde_json::from_str(include_str!("../../assets/sounds.json")) .expect("Failed to parse sounds.json"); - let variants = array_to_tokenstream(sound.clone()); + let variants = array_to_tokenstream(&sound); let type_from_name = &sound .iter() diff --git a/pumpkin-data/build/sound_category.rs b/pumpkin-data/build/sound_category.rs index cf10c80f2..837d9a052 100644 --- a/pumpkin-data/build/sound_category.rs +++ b/pumpkin-data/build/sound_category.rs @@ -9,7 +9,7 @@ pub(crate) fn build() -> TokenStream { let sound_categories: Vec = serde_json::from_str(include_str!("../../assets/sound_category.json")) .expect("Failed to parse sound_category.json"); - let variants = array_to_tokenstream(sound_categories); + let variants = array_to_tokenstream(&sound_categories); quote! { #[derive(Clone, Copy)] diff --git a/pumpkin-data/build/spawn_egg.rs b/pumpkin-data/build/spawn_egg.rs new file mode 100644 index 000000000..cf9e220d1 --- /dev/null +++ b/pumpkin-data/build/spawn_egg.rs @@ -0,0 +1,29 @@ +use std::collections::HashMap; + +use heck::ToShoutySnakeCase; +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +pub(crate) fn build() -> TokenStream { + println!("cargo:rerun-if-changed=../assets/spawn_egg.json"); + + let eggs: HashMap = + serde_json::from_str(include_str!("../../assets/spawn_egg.json")) + .expect("Failed to parse spawn_egg.json"); + let mut names = TokenStream::new(); + + for (egg, entity) in &eggs { + let entity = entity.to_shouty_snake_case(); + let entity = format_ident!("{}", entity); + names.extend(quote! { #egg => Some(EntityType::#entity), }); + } + quote! { + + pub fn entity_from_egg(id: u16) -> Option { + match id { + #names + _ => None + } + } + } +} diff --git a/pumpkin-data/src/lib.rs b/pumpkin-data/src/lib.rs index 3ea2ad79e..5c3df13fe 100644 --- a/pumpkin-data/src/lib.rs +++ b/pumpkin-data/src/lib.rs @@ -1,3 +1,7 @@ +pub mod item { + include!(concat!(env!("OUT_DIR"), "/item.rs")); +} + pub mod packet { include!(concat!(env!("OUT_DIR"), "/packet.rs")); } @@ -26,6 +30,7 @@ pub mod game_event { } pub mod entity { + include!(concat!(env!("OUT_DIR"), "/spawn_egg.rs")); include!(concat!(env!("OUT_DIR"), "/entity_type.rs")); include!(concat!(env!("OUT_DIR"), "/entity_pose.rs")); } diff --git a/pumpkin-inventory/src/crafting.rs b/pumpkin-inventory/src/crafting.rs index 640427ddf..713012d35 100644 --- a/pumpkin-inventory/src/crafting.rs +++ b/pumpkin-inventory/src/crafting.rs @@ -1,7 +1,7 @@ +use pumpkin_data::item::Item; use pumpkin_registry::{ flatten_3x3, get_tag_values, IngredientSlot, IngredientType, RecipeResult, TagCategory, RECIPES, }; -use pumpkin_world::item::registry::get_item; use pumpkin_world::item::ItemStack; use rayon::prelude::*; @@ -17,7 +17,9 @@ fn check_ingredient_type(ingredient_type: &IngredientType, input: ItemStack) -> .iter() .any(|tag| check_ingredient_type(&tag.to_ingredient_type(), input)) } - IngredientType::Item(item) => get_item(item).is_some_and(|item| item.id == input.item_id), + IngredientType::Item(item) => { + Item::from_name(item).is_some_and(|item| item.id == input.item.id) + } } } @@ -52,11 +54,11 @@ pub fn check_if_matches_crafting(input: [[Option; 3]; 3]) -> Option Some(ItemStack { - item_id: get_item(id).unwrap().id, + item: Item::from_name(id).unwrap(), item_count: 1, }), RecipeResult::Many { id, count, .. } => Some(ItemStack { - item_id: get_item(id).unwrap().id, + item: Item::from_name(id).unwrap(), item_count: *count, }), RecipeResult::Special => None, diff --git a/pumpkin-inventory/src/drag_handler.rs b/pumpkin-inventory/src/drag_handler.rs index 2ac0b6a13..aa403772b 100644 --- a/pumpkin-inventory/src/drag_handler.rs +++ b/pumpkin-inventory/src/drag_handler.rs @@ -91,7 +91,7 @@ impl DragHandler { single_item.item_count = 1; let changing_slots = - drag.possibly_changing_slots(&slots_cloned, carried_item.item_id); + drag.possibly_changing_slots(&slots_cloned, carried_item.item.id); changing_slots.for_each(|slot| { if carried_item.item_count != 0 { carried_item.item_count -= 1; @@ -117,7 +117,7 @@ impl DragHandler { // In that specific case, follow MouseDragType::Right behaviours instead! let changing_slots = - drag.possibly_changing_slots(&slots_cloned, carried_item.item_id); + drag.possibly_changing_slots(&slots_cloned, carried_item.item.id); let amount_of_slots = changing_slots.clone().count(); let (amount_per_slot, remainder) = if amount_of_slots == 0 { // TODO: please work lol @@ -160,7 +160,7 @@ impl Drag { match slot { Some(item_slot) => { - if item_slot.item_id == carried_item_id { + if item_slot.item.id == carried_item_id { Some(*slot_index) } else { None diff --git a/pumpkin-inventory/src/lib.rs b/pumpkin-inventory/src/lib.rs index 22f25f0ba..ef2b652e9 100644 --- a/pumpkin-inventory/src/lib.rs +++ b/pumpkin-inventory/src/lib.rs @@ -36,7 +36,7 @@ pub trait Container: Sync + Send { if taking_crafted { match (all_slots[slot].as_mut(), carried_item.as_mut()) { (Some(s1), Some(s2)) => { - if s1.item_id == s2.item_id { + if s1.item.id == s2.item.id { handle_item_change(all_slots[slot], carried_item, mouse_click); } } @@ -152,7 +152,7 @@ pub fn handle_item_change( match (current_slot.as_mut(), carried_slot.as_mut()) { // Swap or combine current and carried (Some(current), Some(carried)) => { - if current.item_id == carried.item_id { + if current.item.id == carried.item.id { combine_stacks(carried_slot, current, mouse_click); } else if mouse_click == MouseClick::Left { let carried = *carried; diff --git a/pumpkin-inventory/src/player.rs b/pumpkin-inventory/src/player.rs index 6cc13316f..d89823fbe 100644 --- a/pumpkin-inventory/src/player.rs +++ b/pumpkin-inventory/src/player.rs @@ -135,7 +135,7 @@ impl PlayerInventory { pub fn get_slot_with_item(&self, item_id: u16, max_stack: u8) -> Option { for slot in 9..=44 { match &self.items[slot - 9] { - Some(item) if item.item_id == item_id && item.item_count <= max_stack => { + Some(item) if item.item.id == item_id && item.item_count <= max_stack => { return Some(slot) } _ => continue, diff --git a/pumpkin-macros/Cargo.toml b/pumpkin-macros/Cargo.toml index 9f2428886..28d6c06f5 100644 --- a/pumpkin-macros/Cargo.toml +++ b/pumpkin-macros/Cargo.toml @@ -7,6 +7,8 @@ edition.workspace = true proc-macro = true [dependencies] +pumpkin-data = { path = "../pumpkin-data" } + serde.workspace = true serde_json.workspace = true diff --git a/pumpkin-macros/src/lib.rs b/pumpkin-macros/src/lib.rs index f3561dd65..906ced9fe 100644 --- a/pumpkin-macros/src/lib.rs +++ b/pumpkin-macros/src/lib.rs @@ -1,4 +1,5 @@ use proc_macro::TokenStream; +use pumpkin_data::item::Item; use quote::quote; use syn::{ parse::{Nothing, Parser}, @@ -222,18 +223,15 @@ pub fn pumpkin_item(input: TokenStream, item: TokenStream) -> TokenStream { let input_string = input.to_string(); let packet_name = input_string.trim_matches('"'); - let packet_name_split: Vec<&str> = packet_name.split(":").collect(); - - let namespace = packet_name_split[0]; - let id = packet_name_split[1]; + let item_id = Item::from_name(packet_name).unwrap(); + let id = item_id.id; let item: proc_macro2::TokenStream = item.into(); let gen = quote! { #item impl #impl_generics crate::item::pumpkin_item::ItemMetadata for #name #ty_generics { - const NAMESPACE: &'static str = #namespace; - const ID: &'static str = #id; + const ID: u16 = #id; } }; diff --git a/pumpkin-protocol/src/codec/slot.rs b/pumpkin-protocol/src/codec/slot.rs index 8c7914a01..670194405 100644 --- a/pumpkin-protocol/src/codec/slot.rs +++ b/pumpkin-protocol/src/codec/slot.rs @@ -1,4 +1,5 @@ use crate::VarInt; +use pumpkin_data::item::Item; use pumpkin_world::item::ItemStack; use serde::ser::SerializeSeq; use serde::{ @@ -131,8 +132,9 @@ impl Serialize for Slot { impl Slot { pub fn to_item(self) -> Option { let item_id = self.item_id?.0.try_into().unwrap(); + let item = Item::from_id(item_id)?; Some(ItemStack { - item_id, + item, item_count: self.item_count.0.try_into().unwrap(), }) } @@ -153,7 +155,7 @@ impl From<&ItemStack> for Slot { fn from(item: &ItemStack) -> Self { Slot { item_count: item.item_count.into(), - item_id: Some(VarInt(item.item_id as i32)), + item_id: Some(VarInt(item.item.id as i32)), // TODO: add these num_components_to_add: None, num_components_to_remove: None, diff --git a/pumpkin-world/src/item/categories.rs b/pumpkin-world/src/item/categories.rs index 48f18c94b..301ac4129 100644 --- a/pumpkin-world/src/item/categories.rs +++ b/pumpkin-world/src/item/categories.rs @@ -10,7 +10,7 @@ impl ItemStack { 838, // Diamond 843, // Netherite ] - .contains(&self.item_id) + .contains(&self.item.id) } pub fn is_helmet(&self) -> bool { @@ -24,7 +24,7 @@ impl ItemStack { 872, // Iron 864, ] - .contains(&self.item_id) + .contains(&self.item.id) } pub fn is_chestplate(&self) -> bool { @@ -38,7 +38,7 @@ impl ItemStack { 865, // Elytra 773, ] - .contains(&self.item_id) + .contains(&self.item.id) } pub fn is_leggings(&self) -> bool { @@ -51,7 +51,7 @@ impl ItemStack { 874, // Iron 866, ] - .contains(&self.item_id) + .contains(&self.item.id) } pub fn is_boots(&self) -> bool { @@ -64,6 +64,6 @@ impl ItemStack { 875, // Iron 867, ] - .contains(&self.item_id) + .contains(&self.item.id) } } diff --git a/pumpkin-world/src/item/mod.rs b/pumpkin-world/src/item/mod.rs index fe16a8bb7..f492b8ae1 100644 --- a/pumpkin-world/src/item/mod.rs +++ b/pumpkin-world/src/item/mod.rs @@ -1,6 +1,6 @@ +use pumpkin_data::item::Item; + mod categories; -pub mod registry; -pub use registry::ITEMS; #[derive(serde::Deserialize, Debug, Clone, PartialEq, Eq)] #[serde(rename_all = "lowercase")] /// Item Rarity @@ -13,23 +13,18 @@ pub enum Rarity { #[derive(Clone, Copy, Debug)] pub struct ItemStack { - // This ID is the numerical protocol ID, not the usual minecraft::block ID. - pub item_id: u16, pub item_count: u8, - // TODO: Add Item Components + pub item: Item, } impl PartialEq for ItemStack { fn eq(&self, other: &Self) -> bool { - self.item_id == other.item_id + self.item.id == other.item.id } } impl ItemStack { - pub fn new(item_count: u8, item_id: u16) -> Self { - Self { - item_count, - item_id, - } + pub fn new(item_count: u8, item: Item) -> Self { + Self { item_count, item } } } diff --git a/pumpkin-world/src/item/registry.rs b/pumpkin-world/src/item/registry.rs deleted file mode 100644 index 99c08472b..000000000 --- a/pumpkin-world/src/item/registry.rs +++ /dev/null @@ -1,98 +0,0 @@ -use std::collections::HashMap; -use std::sync::LazyLock; - -use pumpkin_util::text::TextComponent; -use serde::Deserialize; - -const ITEMS_JSON: &str = include_str!("../../../assets/items.json"); - -pub static ITEMS: LazyLock> = LazyLock::new(|| { - serde_json::from_str(ITEMS_JSON).expect("Could not parse items.json registry.") -}); - -pub static ITEMS_REGISTRY_NAME_BY_ID: LazyLock> = LazyLock::new(|| { - let mut map = HashMap::new(); - for item in ITEMS.clone() { - map.insert(item.1.id, item.0.clone()); - } - map -}); - -pub fn get_item(name: &str) -> Option<&Item> { - ITEMS.get(&name.replace("minecraft:", "")) -} - -pub fn get_name_by_id<'a>(item_id: u16) -> Option<&'a String> { - ITEMS_REGISTRY_NAME_BY_ID.get(&item_id) -} - -pub fn get_item_by_id<'a>(item_id: u16) -> Option<&'a Item> { - ITEMS.values().find(|&item| item.id == item_id) -} - -pub fn get_spawn_egg(item_id: u16) -> Option { - if let Some(item_name) = ITEMS_REGISTRY_NAME_BY_ID.get(&item_id) { - if item_name.ends_with("_spawn_egg") { - if let Some(res) = item_name.strip_suffix("_spawn_egg") { - return Some(res.to_owned()); - } - } - }; - None -} - -#[derive(Deserialize, Clone, Debug)] -pub struct Item { - pub id: u16, - pub components: ItemComponents, -} - -impl Item { - pub fn translated_name(&self) -> TextComponent { - serde_json::from_str(&self.components.item_name).expect("Could not parse item name.") - } -} - -#[derive(Deserialize, Clone, Debug)] -pub struct ItemComponents { - #[serde(rename = "minecraft:item_name")] - pub item_name: String, - #[serde(rename = "minecraft:max_stack_size")] - pub max_stack_size: u8, - #[serde(rename = "minecraft:jukebox_playable")] - pub jukebox_playable: Option, - #[serde(rename = "minecraft:damage")] - pub damage: Option, - #[serde(rename = "minecraft:max_damage")] - pub max_damage: Option, - #[serde(rename = "minecraft:attribute_modifiers")] - pub attribute_modifiers: Option, -} - -#[derive(Deserialize, Clone, Debug)] -pub struct JukeboxPlayable { - pub song: String, -} - -#[derive(Deserialize, Clone, Debug)] -pub struct AttributeModifiers { - pub modifiers: Vec, -} - -#[derive(Deserialize, Clone, Debug)] -pub struct Modifier { - pub r#type: String, - pub id: String, - pub amount: f64, - pub operation: Operation, - // TODO: Make this an enum - pub slot: String, -} - -#[derive(Deserialize, Clone, Debug, PartialEq)] -#[serde(rename_all = "snake_case")] -pub enum Operation { - AddValue, - AddMultipliedBase, - AddMultipliedTotal, -} diff --git a/pumpkin/src/block/blocks/chest.rs b/pumpkin/src/block/blocks/chest.rs index b14f4b446..e062c7f24 100644 --- a/pumpkin/src/block/blocks/chest.rs +++ b/pumpkin/src/block/blocks/chest.rs @@ -1,4 +1,5 @@ use async_trait::async_trait; +use pumpkin_data::item::Item; use pumpkin_data::{ screen::WindowType, sound::{Sound, SoundCategory}, @@ -7,10 +8,7 @@ use pumpkin_inventory::{Chest, OpenContainer}; use pumpkin_macros::pumpkin_block; use pumpkin_protocol::{client::play::CBlockAction, codec::var_int::VarInt}; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::{ - block::registry::{get_block, Block}, - item::registry::Item, -}; +use pumpkin_world::block::registry::{get_block, Block}; use crate::{ block::{pumpkin_block::PumpkinBlock, registry::BlockActionResult}, diff --git a/pumpkin/src/block/blocks/crafting_table.rs b/pumpkin/src/block/blocks/crafting_table.rs index 2d0162b22..bec3bc751 100644 --- a/pumpkin/src/block/blocks/crafting_table.rs +++ b/pumpkin/src/block/blocks/crafting_table.rs @@ -3,11 +3,12 @@ use crate::block::registry::BlockActionResult; use crate::entity::player::Player; use crate::server::Server; use async_trait::async_trait; +use pumpkin_data::item::Item; use pumpkin_data::screen::WindowType; use pumpkin_inventory::{CraftingTable, OpenContainer}; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; -use pumpkin_world::{block::registry::Block, item::registry::Item}; +use pumpkin_world::block::registry::Block; #[pumpkin_block("minecraft:crafting_table")] pub struct CraftingTableBlock; diff --git a/pumpkin/src/block/blocks/furnace.rs b/pumpkin/src/block/blocks/furnace.rs index 99b829ceb..160466ef7 100644 --- a/pumpkin/src/block/blocks/furnace.rs +++ b/pumpkin/src/block/blocks/furnace.rs @@ -1,12 +1,12 @@ use crate::block::registry::BlockActionResult; use crate::entity::player::Player; use async_trait::async_trait; +use pumpkin_data::item::Item; use pumpkin_data::screen::WindowType; use pumpkin_inventory::Furnace; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::registry::Block; -use pumpkin_world::item::registry::Item; use crate::{block::pumpkin_block::PumpkinBlock, server::Server}; diff --git a/pumpkin/src/block/blocks/jukebox.rs b/pumpkin/src/block/blocks/jukebox.rs index dc58f22cd..3abc5ebc9 100644 --- a/pumpkin/src/block/blocks/jukebox.rs +++ b/pumpkin/src/block/blocks/jukebox.rs @@ -3,11 +3,11 @@ use crate::block::registry::BlockActionResult; use crate::entity::player::Player; use crate::server::Server; use async_trait::async_trait; +use pumpkin_data::item::Item; use pumpkin_macros::pumpkin_block; use pumpkin_registry::SYNCED_REGISTRIES; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::registry::Block; -use pumpkin_world::item::registry::Item; #[pumpkin_block("minecraft:jukebox")] pub struct JukeboxBlock; diff --git a/pumpkin/src/block/mod.rs b/pumpkin/src/block/mod.rs index bedc8430c..4d30959fa 100644 --- a/pumpkin/src/block/mod.rs +++ b/pumpkin/src/block/mod.rs @@ -2,6 +2,7 @@ use blocks::chest::ChestBlock; use blocks::furnace::FurnaceBlock; use properties::BlockPropertiesManager; use pumpkin_data::entity::EntityType; +use pumpkin_data::item::Item; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::block::registry::Block; @@ -43,7 +44,10 @@ pub async fn drop_loot(server: &Server, world: &Arc, block: &Block, pos: ); let entity = server.add_entity(pos, EntityType::ITEM, world); - let item_entity = Arc::new(ItemEntity::new(entity, &ItemStack::new(1, block.item_id))); + let item_entity = Arc::new(ItemEntity::new( + entity, + &ItemStack::new(1, Item::from_id(block.item_id).unwrap()), + )); world.spawn_entity(item_entity.clone()).await; item_entity.send_meta_packet().await; } diff --git a/pumpkin/src/block/pumpkin_block.rs b/pumpkin/src/block/pumpkin_block.rs index 3a15237cf..647bc53d4 100644 --- a/pumpkin/src/block/pumpkin_block.rs +++ b/pumpkin/src/block/pumpkin_block.rs @@ -2,10 +2,10 @@ use crate::block::registry::BlockActionResult; use crate::entity::player::Player; use crate::server::Server; use async_trait::async_trait; +use pumpkin_data::item::Item; use pumpkin_inventory::OpenContainer; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::registry::Block; -use pumpkin_world::item::registry::Item; pub trait BlockMetadata { const NAMESPACE: &'static str; diff --git a/pumpkin/src/block/registry.rs b/pumpkin/src/block/registry.rs index 46c4ca31c..a33aca6ad 100644 --- a/pumpkin/src/block/registry.rs +++ b/pumpkin/src/block/registry.rs @@ -1,10 +1,10 @@ use crate::block::pumpkin_block::{BlockMetadata, PumpkinBlock}; use crate::entity::player::Player; use crate::server::Server; +use pumpkin_data::item::Item; use pumpkin_inventory::OpenContainer; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::registry::Block; -use pumpkin_world::item::registry::Item; use std::collections::HashMap; use std::sync::Arc; diff --git a/pumpkin/src/command/args/damage_type.rs b/pumpkin/src/command/args/damage_type.rs index e40db91a5..5c9df7255 100644 --- a/pumpkin/src/command/args/damage_type.rs +++ b/pumpkin/src/command/args/damage_type.rs @@ -52,7 +52,7 @@ impl ArgumentConsumer for DamageTypeArgumentConsumer { impl DefaultNameArgConsumer for DamageTypeArgumentConsumer { fn default_name(&self) -> &'static str { - "damage_type" + "damageType" } } diff --git a/pumpkin/src/command/args/item.rs b/pumpkin/src/command/args/item.rs index faa3ac740..81f61d723 100644 --- a/pumpkin/src/command/args/item.rs +++ b/pumpkin/src/command/args/item.rs @@ -1,6 +1,6 @@ use async_trait::async_trait; +use pumpkin_data::item::Item; use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; -use pumpkin_world::item::registry::{self, Item}; use crate::{command::dispatcher::CommandError, server::Server}; @@ -53,11 +53,11 @@ impl DefaultNameArgConsumer for ItemArgumentConsumer { } impl<'a> FindArg<'a> for ItemArgumentConsumer { - type Data = (&'a str, &'a Item); + type Data = (&'a str, Item); fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result { match args.get(name) { - Some(Arg::Item(name)) => registry::get_item(name).map_or_else( + Some(Arg::Item(name)) => Item::from_name(&name.replace("minecraft:", "")).map_or_else( || { Err(CommandError::GeneralCommandIssue(format!( "Item {name} does not exist." diff --git a/pumpkin/src/command/commands/give.rs b/pumpkin/src/command/commands/give.rs index b56a8060c..ddca7e4c2 100644 --- a/pumpkin/src/command/commands/give.rs +++ b/pumpkin/src/command/commands/give.rs @@ -56,7 +56,6 @@ impl CommandExecutor for GiveExecutor { for target in targets { target.give_items(item, item_count as u32).await; } - let msg = if targets.len() == 1 { TextComponent::translate( "commands.give.success.single", diff --git a/pumpkin/src/entity/item.rs b/pumpkin/src/entity/item.rs index 5166445fc..53bac1b3d 100644 --- a/pumpkin/src/entity/item.rs +++ b/pumpkin/src/entity/item.rs @@ -22,7 +22,7 @@ impl ItemEntity { let slot = Slot::from(stack); Self { entity, - id: stack.item_id, + id: stack.item.id, count: stack.item_count, item: slot, pickup_delay: AtomicI8::new(10), // Vanilla diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index e0131c34e..02ba51aa3 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -13,6 +13,7 @@ use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_data::{ damage::DamageType, entity::EntityType, + item::Operation, sound::{Sound, SoundCategory}, }; use pumpkin_inventory::player::PlayerInventory; @@ -57,13 +58,7 @@ use pumpkin_util::{ text::TextComponent, GameMode, }; -use pumpkin_world::{ - cylindrical_chunk_iterator::Cylindrical, - item::{ - registry::{get_item_by_id, Operation}, - ItemStack, - }, -}; +use pumpkin_world::{cylindrical_chunk_iterator::Cylindrical, item::ItemStack}; use tokio::sync::{Mutex, Notify, RwLock}; use super::{ @@ -294,17 +289,15 @@ impl Player { // get attack damage if let Some(item_stack) = item_slot { - if let Some(item) = get_item_by_id(item_stack.item_id) { - // TODO: this should be cached in memory - if let Some(modifiers) = &item.components.attribute_modifiers { - for item_mod in &modifiers.modifiers { - 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" { - add_speed = item_mod.amount; - } + // TODO: this should be cached in memory + if let Some(modifiers) = &item_stack.item.components.attribute_modifiers { + for item_mod in modifiers.modifiers { + 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" { + add_speed = item_mod.amount; } } } diff --git a/pumpkin/src/item/items/egg.rs b/pumpkin/src/item/items/egg.rs index e040c0c0c..6c3a74ee0 100644 --- a/pumpkin/src/item/items/egg.rs +++ b/pumpkin/src/item/items/egg.rs @@ -6,10 +6,11 @@ use crate::item::pumpkin_item::PumpkinItem; use crate::server::Server; use async_trait::async_trait; use pumpkin_data::entity::EntityType; +use pumpkin_data::item::Item; use pumpkin_data::sound::Sound; use pumpkin_macros::pumpkin_item; -use pumpkin_world::item::registry::Item; -#[pumpkin_item("minecraft:egg")] + +#[pumpkin_item("egg")] pub struct EggItem; const POWER: f32 = 1.5; diff --git a/pumpkin/src/item/items/snowball.rs b/pumpkin/src/item/items/snowball.rs index a944ec39f..d03b62777 100644 --- a/pumpkin/src/item/items/snowball.rs +++ b/pumpkin/src/item/items/snowball.rs @@ -6,10 +6,11 @@ use crate::item::pumpkin_item::PumpkinItem; use crate::server::Server; use async_trait::async_trait; use pumpkin_data::entity::EntityType; +use pumpkin_data::item::Item; use pumpkin_data::sound::Sound; use pumpkin_macros::pumpkin_item; -use pumpkin_world::item::registry::Item; -#[pumpkin_item("minecraft:snowball")] + +#[pumpkin_item("snowball")] pub struct SnowBallItem; const POWER: f32 = 1.5; diff --git a/pumpkin/src/item/pumpkin_item.rs b/pumpkin/src/item/pumpkin_item.rs index 52ec40408..11ba9c6e6 100644 --- a/pumpkin/src/item/pumpkin_item.rs +++ b/pumpkin/src/item/pumpkin_item.rs @@ -1,16 +1,12 @@ use crate::entity::player::Player; use crate::server::Server; use async_trait::async_trait; +use pumpkin_data::item::Item; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::registry::Block; -use pumpkin_world::item::registry::Item; pub trait ItemMetadata { - const NAMESPACE: &'static str; - const ID: &'static str; - fn name(&self) -> String { - format!("{}:{}", Self::NAMESPACE, Self::ID) - } + const ID: u16; } #[async_trait] diff --git a/pumpkin/src/item/registry.rs b/pumpkin/src/item/registry.rs index c604b3b6e..4d7394d8b 100644 --- a/pumpkin/src/item/registry.rs +++ b/pumpkin/src/item/registry.rs @@ -1,8 +1,8 @@ use crate::entity::player::Player; use crate::server::Server; +use pumpkin_data::item::Item; use pumpkin_util::math::position::BlockPos; use pumpkin_world::block::registry::Block; -use pumpkin_world::item::registry::Item; use std::collections::HashMap; use std::sync::Arc; @@ -10,16 +10,16 @@ use super::pumpkin_item::{ItemMetadata, PumpkinItem}; #[derive(Default)] pub struct ItemRegistry { - blocks: HashMap>, + items: HashMap>, } impl ItemRegistry { - pub fn register(&mut self, block: T) { - self.blocks.insert(block.name(), Arc::new(block)); + pub fn register(&mut self, item: T) { + self.items.insert(T::ID, Arc::new(item)); } - pub async fn on_use(&self, item_name: &str, item: &Item, player: &Player, server: &Server) { - let pumpkin_block = self.get_pumpkin_item(item_name); + pub async fn on_use(&self, item: &Item, player: &Player, server: &Server) { + let pumpkin_block = self.get_pumpkin_item(item.id); if let Some(pumpkin_block) = pumpkin_block { pumpkin_block.normal_use(item, player, server).await; } @@ -27,23 +27,22 @@ impl ItemRegistry { pub async fn use_on_block( &self, - item_name: &str, item: &Item, player: &Player, location: BlockPos, block: &Block, server: &Server, ) { - let pumpkin_block = self.get_pumpkin_item(item_name); - if let Some(pumpkin_block) = pumpkin_block { - return pumpkin_block + let pumpkin_item = self.get_pumpkin_item(item.id); + if let Some(pumpkin_item) = pumpkin_item { + return pumpkin_item .use_on_block(item, player, location, block, server) .await; } } #[must_use] - pub fn get_pumpkin_item(&self, item_name: &str) -> Option<&Arc> { - self.blocks.get(format!("minecraft:{item_name}").as_str()) + pub fn get_pumpkin_item(&self, item_id: u16) -> Option<&Arc> { + self.items.get(&item_id) } } diff --git a/pumpkin/src/net/container.rs b/pumpkin/src/net/container.rs index b6380bf08..34c4c3932 100644 --- a/pumpkin/src/net/container.rs +++ b/pumpkin/src/net/container.rs @@ -1,5 +1,6 @@ use crate::entity::player::Player; use crate::server::Server; +use pumpkin_data::item::Item; use pumpkin_data::screen::WindowType; use pumpkin_inventory::container_click::{ Click, ClickType, KeyClick, MouseClick, MouseDragState, MouseDragType, @@ -16,7 +17,6 @@ use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::server::play::SClickContainer; use pumpkin_util::text::TextComponent; use pumpkin_util::GameMode; -use pumpkin_world::item::registry::Item; use pumpkin_world::item::ItemStack; use std::sync::Arc; @@ -323,7 +323,7 @@ impl Player { let find_condition = |(slot_number, slot): (usize, &mut Option)| { // TODO: Check for max item count here match slot { - Some(item) => (item.item_id == item_in_pressed_slot.item_id + Some(item) => (item.item.id == item_in_pressed_slot.item.id && item.item_count != 64) .then_some(slot_number), None => Some(slot_number), @@ -411,7 +411,7 @@ impl Player { **item = None; for slot in slots.iter_mut().filter_map(|slot| slot.as_mut()) { - if slot.item_id == carried_item.item_id { + if slot.item.id == carried_item.item.id { // TODO: Check for max stack size if slot.item_count + carried_item.item_count <= 64 { slot.item_count = 0; @@ -553,14 +553,14 @@ impl Player { } } - async fn pickup_items(&self, item: &Item, mut amount: u32) { + async fn pickup_items(&self, item: Item, mut amount: u32) { let max_stack = item.components.max_stack_size; let mut inventory = self.inventory().lock().await; let slots = inventory.slots_with_hotbar_first(); let matching_slots = slots.filter_map(|slot| { if let Some(item_slot) = slot.as_mut() { - (item_slot.item_id == item.id && item_slot.item_count < max_stack).then(|| { + (item_slot.item.id == item.id && item_slot.item_count < max_stack).then(|| { let item_count = item_slot.item_count; (item_slot, item_count) }) @@ -577,12 +577,12 @@ impl Player { if let Some(amount_left) = amount.checked_sub(u32::from(amount_to_add)) { amount = amount_left; *slot = ItemStack { - item_id: item.id, + item, item_count: item.components.max_stack_size, }; } else { *slot = ItemStack { - item_id: item.id, + item, item_count: max_stack - (amount_to_add - amount as u8), }; return; @@ -599,12 +599,12 @@ impl Player { if let Some(remaining_amount) = amount.checked_sub(u32::from(max_stack)) { amount = remaining_amount; *slot = Some(ItemStack { - item_id: item.id, + item, item_count: max_stack, }); } else { *slot = Some(ItemStack { - item_id: item.id, + item, item_count: amount as u8, }); return; @@ -618,7 +618,7 @@ impl Player { /// Add items to inventory if there's space, else drop them to the ground. /// /// This method automatically syncs changes with the client. - pub async fn give_items(&self, item: &Item, amount: u32) { + pub async fn give_items(&self, item: Item, amount: u32) { self.pickup_items(item, amount).await; self.set_container_content(None).await; } diff --git a/pumpkin/src/net/packet/play.rs b/pumpkin/src/net/packet/play.rs index 6e772feac..5d5efb9c3 100644 --- a/pumpkin/src/net/packet/play.rs +++ b/pumpkin/src/net/packet/play.rs @@ -13,7 +13,8 @@ use crate::{ world::chunker, }; use pumpkin_config::ADVANCED_CONFIG; -use pumpkin_data::entity::{EntityPose, EntityType}; +use pumpkin_data::entity::{entity_from_egg, EntityPose, EntityType}; +use pumpkin_data::item::Item; use pumpkin_data::world::CHAT; use pumpkin_inventory::player::PlayerInventory; use pumpkin_inventory::InventoryError; @@ -49,12 +50,8 @@ use pumpkin_util::{ use pumpkin_world::block::interactive::sign::Sign; use pumpkin_world::block::registry::get_block_collision_shapes; use pumpkin_world::block::registry::Block; -use pumpkin_world::item::registry::{get_item_by_id, get_name_by_id}; +use pumpkin_world::block::{registry::get_block_by_item, BlockDirection}; use pumpkin_world::item::ItemStack; -use pumpkin_world::{ - block::{registry::get_block_by_item, BlockDirection}, - item::registry::get_spawn_egg, -}; use pumpkin_world::{WORLD_LOWEST_Y, WORLD_MAX_Y}; use thiserror::Error; @@ -464,7 +461,7 @@ impl Player { } None if self.gamemode.load() == GameMode::Creative => { // Case where item is not present, if in creative mode create the item - let item_stack = ItemStack::new(1, block.item_id); + let item_stack = ItemStack::new(1, Item::from_id(block.item_id).unwrap()); let slot_data = Slot::from(&item_stack); self.update_single_slot(&mut inventory, dest_slot as i16 + 36, slot_data) .await; @@ -975,11 +972,6 @@ impl Player { } return Ok(()); }; - let Some(item) = get_item_by_id(stack.item_id) else { - // If there is an item stack, there should be an item - return Err(BlockPlacingError::InventoryInvalid.into()); - }; - if !self .living_entity .entity @@ -988,7 +980,7 @@ impl Player { { let action_result = server .block_registry - .use_with_item(block, self, location, item, server) + .use_with_item(block, self, location, &stack.item, server) .await; match action_result { BlockActionResult::Continue => {} @@ -998,16 +990,16 @@ impl Player { } } // check if item is a block, Because Not every item can be placed :D - if let Some(block) = get_block_by_item(stack.item_id) { + if let Some(block) = get_block_by_item(stack.item.id) { should_try_decrement = self .run_is_block_place(block.clone(), server, use_item_on, location, &face) .await?; } // check if item is a spawn egg - if let Some(item_t) = get_spawn_egg(stack.item_id) { - should_try_decrement = self - .run_is_spawn_egg(item_t, server, location, &face) - .await?; + if let Some(entity) = entity_from_egg(stack.item.id) { + self.spawn_entity_from_egg(entity, server, location, &face) + .await; + should_try_decrement = true; }; if should_try_decrement { @@ -1062,11 +1054,7 @@ impl Player { return; } if let Some(held) = self.inventory().lock().await.held_item() { - // this is so ugly and slow brooo... - if let Some(item) = get_item_by_id(held.item_id) { - let name = get_name_by_id(item.id).unwrap(); - server.item_registry.on_use(name, item, self, server).await; - } + server.item_registry.on_use(&held.item, self, server).await; } } @@ -1170,48 +1158,40 @@ impl Player { ); } - async fn run_is_spawn_egg( + async fn spawn_entity_from_egg( &self, - item_t: String, + entity_type: EntityType, server: &Server, location: BlockPos, face: &BlockDirection, - ) -> Result> { - // checks if spawn egg has a corresponding entity name - if let Some(entity_type) = EntityType::from_name(&item_t) { - let world_pos = BlockPos(location.0 + face.to_offset()); - // align position like Vanilla does - let pos = Vector3::new( - f64::from(world_pos.0.x) + 0.5, - f64::from(world_pos.0.y), - f64::from(world_pos.0.z) + 0.5, - ); - // create rotation like Vanilla - let yaw = wrap_degrees(rand::random::() * 360.0) % 360.0; + ) { + let world_pos = BlockPos(location.0 + face.to_offset()); + // align position like Vanilla does + let pos = Vector3::new( + f64::from(world_pos.0.x) + 0.5, + f64::from(world_pos.0.y), + f64::from(world_pos.0.z) + 0.5, + ); + // create rotation like Vanilla + let yaw = wrap_degrees(rand::random::() * 360.0) % 360.0; - let world = self.world(); - // create new mob and uuid based on spawn egg id - let mob = mob::from_type( - EntityType::from_raw(entity_type.id).unwrap(), - server, - pos, - world, - ) - .await; + let world = self.world(); + // create new mob and uuid based on spawn egg id + let mob = mob::from_type( + EntityType::from_raw(entity_type.id).unwrap(), + server, + pos, + world, + ) + .await; - // set the rotation - mob.get_entity().set_rotation(yaw, 0.0); + // set the rotation + mob.get_entity().set_rotation(yaw, 0.0); - // broadcast new mob to all players - world.spawn_entity(mob).await; + // broadcast new mob to all players + world.spawn_entity(mob).await; - // TODO: send/configure additional commands/data based on type of entity (horse, slime, etc) - } else { - // TODO: maybe include additional error types - return Ok(false); - }; - - Ok(true) + // TODO: send/configure additional commands/data based on type of entity (horse, slime, etc) } fn get_player_direction(&self) -> Direction {