Parse Items at Compile time
This commit is contained in:
parent
5c5bc67b3a
commit
8592d095f3
1
assets/spawn_egg.json
Normal file
1
assets/spawn_egg.json
Normal file
@ -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"}
|
||||
@ -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" }
|
||||
|
||||
@ -7,8 +7,8 @@ pub(crate) fn build() -> TokenStream {
|
||||
println!("cargo:rerun-if-changed=../assets/biome.json");
|
||||
|
||||
let biomes: Vec<String> = 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)]
|
||||
|
||||
@ -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<String>) -> TokenStream {
|
||||
pub fn array_to_tokenstream(array: &[String]) -> TokenStream {
|
||||
let mut variants = TokenStream::new();
|
||||
|
||||
for item in array.iter() {
|
||||
|
||||
@ -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<String>,
|
||||
death_message_type: Option<DeathMessageType>,
|
||||
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<DeathMessageType>,
|
||||
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)*
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
let poses: Vec<String> = 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)]
|
||||
|
||||
@ -9,7 +9,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
let game_events: Vec<String> =
|
||||
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 {
|
||||
|
||||
239
pumpkin-data/build/item.rs
Normal file
239
pumpkin-data/build/item.rs
Normal file
@ -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<String>,
|
||||
#[serde(rename = "minecraft:max_stack_size")]
|
||||
pub max_stack_size: u8,
|
||||
#[serde(rename = "minecraft:jukebox_playable")]
|
||||
pub jukebox_playable: Option<JukeboxPlayable>,
|
||||
#[serde(rename = "minecraft:damage")]
|
||||
pub damage: Option<u16>,
|
||||
#[serde(rename = "minecraft:max_damage")]
|
||||
pub max_damage: Option<u16>,
|
||||
#[serde(rename = "minecraft:attribute_modifiers")]
|
||||
pub attribute_modifiers: Option<AttributeModifiers>,
|
||||
}
|
||||
|
||||
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<Modifier>,
|
||||
}
|
||||
|
||||
#[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<String, Item> =
|
||||
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<JukeboxPlayable>,
|
||||
pub damage: Option<u16>,
|
||||
pub max_damage: Option<u16>,
|
||||
pub attribute_modifiers: Option<AttributeModifiers>,
|
||||
}
|
||||
|
||||
#[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<Self> {
|
||||
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<Self> {
|
||||
match id {
|
||||
#type_from_raw_id_arms
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
let particle: Vec<String> = 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)]
|
||||
|
||||
@ -9,7 +9,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
let sound_categories: Vec<String> =
|
||||
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 {
|
||||
|
||||
@ -8,7 +8,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
let screens: Vec<String> = 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)]
|
||||
|
||||
@ -9,7 +9,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
|
||||
let sound: Vec<String> = 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()
|
||||
|
||||
@ -9,7 +9,7 @@ pub(crate) fn build() -> TokenStream {
|
||||
let sound_categories: Vec<String> =
|
||||
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)]
|
||||
|
||||
29
pumpkin-data/build/spawn_egg.rs
Normal file
29
pumpkin-data/build/spawn_egg.rs
Normal file
@ -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<u16, String> =
|
||||
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<EntityType> {
|
||||
match id {
|
||||
#names
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
|
||||
@ -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<ItemStack>; 3]; 3]) -> Option<I
|
||||
})
|
||||
.map(|recipe| match recipe.result() {
|
||||
RecipeResult::Single { id, .. } => 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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -135,7 +135,7 @@ impl PlayerInventory {
|
||||
pub fn get_slot_with_item(&self, item_id: u16, max_stack: u8) -> Option<usize> {
|
||||
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,
|
||||
|
||||
@ -7,6 +7,8 @@ edition.workspace = true
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
pumpkin-data = { path = "../pumpkin-data" }
|
||||
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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<ItemStack> {
|
||||
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,
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 }
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<HashMap<String, Item>> = LazyLock::new(|| {
|
||||
serde_json::from_str(ITEMS_JSON).expect("Could not parse items.json registry.")
|
||||
});
|
||||
|
||||
pub static ITEMS_REGISTRY_NAME_BY_ID: LazyLock<HashMap<u16, String>> = 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<String> {
|
||||
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<JukeboxPlayable>,
|
||||
#[serde(rename = "minecraft:damage")]
|
||||
pub damage: Option<u16>,
|
||||
#[serde(rename = "minecraft:max_damage")]
|
||||
pub max_damage: Option<u16>,
|
||||
#[serde(rename = "minecraft:attribute_modifiers")]
|
||||
pub attribute_modifiers: Option<AttributeModifiers>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct JukeboxPlayable {
|
||||
pub song: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct AttributeModifiers {
|
||||
pub modifiers: Vec<Modifier>,
|
||||
}
|
||||
|
||||
#[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,
|
||||
}
|
||||
@ -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},
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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};
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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<World>, 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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ impl ArgumentConsumer for DamageTypeArgumentConsumer {
|
||||
|
||||
impl DefaultNameArgConsumer for DamageTypeArgumentConsumer {
|
||||
fn default_name(&self) -> &'static str {
|
||||
"damage_type"
|
||||
"damageType"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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<Self::Data, CommandError> {
|
||||
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."
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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<String, Arc<dyn PumpkinItem>>,
|
||||
items: HashMap<u16, Arc<dyn PumpkinItem>>,
|
||||
}
|
||||
|
||||
impl ItemRegistry {
|
||||
pub fn register<T: PumpkinItem + ItemMetadata + 'static>(&mut self, block: T) {
|
||||
self.blocks.insert(block.name(), Arc::new(block));
|
||||
pub fn register<T: PumpkinItem + ItemMetadata + 'static>(&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<dyn PumpkinItem>> {
|
||||
self.blocks.get(format!("minecraft:{item_name}").as_str())
|
||||
pub fn get_pumpkin_item(&self, item_id: u16) -> Option<&Arc<dyn PumpkinItem>> {
|
||||
self.items.get(&item_id)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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<ItemStack>)| {
|
||||
// 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;
|
||||
}
|
||||
|
||||
@ -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<bool, Box<dyn PumpkinError>> {
|
||||
// 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::<f32>() * 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::<f32>() * 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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user