Skip to content

Commit

Permalink
Add Effects
Browse files Browse the repository at this point in the history
  • Loading branch information
Snowiiii committed Feb 22, 2025
1 parent 0fb1b76 commit 995edfb
Show file tree
Hide file tree
Showing 59 changed files with 796 additions and 396 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ rayon = "1.10"
parking_lot = { version = "0.12", features = ["send_guard"] }
crossbeam = "0.8"

uuid = { version = "1.13", features = ["serde", "v3", "v4"] }
uuid = { version = "1.14", features = ["serde", "v3", "v4"] }
derive_more = { version = "2.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ and customizable experience. It prioritizes performance and player enjoyment whi
- [ ] Advancements
- Entities
- [x] Non-Living (Minecart, Eggs...)
- [x] Entity Effects
- [x] Players
- [x] Mobs
- [x] Animals
Expand Down
41 changes: 41 additions & 0 deletions assets/status_effects.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
[
"speed",
"slowness",
"haste",
"mining_fatigue",
"strength",
"instant_health",
"instant_damage",
"jump_boost",
"nausea",
"regeneration",
"resistance",
"fire_resistance",
"water_breathing",
"invisibility",
"blindness",
"night_vision",
"hunger",
"weakness",
"poison",
"wither",
"health_boost",
"absorption",
"saturation",
"glowing",
"levitation",
"luck",
"unluck",
"slow_falling",
"conduit_power",
"dolphins_grace",
"bad_omen",
"hero_of_the_village",
"darkness",
"trial_omen",
"raid_omen",
"wind_charged",
"weaving",
"oozing",
"infested"
]
2 changes: 2 additions & 0 deletions pumpkin-data/build/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod screen;
mod sound;
mod sound_category;
mod spawn_egg;
mod status_effect;
mod world_event;

pub fn main() {
Expand All @@ -42,6 +43,7 @@ pub fn main() {
write_generated_file(spawn_egg::build(), "spawn_egg.rs");
write_generated_file(item::build(), "item.rs");
write_generated_file(fluid::build(), "fluid.rs");
write_generated_file(status_effect::build(), "status_effect.rs");
}

pub fn array_to_tokenstream(array: &[String]) -> TokenStream {
Expand Down
51 changes: 51 additions & 0 deletions pumpkin-data/build/status_effect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use heck::ToPascalCase;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};

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

let chunk_status: Vec<String> =
serde_json::from_str(include_str!("../../assets/status_effects.json"))
.expect("Failed to parse status_effects.json");
let mut variants = TokenStream::new();
let mut type_from_name = TokenStream::new();
let mut type_to_name = TokenStream::new();

for status in chunk_status.iter() {
let const_ident = format_ident!("{}", status.to_pascal_case());
let resource_name = status.to_lowercase();

variants.extend([quote! {
#const_ident,
}]);
type_from_name.extend(quote! {
#resource_name => Some(Self::#const_ident),
});
type_to_name.extend(quote! {
Self::#const_ident => #resource_name,
});
}
quote! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EffectType {
#variants
}

impl EffectType {
#[doc = r" Try to parse a Effect Type from a resource location string"]
pub fn from_name(name: &str) -> Option<Self> {
match name {
#type_from_name
_ => None
}
}

pub const fn to_name(&self) -> &'static str {
match self {
#type_to_name
}
}
}
}
}
1 change: 1 addition & 0 deletions pumpkin-data/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ pub mod game_event {
}

pub mod entity {
include!(concat!(env!("OUT_DIR"), "/status_effect.rs"));
include!(concat!(env!("OUT_DIR"), "/spawn_egg.rs"));
include!(concat!(env!("OUT_DIR"), "/entity_type.rs"));
include!(concat!(env!("OUT_DIR"), "/entity_pose.rs"));
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 @@ -71,6 +71,7 @@ mod unload_chunk;
mod update_entity_pos;
mod update_entity_pos_rot;
mod update_entity_rot;
mod update_mob_effect;
mod update_objectives;
mod update_score;
mod worldevent;
Expand Down Expand Up @@ -148,6 +149,7 @@ pub use unload_chunk::*;
pub use update_entity_pos::*;
pub use update_entity_pos_rot::*;
pub use update_entity_rot::*;
pub use update_mob_effect::*;
pub use update_objectives::*;
pub use update_score::*;
pub use worldevent::*;
33 changes: 33 additions & 0 deletions pumpkin-protocol/src/client/play/update_mob_effect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use pumpkin_data::packet::clientbound::PLAY_UPDATE_MOB_EFFECT;
use pumpkin_macros::client_packet;
use serde::Serialize;

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

#[derive(Serialize)]
#[client_packet(PLAY_UPDATE_MOB_EFFECT)]
pub struct CUpdateMobEffect {
entity_id: VarInt,
effect_id: VarInt,
amplifier: VarInt,
duration: VarInt,
flags: i8,
}

impl CUpdateMobEffect {
pub fn new(
entity_id: VarInt,
effect_id: VarInt,
amplifier: VarInt,
duration: VarInt,
flags: i8,
) -> Self {
Self {
entity_id,
effect_id,
amplifier,
duration,
flags,
}
}
}
2 changes: 1 addition & 1 deletion pumpkin-world/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ num-traits = "0.2"
# Compression
flate2 = "1.0"
lz4 = "1.28"
zstd = "0.13.2"
zstd = "0.13.3"


file-guard = "0.2"
Expand Down
6 changes: 3 additions & 3 deletions pumpkin/src/command/args/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::{collections::HashMap, hash::Hash, sync::Arc};
use async_trait::async_trait;
use bounded_num::{NotInBounds, Number};
use pumpkin_data::damage::DamageType;
use pumpkin_data::entity::EffectType;
use pumpkin_data::particle::Particle;
use pumpkin_data::sound::SoundCategory;
use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
Expand All @@ -27,17 +28,15 @@ pub mod bossbar_style;
pub mod bounded_num;
pub mod command;
mod coordinate;
pub mod damage_type;
pub mod entities;
pub mod entity;
pub mod gamemode;
pub mod item;
pub mod message;
pub mod particle;
pub mod players;
pub mod position_2d;
pub mod position_3d;
pub mod position_block;
pub mod resource;
pub mod resource_location;
pub mod rotation;
pub mod simple;
Expand Down Expand Up @@ -105,6 +104,7 @@ pub enum Arg<'a> {
Simple(&'a str),
SoundCategory(SoundCategory),
DamageType(DamageType),
Effect(EffectType),
}

/// see [`crate::commands::tree::builder::argument`] and [`CommandTree::execute`]/[`crate::commands::tree::builder::NonLeafNodeBuilder::execute`]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, Suggestion

use crate::command::{
CommandSender,
args::{Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser},
args::{
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
GetClientSideArgParser,
},
dispatcher::CommandError,
tree::RawArgs,
};
Expand Down Expand Up @@ -59,7 +62,7 @@ impl DefaultNameArgConsumer for DamageTypeArgumentConsumer {
impl<'a> FindArg<'a> for DamageTypeArgumentConsumer {
type Data = &'a DamageType;

fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
match args.get(name) {
Some(Arg::DamageType(data)) => Ok(data),
_ => Err(CommandError::InvalidConsumption(Some(name.to_string()))),
Expand Down
71 changes: 71 additions & 0 deletions pumpkin/src/command/args/resource/effect.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use async_trait::async_trait;
use pumpkin_data::entity::EffectType;
use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};

use crate::command::{
CommandSender,
args::{
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
GetClientSideArgParser,
},
dispatcher::CommandError,
tree::RawArgs,
};
use crate::server::Server;

pub struct EffectTypeArgumentConsumer;

impl GetClientSideArgParser for EffectTypeArgumentConsumer {
fn get_client_side_parser(&self) -> ArgumentType {
ArgumentType::Resource {
identifier: "mob_effect",
}
}

fn get_client_side_suggestion_type_override(&self) -> Option<SuggestionProviders> {
None
}
}

#[async_trait]
impl ArgumentConsumer for EffectTypeArgumentConsumer {
async fn consume<'a>(
&'a self,
_sender: &CommandSender<'a>,
_server: &'a Server,
args: &mut RawArgs<'a>,
) -> Option<Arg<'a>> {
let name = args.pop()?;

// Create a static damage type first
let damage_type = EffectType::from_name(&name.replace("minecraft:", ""))?;
// Find matching static damage type from values array
Some(Arg::Effect(damage_type))
}

async fn suggest<'a>(
&'a self,
_sender: &CommandSender<'a>,
_server: &'a Server,
_input: &'a str,
) -> Result<Option<Vec<CommandSuggestion>>, CommandError> {
Ok(None)
}
}

impl DefaultNameArgConsumer for EffectTypeArgumentConsumer {
fn default_name(&self) -> &'static str {
"mob_effect"
}
}

impl<'a> FindArg<'a> for EffectTypeArgumentConsumer {
type Data = &'a EffectType;

fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
match args.get(name) {
Some(Arg::Effect(data)) => Ok(data),
_ => Err(CommandError::InvalidConsumption(Some(name.to_string()))),
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ use async_trait::async_trait;
use pumpkin_data::item::Item;
use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};

use crate::{command::dispatcher::CommandError, server::Server};

use super::{
super::{
CommandSender,
args::{ArgumentConsumer, RawArgs},
use crate::command::{
CommandSender,
args::{
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
GetClientSideArgParser,
},
Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser,
dispatcher::CommandError,
tree::RawArgs,
};
use crate::server::Server;

pub struct ItemArgumentConsumer;

Expand Down Expand Up @@ -55,7 +56,7 @@ impl DefaultNameArgConsumer for ItemArgumentConsumer {
impl<'a> FindArg<'a> for ItemArgumentConsumer {
type Data = (&'a str, Item);

fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
match args.get(name) {
Some(Arg::Item(name)) => Item::from_name(&name.replace("minecraft:", "")).map_or_else(
|| {
Expand Down
4 changes: 4 additions & 0 deletions pumpkin/src/command/args/resource/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod damage_type;
pub mod effect;
pub mod item;
pub mod particle;
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, Suggestion

use crate::command::{
CommandSender,
args::{Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser},
args::{
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
GetClientSideArgParser,
},
dispatcher::CommandError,
tree::RawArgs,
};
Expand Down Expand Up @@ -59,7 +62,7 @@ impl DefaultNameArgConsumer for ParticleArgumentConsumer {
impl<'a> FindArg<'a> for ParticleArgumentConsumer {
type Data = &'a Particle;

fn find_arg(args: &'a super::ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
match args.get(name) {
Some(Arg::Particle(data)) => Ok(data),
_ => Err(CommandError::InvalidConsumption(Some(name.to_string()))),
Expand Down
Loading

0 comments on commit 995edfb

Please sign in to comment.