mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
* replace slot with ItemStackSeralizer * Dismantle current inv logic * Disassemble even more, items need fixing markd with TODO: Inv * Player inventory methods implmented * make some fields private * remove invalid comment * Partial screen handler and slot * Add armorslot * prepare for crafting * implement some more crafting traits * Fix ItemStack * Fix pickup and remove faulty crafting (getting reworked in inv revamp) * fix * fix error * add readme * move readme file * asd * remove readme * add uppsercase readme * Some more work * merge * Move from generics to dynamics * fix * move to same impl * Some changes * add player open logic * Basic impl of sync and listeners * Why did mojang complicate it so much * bruh * Simplify things with DefaultScreenHandlerBehaviour * Implement partial slot clicks * add * itemhashes are broken * Start implementing packets * fix deadlock * new vanilla update * Use static item refs and use Arc Mutex on invs * working normal click * fix deadlock * implement sync handler * small patch * Implement quick_move * Implement Swap click type * pickup, update bug * start fixing updates * fix pickup desync * Make Inventories non mutexes * Implement some finishing stuff * close screen on player leave * fix pick item * Move to item hashes * remove some logs * begin implementing barrels * Begin implementing barrel * implement barrels * fix clippy + typos * Fix merge conflicts * Fix lint error * don't use unwrap when getting equipment slot * Fix typo and single threaded * Accidentally removed tokio completley * Fix crash when opening barrel holding item * Add support for marking block entities dirty * Player Equipment changes * Make ScreenHandlerFactory own inventory instead of passing in optional * remove log * replace todo's * fix clippy --------- Co-authored-by: Alexander Medvedev <lilalexmed@proton.me>
366 lines
12 KiB
Rust
366 lines
12 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use heck::ToShoutySnakeCase;
|
|
use proc_macro2::{Span, TokenStream};
|
|
use pumpkin_util::{registry::RegistryEntryList, text::TextComponent};
|
|
use quote::{ToTokens, format_ident, quote};
|
|
use serde::Deserialize;
|
|
use syn::{Ident, LitBool, LitFloat, 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")]
|
|
pub item_name: TextComponent,
|
|
#[serde(rename = "minecraft:max_stack_size")]
|
|
pub max_stack_size: u8,
|
|
#[serde(rename = "minecraft:jukebox_playable")]
|
|
pub jukebox_playable: Option<String>,
|
|
#[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<Vec<Modifier>>,
|
|
#[serde(rename = "minecraft:tool")]
|
|
pub tool: Option<ToolComponent>,
|
|
#[serde(rename = "minecraft:food")]
|
|
pub food: Option<FoodComponent>,
|
|
}
|
|
|
|
impl ToTokens for ItemComponents {
|
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
|
let max_stack_size = LitInt::new(&self.max_stack_size.to_string(), Span::call_site());
|
|
let jukebox_playable = match &self.jukebox_playable {
|
|
Some(playable) => {
|
|
let song = LitStr::new(playable, Span::call_site());
|
|
quote! { Some(#song) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
|
|
let item_name = {
|
|
let text = self.item_name.clone().get_text();
|
|
let item_name = LitStr::new(&text, Span::call_site());
|
|
quote! { #item_name }
|
|
};
|
|
|
|
let damage = match self.damage {
|
|
Some(d) => {
|
|
let damage_lit = LitInt::new(&d.to_string(), Span::call_site());
|
|
quote! { Some(#damage_lit) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
|
|
let max_damage = match self.max_damage {
|
|
Some(md) => {
|
|
let max_damage_lit = LitInt::new(&md.to_string(), Span::call_site());
|
|
quote! { Some(#max_damage_lit) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
|
|
let attribute_modifiers = match &self.attribute_modifiers {
|
|
Some(modifiers) => {
|
|
let modifier_code = modifiers.iter().map(|modifier| {
|
|
let r#type = LitStr::new(&modifier.r#type, Span::call_site());
|
|
let id = LitStr::new(&modifier.id, Span::call_site());
|
|
let amount = modifier.amount;
|
|
let operation =
|
|
Ident::new(&format!("{:?}", modifier.operation), Span::call_site());
|
|
let slot = LitStr::new(&modifier.slot, Span::call_site());
|
|
|
|
quote! {
|
|
Modifier {
|
|
r#type: #r#type,
|
|
id: #id,
|
|
amount: #amount,
|
|
operation: Operation::#operation,
|
|
slot: #slot,
|
|
}
|
|
}
|
|
});
|
|
quote! { Some(&[#(#modifier_code),*]) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
|
|
let tool = match &self.tool {
|
|
Some(tool) => {
|
|
let rules_code = tool.rules.iter().map(|rule| {
|
|
let mut block_array = Vec::new();
|
|
|
|
// TODO: According to the wiki, this can be a string or a list.
|
|
// I dont think there'll be any issues with always using a list, but we can
|
|
// probably save bandwidth by doing single strings.
|
|
for reg in rule.blocks.get_values() {
|
|
let tag_string = reg.serialize();
|
|
// The client knows what tags are; just send them the tag instead of all the
|
|
// blocks that are a part of the tag.
|
|
block_array.extend(quote! { #tag_string });
|
|
}
|
|
|
|
let speed = match rule.speed {
|
|
Some(speed) => {
|
|
quote! { Some(#speed) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
let correct_for_drops = match rule.correct_for_drops {
|
|
Some(correct_for_drops) => {
|
|
let correct_for_drops =
|
|
LitBool::new(correct_for_drops, Span::call_site());
|
|
quote! { Some(#correct_for_drops) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
quote! {
|
|
ToolRule {
|
|
blocks: &[#(#block_array),*],
|
|
speed: #speed,
|
|
correct_for_drops: #correct_for_drops
|
|
}
|
|
}
|
|
});
|
|
let damage_per_block = match tool.damage_per_block {
|
|
Some(speed) => {
|
|
let speed = LitInt::new(&speed.to_string(), Span::call_site());
|
|
quote! { Some(#speed) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
let default_mining_speed = match tool.default_mining_speed {
|
|
Some(speed) => {
|
|
let speed = LitFloat::new(&speed.to_string(), Span::call_site());
|
|
quote! { Some(#speed) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
quote! { Some(ToolComponent { rules: &[#(#rules_code),*], damage_per_block: #damage_per_block, default_mining_speed: #default_mining_speed }) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
|
|
let food = match &self.food {
|
|
Some(food) => {
|
|
let nutrition = LitInt::new(&food.nutrition.to_string(), Span::call_site());
|
|
let saturation =
|
|
LitFloat::new(&format!("{:.1}", food.saturation), Span::call_site());
|
|
let can_always_eat = match food.can_always_eat {
|
|
Some(can) => {
|
|
let can = LitBool::new(can, Span::call_site());
|
|
quote! { Some(#can) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
quote! { Some(FoodComponent {
|
|
nutrition: #nutrition,
|
|
saturation: #saturation,
|
|
can_always_eat: #can_always_eat,
|
|
} ) }
|
|
}
|
|
None => quote! { None },
|
|
};
|
|
|
|
tokens.extend(quote! {
|
|
ItemComponents {
|
|
item_name: #item_name,
|
|
max_stack_size: #max_stack_size,
|
|
jukebox_playable: #jukebox_playable,
|
|
damage: #damage,
|
|
max_damage: #max_damage,
|
|
attribute_modifiers: #attribute_modifiers,
|
|
tool: #tool,
|
|
food: #food
|
|
}
|
|
});
|
|
}
|
|
}
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
pub struct ToolComponent {
|
|
rules: Vec<ToolRule>,
|
|
default_mining_speed: Option<f32>,
|
|
damage_per_block: Option<u32>,
|
|
}
|
|
|
|
#[derive(Deserialize, Copy, Clone, Debug)]
|
|
pub struct FoodComponent {
|
|
nutrition: u8,
|
|
saturation: f32,
|
|
can_always_eat: Option<bool>,
|
|
}
|
|
|
|
#[derive(Deserialize, Clone, Debug)]
|
|
pub struct ToolRule {
|
|
blocks: RegistryEntryList,
|
|
speed: Option<f32>,
|
|
correct_for_drops: Option<bool>,
|
|
}
|
|
|
|
#[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,
|
|
registry_key: #name,
|
|
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;
|
|
use crate::tag::{Tagable, RegistryKey};
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct Item {
|
|
pub id: u16,
|
|
pub registry_key: &'static str,
|
|
pub components: ItemComponents,
|
|
}
|
|
|
|
impl PartialEq for Item {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
self.id == other.id
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct ItemComponents {
|
|
pub item_name: &'static str,
|
|
pub max_stack_size: u8,
|
|
pub jukebox_playable: Option<&'static str>,
|
|
pub damage: Option<u16>,
|
|
pub max_damage: Option<u16>,
|
|
pub attribute_modifiers: Option<&'static [Modifier]>,
|
|
pub tool: Option<ToolComponent>,
|
|
pub food: Option<FoodComponent>
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct Modifier {
|
|
pub r#type: &'static str,
|
|
pub id: &'static str,
|
|
pub amount: f64,
|
|
pub operation: Operation,
|
|
// TODO: Make this an enum
|
|
pub slot: &'static str,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub enum Operation {
|
|
AddValue,
|
|
AddMultipliedBase,
|
|
AddMultipliedTotal,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub struct ToolComponent {
|
|
pub rules: &'static [ToolRule],
|
|
pub default_mining_speed: Option<f32>,
|
|
pub damage_per_block: Option<u32>,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub struct ToolRule {
|
|
pub blocks: &'static [&'static str],
|
|
pub speed: Option<f32>,
|
|
pub correct_for_drops: Option<bool>,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub struct FoodComponent {
|
|
pub nutrition: u8,
|
|
pub saturation: f32,
|
|
pub can_always_eat: Option<bool>,
|
|
}
|
|
|
|
impl Item {
|
|
#constants
|
|
|
|
pub fn translated_name(&self) -> TextComponent {
|
|
TextComponent::text(self.components.item_name)
|
|
}
|
|
|
|
#[doc = "Try to parse an item from a resource location string."]
|
|
pub fn from_registry_key(name: &str) -> Option<&'static Self> {
|
|
match name {
|
|
#type_from_name
|
|
_ => None
|
|
}
|
|
}
|
|
|
|
#[doc = "Try to parse an item from a raw id."]
|
|
pub const fn from_id(id: u16) -> Option<&'static Self> {
|
|
match id {
|
|
#type_from_raw_id_arms
|
|
_ => None
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Tagable for Item {
|
|
#[inline]
|
|
fn tag_key() -> RegistryKey {
|
|
RegistryKey::Item
|
|
}
|
|
|
|
#[inline]
|
|
fn registry_key(&self) -> &str {
|
|
self.registry_key
|
|
}
|
|
}
|
|
}
|
|
}
|