Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eating): initial version of food eating #602

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
255 changes: 253 additions & 2 deletions pumpkin-data/build/item.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use heck::ToShoutySnakeCase;
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, format_ident, quote};
use std::fmt;
use syn::{Ident, LitBool, LitFloat, LitInt, LitStr};

include!("../src/tag.rs");
Expand Down Expand Up @@ -28,6 +29,12 @@ pub struct ItemComponents {
pub attribute_modifiers: Option<AttributeModifiers>,
#[serde(rename = "minecraft:tool")]
pub tool: Option<ToolComponent>,
#[serde(rename = "minecraft:food")]
pub food: Option<Food>,
#[serde(rename = "minecraft:consumable")]
pub consumable: Option<Consumable>,
#[serde(rename = "minecraft:use_remainder")]
pub use_remainder: Option<Remainder>,
}

impl ToTokens for ItemComponents {
Expand Down Expand Up @@ -90,6 +97,117 @@ impl ToTokens for ItemComponents {
None => quote! { None },
};

let food = match &self.food {
Some(food) => {
let nutrition = food.nutrition;
let saturation = food.saturation;
let can_always_eat = food
.can_always_eat
.is_some_and(|can_always_eat| can_always_eat);
quote! { Some(Food { nutrition: #nutrition, saturation: #saturation, can_always_eat: #can_always_eat }) }
}
None => quote! { None },
};

let use_remainder = match &self.use_remainder {
Some(remainder) => {
let id = LitStr::new(&remainder.id, Span::call_site());
let count = remainder.count.map_or(1, |count| count);
quote! { Some(Remainder { id: #id, count: #count }) }
}
None => quote! { None },
};

let consumable = match &self.consumable {
Some(consumable) => {
let consume_seconds = consumable
.consume_seconds
.map_or(1.6, |consume_seconds| consume_seconds);
let sound = consumable
.sound
.as_ref()
.map_or("entity.generic.eat", |s| s)
.to_string();
let animation = consumable
.animation
.as_ref()
.map_or("eat", |s| s)
.to_string();
let has_consume_particles = consumable
.has_consume_particles
.is_some_and(|has_consume_particles| has_consume_particles);

let on_consume_effects = match &consumable.on_consume_effects {
Some(on_consume_effects) => {
let on_consume_effects = on_consume_effects.iter().map(|consume_effect| {
let r#type = LitStr::new(&consume_effect.r#type, Span::call_site());
let effects = match &consume_effect.effects {
Some(effects) => match &effects {
Effects::Single(s) => {
let s = LitStr::new(s, Span::call_site());
quote! { Some(&Effects::Single(#s)) }
}
Effects::List(effects) => {
let effects = effects.iter().map(|effect| {
let id = LitStr::new(&effect.id, Span::call_site());
let amplifier =
effect.amplifier.map_or(0, |amplifier| amplifier);
let duration =
effect.duration.map_or(1, |duration| duration);
let ambient =
effect.ambient.is_some_and(|ambient| ambient);
let show_particles = effect
.show_particles
.is_none_or(|show_particles| show_particles);
let show_icon =
effect.show_icon.is_none_or(|show_icon| show_icon);
quote! {
Effect {
id: #id,
amplifier: #amplifier,
duration: #duration,
ambient: #ambient,
show_particles: #show_particles,
show_icon: #show_icon,
}
}
});
quote! { Some(&Effects::List(&[#(#effects),*])) }
}
},
None => quote! { None },
};
let probability = consume_effect
.probability
.map_or(1f32, |probability| probability);
let diameter =
consume_effect.diameter.map_or(16f32, |diameter| diameter);
quote! {
ConsumeEffect {
r#type: #r#type,
effects: #effects,
probability: #probability,
diameter: #diameter,
}
}
});
quote! { Some(&[#(#on_consume_effects),*]) }
}
None => quote! { None },
};
quote! {
Some(Consumable {
consume_seconds: #consume_seconds,
sound: #sound,
animation: #animation,
has_consume_particles: #has_consume_particles,
on_consume_effects: #on_consume_effects,
})
}
}
None => quote! { None },
};

let tool = match &self.tool {
Some(tool) => {
let rules_code = tool.rules.iter().map(|rule| {
Expand Down Expand Up @@ -154,7 +272,10 @@ impl ToTokens for ItemComponents {
damage: #damage,
max_damage: #max_damage,
attribute_modifiers: #attribute_modifiers,
tool: #tool
tool: #tool,
food: #food,
consumable: #consumable,
use_remainder: #use_remainder,
}
});
}
Expand Down Expand Up @@ -193,6 +314,53 @@ pub struct Modifier {
pub slot: String,
}

#[derive(Deserialize, Clone, Debug)]
pub struct Food {
pub nutrition: u32,
pub saturation: f32,
pub can_always_eat: Option<bool>,
}

#[derive(Deserialize, Clone, Debug)]
pub struct Remainder {
pub id: String,
pub count: Option<u8>,
}

#[derive(Deserialize, Clone, Debug)]
pub struct Consumable {
pub consume_seconds: Option<f32>,
pub animation: Option<String>,
pub sound: Option<String>,
pub has_consume_particles: Option<bool>,
pub on_consume_effects: Option<Vec<ConsumeEffect>>,
}

#[derive(Deserialize, Clone, Debug)]
pub struct ConsumeEffect {
pub r#type: String,
// effects can either be Vec<Effect> or a Single String
pub effects: Option<Effects>,
pub probability: Option<f32>,
pub diameter: Option<f32>,
}

#[derive(Clone, Debug)]
pub enum Effects {
List(Vec<Effect>),
Single(String),
}

#[derive(Deserialize, Clone, Debug)]
pub struct Effect {
pub id: String,
pub amplifier: Option<i8>,
pub duration: Option<i32>,
pub ambient: Option<bool>,
pub show_particles: Option<bool>,
pub show_icon: Option<bool>,
}

#[derive(Deserialize, Clone, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
#[allow(clippy::enum_variant_names)]
Expand All @@ -202,6 +370,40 @@ pub enum Operation {
AddMultipliedTotal,
}

impl<'de> Deserialize<'de> for Effects {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct EffectsVisitor;

impl<'de> Visitor<'de> for EffectsVisitor {
type Value = Effects;

fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a list of Effect objects or a single String")
}

fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Effects::Single(value.to_string()))
}

fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: de::SeqAccess<'de>,
{
let vec = Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
Ok(Effects::List(vec))
}
}

deserializer.deserialize_any(EffectsVisitor)
}
}

pub(crate) fn build() -> TokenStream {
println!("cargo:rerun-if-changed=../assets/items.json");

Expand Down Expand Up @@ -261,7 +463,56 @@ pub(crate) fn build() -> TokenStream {
pub damage: Option<u16>,
pub max_damage: Option<u16>,
pub attribute_modifiers: Option<AttributeModifiers>,
pub tool: Option<ToolComponent>
pub tool: Option<ToolComponent>,
pub food: Option<Food>,
pub consumable: Option<Consumable>,
pub use_remainder: Option<Remainder>,
}

#[derive(Clone, Copy, Debug)]
pub struct Food {
pub nutrition: u32,
pub saturation: f32,
pub can_always_eat: bool,
}

#[derive(Clone, Copy, Debug)]
pub struct Remainder {
pub id: &'static str,
pub count: u8,
}

#[derive(Clone, Copy, Debug)]
pub struct Consumable {
pub consume_seconds: f32,
pub sound: &'static str,
pub animation: &'static str,
pub has_consume_particles: bool,
pub on_consume_effects: Option<&'static [ConsumeEffect]>,
}

#[derive(Clone, Copy, Debug)]
pub struct ConsumeEffect {
pub r#type: &'static str,
pub effects: Option<&'static Effects>,
pub probability: f32,
pub diameter: f32,
}

#[derive(Clone, Debug)]
pub enum Effects {
List(&'static [Effect]),
Single(&'static str),
}

#[derive(Clone, Copy, Debug)]
pub struct Effect {
pub id: &'static str,
pub amplifier: i8,
pub duration: i32,
pub ambient: bool,
pub show_particles: bool,
pub show_icon: bool,
}

#[derive(Clone, Copy, Debug)]
Expand Down
4 changes: 4 additions & 0 deletions pumpkin-data/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ pub mod damage {
pub mod fluid {
include!(concat!(env!("OUT_DIR"), "/fluid.rs"));
}

pub fn parse_registry_name(name: &str) -> String {
name.replace("minecraft:", "")
}
13 changes: 13 additions & 0 deletions pumpkin-inventory/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,19 @@ impl PlayerInventory {
let (items, hotbar) = self.items.split_at_mut(27);
hotbar.iter_mut().chain(items)
}

pub fn add_item(&mut self, item: ItemStack) -> bool {
if let Some(slot) = self.collect_item_slot(item.item.id) {
let slot_item = self.items[slot];
if let Some(mut slot_item) = slot_item {
slot_item.item_count += item.item_count;
} else {
self.items[slot] = Some(item);
}
return true;
}
false
}
}

impl Container for PlayerInventory {
Expand Down
2 changes: 2 additions & 0 deletions pumpkin-protocol/src/client/play/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ mod player_info_update;
mod player_position;
mod player_remove;
mod remove_entities;
mod remove_mob_effect;
mod reset_score;
mod respawn;
mod server_links;
Expand Down Expand Up @@ -122,6 +123,7 @@ pub use player_info_update::*;
pub use player_position::*;
pub use player_remove::*;
pub use remove_entities::*;
pub use remove_mob_effect::*;
pub use reset_score::*;
pub use respawn::*;
pub use server_links::*;
Expand Down
21 changes: 21 additions & 0 deletions pumpkin-protocol/src/client/play/remove_mob_effect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use pumpkin_data::packet::clientbound::PLAY_REMOVE_MOB_EFFECT;
use pumpkin_macros::packet;
use serde::Serialize;

use crate::codec::var_int::VarInt;

#[derive(Serialize)]
#[packet(PLAY_REMOVE_MOB_EFFECT)]
pub struct CRemoveMobEffect {
entity_id: VarInt,
effect_id: VarInt,
}

impl CRemoveMobEffect {
pub fn new(entity_id: VarInt, effect_id: VarInt) -> Self {
Self {
entity_id,
effect_id,
}
}
}
17 changes: 17 additions & 0 deletions pumpkin/src/entity/living.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,23 @@ impl LivingEntity {
// TODO broadcast metadata
}

pub async fn remove_effect(&self, effect: EffectType) {
let mut effects = self.active_effects.lock().await;
effects.remove(&effect);
// TODO broadcast metadata
}

pub async fn clear_effects(&self) {
let mut effects = self.active_effects.lock().await;
effects.clear();
// TODO broadcast metadata
}

pub async fn get_effects(&self) -> Vec<Effect> {
let effects = self.active_effects.lock().await;
effects.values().cloned().collect()
}

pub async fn has_effect(&self, effect: EffectType) -> bool {
let effects = self.active_effects.lock().await;
effects.contains_key(&effect)
Expand Down
Loading
Loading