diff --git a/Cargo.toml b/Cargo.toml index 65710be7e..cca8bc8e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/README.md b/README.md index 2a778a838..ac50f33a0 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/assets/status_effects.json b/assets/status_effects.json new file mode 100644 index 000000000..d8f4755cc --- /dev/null +++ b/assets/status_effects.json @@ -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" +] \ No newline at end of file diff --git a/pumpkin-data/build/build.rs b/pumpkin-data/build/build.rs index 922e9b281..5a4ef2bca 100644 --- a/pumpkin-data/build/build.rs +++ b/pumpkin-data/build/build.rs @@ -21,6 +21,7 @@ mod screen; mod sound; mod sound_category; mod spawn_egg; +mod status_effect; mod world_event; pub fn main() { @@ -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 { diff --git a/pumpkin-data/build/status_effect.rs b/pumpkin-data/build/status_effect.rs new file mode 100644 index 000000000..cfb825978 --- /dev/null +++ b/pumpkin-data/build/status_effect.rs @@ -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 = + 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 { + match name { + #type_from_name + _ => None + } + } + + pub const fn to_name(&self) -> &'static str { + match self { + #type_to_name + } + } + } + } +} diff --git a/pumpkin-data/src/lib.rs b/pumpkin-data/src/lib.rs index b829ac085..efec08899 100644 --- a/pumpkin-data/src/lib.rs +++ b/pumpkin-data/src/lib.rs @@ -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")); diff --git a/pumpkin-protocol/src/client/play/mod.rs b/pumpkin-protocol/src/client/play/mod.rs index efe5ec623..ca9ad50af 100644 --- a/pumpkin-protocol/src/client/play/mod.rs +++ b/pumpkin-protocol/src/client/play/mod.rs @@ -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; @@ -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::*; diff --git a/pumpkin-protocol/src/client/play/update_mob_effect.rs b/pumpkin-protocol/src/client/play/update_mob_effect.rs new file mode 100644 index 000000000..c447d52a1 --- /dev/null +++ b/pumpkin-protocol/src/client/play/update_mob_effect.rs @@ -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, + } + } +} diff --git a/pumpkin-world/Cargo.toml b/pumpkin-world/Cargo.toml index a3dd85303..d831f9691 100644 --- a/pumpkin-world/Cargo.toml +++ b/pumpkin-world/Cargo.toml @@ -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" diff --git a/pumpkin/src/command/args/mod.rs b/pumpkin/src/command/args/mod.rs index 9630b4c7a..8ac01d7ee 100644 --- a/pumpkin/src/command/args/mod.rs +++ b/pumpkin/src/command/args/mod.rs @@ -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}; @@ -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; @@ -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`] diff --git a/pumpkin/src/command/args/damage_type.rs b/pumpkin/src/command/args/resource/damage_type.rs similarity index 88% rename from pumpkin/src/command/args/damage_type.rs rename to pumpkin/src/command/args/resource/damage_type.rs index 30cd141e3..d5dab260b 100644 --- a/pumpkin/src/command/args/damage_type.rs +++ b/pumpkin/src/command/args/resource/damage_type.rs @@ -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, }; @@ -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 { + fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::DamageType(data)) => Ok(data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/args/resource/effect.rs b/pumpkin/src/command/args/resource/effect.rs new file mode 100644 index 000000000..d071b5258 --- /dev/null +++ b/pumpkin/src/command/args/resource/effect.rs @@ -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 { + None + } +} + +#[async_trait] +impl ArgumentConsumer for EffectTypeArgumentConsumer { + async fn consume<'a>( + &'a self, + _sender: &CommandSender<'a>, + _server: &'a Server, + args: &mut RawArgs<'a>, + ) -> Option> { + 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>, 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 { + match args.get(name) { + Some(Arg::Effect(data)) => Ok(data), + _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), + } + } +} diff --git a/pumpkin/src/command/args/item.rs b/pumpkin/src/command/args/resource/item.rs similarity index 83% rename from pumpkin/src/command/args/item.rs rename to pumpkin/src/command/args/resource/item.rs index ca6040bbe..ea7fcf7d2 100644 --- a/pumpkin/src/command/args/item.rs +++ b/pumpkin/src/command/args/resource/item.rs @@ -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; @@ -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 { + fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Item(name)) => Item::from_name(&name.replace("minecraft:", "")).map_or_else( || { diff --git a/pumpkin/src/command/args/resource/mod.rs b/pumpkin/src/command/args/resource/mod.rs new file mode 100644 index 000000000..4dbe64dd9 --- /dev/null +++ b/pumpkin/src/command/args/resource/mod.rs @@ -0,0 +1,4 @@ +pub mod damage_type; +pub mod effect; +pub mod item; +pub mod particle; diff --git a/pumpkin/src/command/args/particle.rs b/pumpkin/src/command/args/resource/particle.rs similarity index 88% rename from pumpkin/src/command/args/particle.rs rename to pumpkin/src/command/args/resource/particle.rs index f8c956272..6a2ac3eca 100644 --- a/pumpkin/src/command/args/particle.rs +++ b/pumpkin/src/command/args/resource/particle.rs @@ -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, }; @@ -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 { + fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result { match args.get(name) { Some(Arg::Particle(data)) => Ok(data), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), diff --git a/pumpkin/src/command/commands/ban.rs b/pumpkin/src/command/commands/ban.rs index 4103710f2..a07166343 100644 --- a/pumpkin/src/command/commands/ban.rs +++ b/pumpkin/src/command/commands/ban.rs @@ -20,10 +20,10 @@ const DESCRIPTION: &str = "bans a player"; const ARG_TARGET: &str = "player"; const ARG_REASON: &str = "reason"; -struct BanNoReasonExecutor; +struct NoReasonExecutor; #[async_trait] -impl CommandExecutor for BanNoReasonExecutor { +impl CommandExecutor for NoReasonExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -39,10 +39,10 @@ impl CommandExecutor for BanNoReasonExecutor { } } -struct BanReasonExecutor; +struct ReasonExecutor; #[async_trait] -impl CommandExecutor for BanReasonExecutor { +impl CommandExecutor for ReasonExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -107,7 +107,7 @@ async fn ban_player(sender: &CommandSender<'_>, player: &Player, reason: Option< pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).then( argument(ARG_TARGET, PlayersArgumentConsumer) - .execute(BanNoReasonExecutor) - .then(argument(ARG_REASON, MsgArgConsumer).execute(BanReasonExecutor)), + .execute(NoReasonExecutor) + .then(argument(ARG_REASON, MsgArgConsumer).execute(ReasonExecutor)), ) } diff --git a/pumpkin/src/command/commands/banip.rs b/pumpkin/src/command/commands/banip.rs index 1ba6c79fe..7f3636446 100644 --- a/pumpkin/src/command/commands/banip.rs +++ b/pumpkin/src/command/commands/banip.rs @@ -36,10 +36,10 @@ async fn parse_ip(target: &str, server: &Server) -> Option { }) } -struct BanIpNoReasonExecutor; +struct NoReasonExecutor; #[async_trait] -impl CommandExecutor for BanIpNoReasonExecutor { +impl CommandExecutor for NoReasonExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -55,10 +55,10 @@ impl CommandExecutor for BanIpNoReasonExecutor { } } -struct BanIpReasonExecutor; +struct ReasonExecutor; #[async_trait] -impl CommandExecutor for BanIpReasonExecutor { +impl CommandExecutor for ReasonExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -148,7 +148,7 @@ async fn ban_ip(sender: &CommandSender<'_>, server: &Server, target: &str, reaso pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).then( argument(ARG_TARGET, SimpleArgConsumer) - .execute(BanIpNoReasonExecutor) - .then(argument(ARG_REASON, MsgArgConsumer).execute(BanIpReasonExecutor)), + .execute(NoReasonExecutor) + .then(argument(ARG_REASON, MsgArgConsumer).execute(ReasonExecutor)), ) } diff --git a/pumpkin/src/command/commands/banlist.rs b/pumpkin/src/command/commands/banlist.rs index f1cb7ebdf..03d94e5d1 100644 --- a/pumpkin/src/command/commands/banlist.rs +++ b/pumpkin/src/command/commands/banlist.rs @@ -16,10 +16,10 @@ const DESCRIPTION: &str = "shows the banlist"; const ARG_LIST_TYPE: &str = "ips|players"; -struct BanListExecutor; +struct ListExecutor; #[async_trait] -impl CommandExecutor for BanListExecutor { +impl CommandExecutor for ListExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -74,10 +74,10 @@ impl CommandExecutor for BanListExecutor { } } -struct BanListAllExecutor; +struct ListAllExecutor; #[async_trait] -impl CommandExecutor for BanListAllExecutor { +impl CommandExecutor for ListAllExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -138,6 +138,6 @@ async fn handle_banlist(list: Vec<(String, String, String)>, sender: &CommandSen pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .execute(BanListAllExecutor) - .then(argument(ARG_LIST_TYPE, SimpleArgConsumer).execute(BanListExecutor)) + .execute(ListAllExecutor) + .then(argument(ARG_LIST_TYPE, SimpleArgConsumer).execute(ListExecutor)) } diff --git a/pumpkin/src/command/commands/bossbar.rs b/pumpkin/src/command/commands/bossbar.rs index 62a8fc17e..6c6ea96c5 100644 --- a/pumpkin/src/command/commands/bossbar.rs +++ b/pumpkin/src/command/commands/bossbar.rs @@ -52,10 +52,10 @@ enum CommandValueSet { Visible, } -struct BossbarAddExecuter; +struct AddExecuter; #[async_trait] -impl CommandExecutor for BossbarAddExecuter { +impl CommandExecutor for AddExecuter { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -101,10 +101,10 @@ impl CommandExecutor for BossbarAddExecuter { } } -struct BossbarGetExecuter(CommandValueGet); +struct GetExecuter(CommandValueGet); #[async_trait] -impl CommandExecutor for BossbarGetExecuter { +impl CommandExecutor for GetExecuter { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -179,10 +179,10 @@ impl CommandExecutor for BossbarGetExecuter { } } -struct BossbarListExecuter; +struct ListExecuter; #[async_trait] -impl CommandExecutor for BossbarListExecuter { +impl CommandExecutor for ListExecuter { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -238,10 +238,10 @@ impl CommandExecutor for BossbarListExecuter { } } -struct BossbarRemoveExecuter; +struct RemoveExecuter; #[async_trait] -impl CommandExecutor for BossbarRemoveExecuter { +impl CommandExecutor for RemoveExecuter { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -289,10 +289,10 @@ impl CommandExecutor for BossbarRemoveExecuter { } } -struct BossbarSetExecuter(CommandValueSet); +struct SetExecuter(CommandValueSet); #[async_trait] -impl CommandExecutor for BossbarSetExecuter { +impl CommandExecutor for SetExecuter { #[expect(clippy::too_many_lines)] async fn execute<'a>( &self, @@ -591,25 +591,24 @@ fn value_consumer() -> BoundedNumArgumentConsumer { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) .then( - literal("add") - .then(argument_default_name(non_autocomplete_consumer()).then( - argument(ARG_NAME, TextComponentArgConsumer).execute(BossbarAddExecuter), - )), + literal("add").then( + argument_default_name(non_autocomplete_consumer()) + .then(argument(ARG_NAME, TextComponentArgConsumer).execute(AddExecuter)), + ), ) .then( literal("get").then( argument_default_name(autocomplete_consumer()) - .then(literal("max").execute(BossbarGetExecuter(CommandValueGet::Max))) - .then(literal("players").execute(BossbarGetExecuter(CommandValueGet::Players))) - .then(literal("value").execute(BossbarGetExecuter(CommandValueGet::Value))) - .then(literal("visible").execute(BossbarGetExecuter(CommandValueGet::Visible))), + .then(literal("max").execute(GetExecuter(CommandValueGet::Max))) + .then(literal("players").execute(GetExecuter(CommandValueGet::Players))) + .then(literal("value").execute(GetExecuter(CommandValueGet::Value))) + .then(literal("visible").execute(GetExecuter(CommandValueGet::Visible))), ), ) - .then(literal("list").execute(BossbarListExecuter)) + .then(literal("list").execute(ListExecuter)) .then( - literal("remove").then( - argument_default_name(autocomplete_consumer()).execute(BossbarRemoveExecuter), - ), + literal("remove") + .then(argument_default_name(autocomplete_consumer()).execute(RemoveExecuter)), ) .then( literal("set").then( @@ -617,45 +616,45 @@ pub fn init_command_tree() -> CommandTree { .then( literal("color").then( argument_default_name(BossbarColorArgumentConsumer) - .execute(BossbarSetExecuter(CommandValueSet::Color)), + .execute(SetExecuter(CommandValueSet::Color)), ), ) .then( literal("max").then( argument_default_name(max_value_consumer()) - .execute(BossbarSetExecuter(CommandValueSet::Max)), + .execute(SetExecuter(CommandValueSet::Max)), ), ) .then( literal("name").then( argument(ARG_NAME, TextComponentArgConsumer) - .execute(BossbarSetExecuter(CommandValueSet::Name)), + .execute(SetExecuter(CommandValueSet::Name)), ), ) .then( literal("players") .then( argument_default_name(PlayersArgumentConsumer) - .execute(BossbarSetExecuter(CommandValueSet::Players(true))), + .execute(SetExecuter(CommandValueSet::Players(true))), ) - .execute(BossbarSetExecuter(CommandValueSet::Players(false))), + .execute(SetExecuter(CommandValueSet::Players(false))), ) .then( literal("style").then( argument_default_name(BossbarStyleArgumentConsumer) - .execute(BossbarSetExecuter(CommandValueSet::Style)), + .execute(SetExecuter(CommandValueSet::Style)), ), ) .then( literal("value").then( argument_default_name(value_consumer()) - .execute(BossbarSetExecuter(CommandValueSet::Value)), + .execute(SetExecuter(CommandValueSet::Value)), ), ) .then( literal("visible").then( argument(ARG_VISIBLE, BoolArgConsumer) - .execute(BossbarSetExecuter(CommandValueSet::Visible)), + .execute(SetExecuter(CommandValueSet::Visible)), ), ), ), diff --git a/pumpkin/src/command/commands/clear.rs b/pumpkin/src/command/commands/clear.rs index 86f9441bd..ecfbafaba 100644 --- a/pumpkin/src/command/commands/clear.rs +++ b/pumpkin/src/command/commands/clear.rs @@ -73,10 +73,10 @@ fn clear_command_text_output(item_count: usize, targets: &[Arc]) -> Text } } -struct ClearExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for ClearExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -100,10 +100,10 @@ impl CommandExecutor for ClearExecutor { } } -struct ClearSelfExecutor; +struct SelfExecutor; #[async_trait] -impl CommandExecutor for ClearSelfExecutor { +impl CommandExecutor for SelfExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -126,6 +126,6 @@ impl CommandExecutor for ClearSelfExecutor { #[allow(clippy::redundant_closure_for_method_calls)] // causes lifetime issues pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .then(argument(ARG_TARGET, EntitiesArgumentConsumer).execute(ClearExecutor)) - .then(require(|sender| sender.is_player()).execute(ClearSelfExecutor)) + .then(argument(ARG_TARGET, EntitiesArgumentConsumer).execute(Executor)) + .then(require(|sender| sender.is_player()).execute(SelfExecutor)) } diff --git a/pumpkin/src/command/commands/damage.rs b/pumpkin/src/command/commands/damage.rs index 53406fdbb..1621b57b6 100644 --- a/pumpkin/src/command/commands/damage.rs +++ b/pumpkin/src/command/commands/damage.rs @@ -9,11 +9,13 @@ use crate::command::{ CommandError, CommandExecutor, CommandSender, args::{ Arg, ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer, - damage_type::DamageTypeArgumentConsumer, entity::EntityArgumentConsumer, - position_3d::Position3DArgumentConsumer, + entity::EntityArgumentConsumer, position_3d::Position3DArgumentConsumer, + resource::damage_type::DamageTypeArgumentConsumer, + }, + tree::{ + CommandTree, + builder::{argument, literal}, }, - tree::CommandTree, - tree::builder::{argument, literal}, }; const NAMES: [&str; 1] = ["damage"]; @@ -29,8 +31,8 @@ fn amount_consumer() -> BoundedNumArgumentConsumer { BoundedNumArgumentConsumer::new().name(ARG_AMOUNT).min(0.0) } -struct DamageLocationExecutor; -struct DamageEntityExecutor(bool); +struct LocationExecutor; +struct EntityExecutor(bool); async fn send_damage_result( sender: &mut CommandSender<'_>, @@ -60,7 +62,7 @@ async fn send_damage_result( } #[async_trait] -impl CommandExecutor for DamageLocationExecutor { +impl CommandExecutor for LocationExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -100,7 +102,7 @@ impl CommandExecutor for DamageLocationExecutor { } #[async_trait] -impl CommandExecutor for DamageEntityExecutor { +impl CommandExecutor for EntityExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -155,28 +157,28 @@ pub fn init_command_tree() -> CommandTree { argument(ARG_TARGET, EntityArgumentConsumer).then( argument(ARG_AMOUNT, amount_consumer()) // Basic damage - .execute(DamageEntityExecutor(false)) + .execute(EntityExecutor(false)) // With damage type .then( argument(ARG_DAMAGE_TYPE, DamageTypeArgumentConsumer) - .execute(DamageEntityExecutor(false)) + .execute(EntityExecutor(false)) // At location .then( literal("at").then( argument(ARG_LOCATION, Position3DArgumentConsumer) - .execute(DamageLocationExecutor), + .execute(LocationExecutor), ), ) // By entity .then( literal("by").then( argument(ARG_ENTITY, EntityArgumentConsumer) - .execute(DamageEntityExecutor(false)) + .execute(EntityExecutor(false)) // From cause .then( literal("from").then( argument(ARG_CAUSE, EntityArgumentConsumer) - .execute(DamageEntityExecutor(true)), + .execute(EntityExecutor(true)), ), ), ), diff --git a/pumpkin/src/command/commands/deop.rs b/pumpkin/src/command/commands/deop.rs index 97960de3f..67684f9f8 100644 --- a/pumpkin/src/command/commands/deop.rs +++ b/pumpkin/src/command/commands/deop.rs @@ -15,10 +15,10 @@ const NAMES: [&str; 1] = ["deop"]; const DESCRIPTION: &str = "Revokes operator status from a player."; const ARG_TARGETS: &str = "targets"; -struct DeopExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for DeopExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -61,5 +61,5 @@ impl CommandExecutor for DeopExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .then(argument(ARG_TARGETS, PlayersArgumentConsumer).execute(DeopExecutor)) + .then(argument(ARG_TARGETS, PlayersArgumentConsumer).execute(Executor)) } diff --git a/pumpkin/src/command/commands/effect.rs b/pumpkin/src/command/commands/effect.rs new file mode 100644 index 000000000..8bc48209a --- /dev/null +++ b/pumpkin/src/command/commands/effect.rs @@ -0,0 +1,100 @@ +use async_trait::async_trait; + +use crate::command::args::resource::effect::EffectTypeArgumentConsumer; + +use crate::TextComponent; + +use crate::command::args::players::PlayersArgumentConsumer; + +use crate::command::args::{Arg, ConsumedArgs}; +use crate::command::dispatcher::CommandError; +use crate::command::dispatcher::CommandError::InvalidConsumption; +use crate::command::tree::CommandTree; +use crate::command::tree::builder::{argument, literal}; +use crate::command::{CommandExecutor, CommandSender}; +use crate::server::Server; + +const NAMES: [&str; 1] = ["effect"]; + +const DESCRIPTION: &str = "Adds or removes the status effects of players and other entities."; + +// const ARG_CLEAR: &str = "clear"; +const ARG_GIVE: &str = "give"; + +const ARG_TARGET: &str = "target"; +const ARG_EFFECT: &str = "effect"; + +struct GiveExecutor; + +#[async_trait] +impl CommandExecutor for GiveExecutor { + async fn execute<'a>( + &self, + sender: &mut CommandSender<'a>, + _server: &Server, + args: &ConsumedArgs<'a>, + ) -> Result<(), CommandError> { + let Some(Arg::Players(targets)) = args.get(ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); + }; + let Some(Arg::Effect(effect)) = args.get(ARG_EFFECT) else { + return Err(InvalidConsumption(Some(ARG_EFFECT.into()))); + }; + + let target_count = targets.len(); + + for target in targets { + target + .add_effect( + crate::entity::effect::Effect { + r#type: *effect, + duration: 30, + amplifier: 1, + ambient: true, + show_particles: true, + show_icon: true, + }, + true, + ) + .await; + } + + let translation_name = + TextComponent::translate(format!("effect.minecraft.{}", effect.to_name()), []); + if target_count == 1 { + // TODO: use entity name + sender + .send_message(TextComponent::translate( + "commands.effect.give.success.single", + [ + translation_name, + TextComponent::text(targets[0].gameprofile.name.clone()), + ], + )) + .await; + } else { + sender + .send_message(TextComponent::translate( + "commands.effect.give.success.multiple", + [ + translation_name, + TextComponent::text(target_count.to_string()), + ], + )) + .await; + } + + Ok(()) + } +} + +#[allow(clippy::redundant_closure_for_method_calls)] +pub fn init_command_tree() -> CommandTree { + CommandTree::new(NAMES, DESCRIPTION).then( + literal(ARG_GIVE).then( + argument(ARG_TARGET, PlayersArgumentConsumer) + .then(argument(ARG_EFFECT, EffectTypeArgumentConsumer).execute(GiveExecutor)), + ), + ) + // TODO: Add more things +} diff --git a/pumpkin/src/command/commands/experience.rs b/pumpkin/src/command/commands/experience.rs index 59f5f96f1..fe322596f 100644 --- a/pumpkin/src/command/commands/experience.rs +++ b/pumpkin/src/command/commands/experience.rs @@ -38,12 +38,12 @@ enum ExpType { Levels, } -struct ExperienceExecutor { +struct Executor { mode: Mode, exp_type: Option, } -impl ExperienceExecutor { +impl Executor { async fn handle_query( &self, sender: &mut CommandSender<'_>, @@ -202,7 +202,7 @@ impl ExperienceExecutor { } #[async_trait] -impl CommandExecutor for ExperienceExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -281,15 +281,15 @@ pub fn init_command_tree() -> CommandTree { literal("add").then( argument(ARG_TARGETS, PlayersArgumentConsumer).then( argument(ARG_AMOUNT, xp_amount()) - .then(literal("levels").execute(ExperienceExecutor { + .then(literal("levels").execute(Executor { mode: Mode::Add, exp_type: Some(ExpType::Levels), })) - .then(literal("points").execute(ExperienceExecutor { + .then(literal("points").execute(Executor { mode: Mode::Add, exp_type: Some(ExpType::Points), })) - .execute(ExperienceExecutor { + .execute(Executor { mode: Mode::Add, exp_type: Some(ExpType::Points), }), @@ -300,15 +300,15 @@ pub fn init_command_tree() -> CommandTree { literal("set").then( argument(ARG_TARGETS, PlayersArgumentConsumer).then( argument(ARG_AMOUNT, xp_amount()) - .then(literal("levels").execute(ExperienceExecutor { + .then(literal("levels").execute(Executor { mode: Mode::Set, exp_type: Some(ExpType::Levels), })) - .then(literal("points").execute(ExperienceExecutor { + .then(literal("points").execute(Executor { mode: Mode::Set, exp_type: Some(ExpType::Points), })) - .execute(ExperienceExecutor { + .execute(Executor { mode: Mode::Set, exp_type: Some(ExpType::Points), }), @@ -318,11 +318,11 @@ pub fn init_command_tree() -> CommandTree { .then( literal("query").then( argument(ARG_TARGETS, PlayersArgumentConsumer) - .then(literal("levels").execute(ExperienceExecutor { + .then(literal("levels").execute(Executor { mode: Mode::Query, exp_type: Some(ExpType::Levels), })) - .then(literal("points").execute(ExperienceExecutor { + .then(literal("points").execute(Executor { mode: Mode::Query, exp_type: Some(ExpType::Points), })), diff --git a/pumpkin/src/command/commands/fill.rs b/pumpkin/src/command/commands/fill.rs index db423d3d1..f562f25eb 100644 --- a/pumpkin/src/command/commands/fill.rs +++ b/pumpkin/src/command/commands/fill.rs @@ -33,11 +33,11 @@ enum Mode { Replace, } -struct SetblockExecutor(Mode); +struct Executor(Mode); #[expect(clippy::too_many_lines)] #[async_trait] -impl CommandExecutor for SetblockExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -164,12 +164,12 @@ pub fn init_command_tree() -> CommandTree { argument(ARG_FROM, BlockPosArgumentConsumer).then( argument(ARG_TO, BlockPosArgumentConsumer).then( argument(ARG_BLOCK, BlockArgumentConsumer) - .then(literal("destroy").execute(SetblockExecutor(Mode::Destroy))) - .then(literal("hollow").execute(SetblockExecutor(Mode::Hollow))) - .then(literal("keep").execute(SetblockExecutor(Mode::Keep))) - .then(literal("outline").execute(SetblockExecutor(Mode::Outline))) - .then(literal("replace").execute(SetblockExecutor(Mode::Replace))) - .execute(SetblockExecutor(Mode::Replace)), + .then(literal("destroy").execute(Executor(Mode::Destroy))) + .then(literal("hollow").execute(Executor(Mode::Hollow))) + .then(literal("keep").execute(Executor(Mode::Keep))) + .then(literal("outline").execute(Executor(Mode::Outline))) + .then(literal("replace").execute(Executor(Mode::Replace))) + .execute(Executor(Mode::Replace)), ), ), ) diff --git a/pumpkin/src/command/commands/gamemode.rs b/pumpkin/src/command/commands/gamemode.rs index 1ff623095..984c8a3fb 100644 --- a/pumpkin/src/command/commands/gamemode.rs +++ b/pumpkin/src/command/commands/gamemode.rs @@ -23,10 +23,10 @@ const DESCRIPTION: &str = "Change a player's gamemode."; const ARG_GAMEMODE: &str = "gamemode"; const ARG_TARGET: &str = "target"; -struct GamemodeTargetSelf; +struct TargetSelfExecutor; #[async_trait] -impl CommandExecutor for GamemodeTargetSelf { +impl CommandExecutor for TargetSelfExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -56,10 +56,10 @@ impl CommandExecutor for GamemodeTargetSelf { } } -struct GamemodeTargetPlayer; +struct TargetPlayerExecutor; #[async_trait] -impl CommandExecutor for GamemodeTargetPlayer { +impl CommandExecutor for TargetPlayerExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -108,7 +108,7 @@ impl CommandExecutor for GamemodeTargetPlayer { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).then( argument(ARG_GAMEMODE, GamemodeArgumentConsumer) - .then(require(|sender| sender.is_player()).execute(GamemodeTargetSelf)) - .then(argument(ARG_TARGET, PlayersArgumentConsumer).execute(GamemodeTargetPlayer)), + .then(require(|sender| sender.is_player()).execute(TargetSelfExecutor)) + .then(argument(ARG_TARGET, PlayersArgumentConsumer).execute(TargetPlayerExecutor)), ) } diff --git a/pumpkin/src/command/commands/give.rs b/pumpkin/src/command/commands/give.rs index c09cd917d..8ac2052eb 100644 --- a/pumpkin/src/command/commands/give.rs +++ b/pumpkin/src/command/commands/give.rs @@ -5,8 +5,8 @@ use pumpkin_util::text::color::{Color, NamedColor}; use pumpkin_util::text::hover::HoverEvent; use crate::command::args::bounded_num::BoundedNumArgumentConsumer; -use crate::command::args::item::ItemArgumentConsumer; use crate::command::args::players::PlayersArgumentConsumer; +use crate::command::args::resource::item::ItemArgumentConsumer; use crate::command::args::{ConsumedArgs, FindArg, FindArgDefaultName}; use crate::command::tree::CommandTree; use crate::command::tree::builder::{argument, argument_default_name}; @@ -25,10 +25,10 @@ fn item_count_consumer() -> BoundedNumArgumentConsumer { .max(i32::MAX) } -struct GiveExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for GiveExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -112,8 +112,8 @@ pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).then( argument_default_name(PlayersArgumentConsumer).then( argument(ARG_ITEM, ItemArgumentConsumer) - .execute(GiveExecutor) - .then(argument_default_name(item_count_consumer()).execute(GiveExecutor)), + .execute(Executor) + .then(argument_default_name(item_count_consumer()).execute(Executor)), ), ) } diff --git a/pumpkin/src/command/commands/help.rs b/pumpkin/src/command/commands/help.rs index cb0fbc4d9..5e65a6801 100644 --- a/pumpkin/src/command/commands/help.rs +++ b/pumpkin/src/command/commands/help.rs @@ -25,10 +25,10 @@ fn page_number_consumer() -> BoundedNumArgumentConsumer { BoundedNumArgumentConsumer::new().name("page").min(1) } -struct CommandHelpExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for CommandHelpExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -222,7 +222,7 @@ impl CommandExecutor for BaseHelpExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .then(argument(ARG_COMMAND, CommandTreeArgumentConsumer).execute(CommandHelpExecutor)) + .then(argument(ARG_COMMAND, CommandTreeArgumentConsumer).execute(Executor)) .then(argument_default_name(page_number_consumer()).execute(BaseHelpExecutor)) .execute(BaseHelpExecutor) } diff --git a/pumpkin/src/command/commands/kick.rs b/pumpkin/src/command/commands/kick.rs index ed3641e52..092694c06 100644 --- a/pumpkin/src/command/commands/kick.rs +++ b/pumpkin/src/command/commands/kick.rs @@ -18,10 +18,10 @@ const ARG_TARGETS: &str = "targets"; const ARG_REASON: &str = "reason"; -struct KickExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for KickExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -52,7 +52,7 @@ impl CommandExecutor for KickExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).then( argument(ARG_TARGETS, PlayersArgumentConsumer) - .execute(KickExecutor) - .then(argument(ARG_REASON, MsgArgConsumer).execute(KickExecutor)), + .execute(Executor) + .then(argument(ARG_REASON, MsgArgConsumer).execute(Executor)), ) } diff --git a/pumpkin/src/command/commands/kill.rs b/pumpkin/src/command/commands/kill.rs index 4f372b9b1..45e3a8c99 100644 --- a/pumpkin/src/command/commands/kill.rs +++ b/pumpkin/src/command/commands/kill.rs @@ -16,10 +16,10 @@ const DESCRIPTION: &str = "Kills all target entities."; const ARG_TARGET: &str = "target"; -struct KillExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for KillExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -66,10 +66,10 @@ impl CommandExecutor for KillExecutor { } } -struct KillSelfExecutor; +struct SelfExecutor; #[async_trait] -impl CommandExecutor for KillSelfExecutor { +impl CommandExecutor for SelfExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -104,6 +104,6 @@ impl CommandExecutor for KillSelfExecutor { #[allow(clippy::redundant_closure_for_method_calls)] // causes lifetime issues pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .then(argument(ARG_TARGET, EntitiesArgumentConsumer).execute(KillExecutor)) - .then(require(|sender| sender.is_player()).execute(KillSelfExecutor)) + .then(argument(ARG_TARGET, EntitiesArgumentConsumer).execute(Executor)) + .then(require(|sender| sender.is_player()).execute(SelfExecutor)) } diff --git a/pumpkin/src/command/commands/list.rs b/pumpkin/src/command/commands/list.rs index cd94feea3..d10d3b9dd 100644 --- a/pumpkin/src/command/commands/list.rs +++ b/pumpkin/src/command/commands/list.rs @@ -15,10 +15,10 @@ const NAMES: [&str; 1] = ["list"]; const DESCRIPTION: &str = "Print the list of online players."; -struct ListExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for ListExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -54,5 +54,5 @@ fn get_player_names(players: Vec>) -> String { } pub fn init_command_tree() -> CommandTree { - CommandTree::new(NAMES, DESCRIPTION).execute(ListExecutor) + CommandTree::new(NAMES, DESCRIPTION).execute(Executor) } diff --git a/pumpkin/src/command/commands/me.rs b/pumpkin/src/command/commands/me.rs index 7b6f70026..984500790 100644 --- a/pumpkin/src/command/commands/me.rs +++ b/pumpkin/src/command/commands/me.rs @@ -16,10 +16,10 @@ const DESCRIPTION: &str = "Broadcasts a narrative message about yourself."; const ARG_MESSAGE: &str = "action"; -struct MeExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for MeExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -44,5 +44,5 @@ impl CommandExecutor for MeExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .then(argument(ARG_MESSAGE, MsgArgConsumer).execute(MeExecutor)) + .then(argument(ARG_MESSAGE, MsgArgConsumer).execute(Executor)) } diff --git a/pumpkin/src/command/commands/mod.rs b/pumpkin/src/command/commands/mod.rs index 0459f26ac..87afd04ff 100644 --- a/pumpkin/src/command/commands/mod.rs +++ b/pumpkin/src/command/commands/mod.rs @@ -1,36 +1,90 @@ -pub mod ban; -pub mod banip; -pub mod banlist; -pub mod bossbar; -pub mod clear; -pub mod damage; -pub mod deop; -pub mod experience; -pub mod fill; -pub mod gamemode; -pub mod give; -pub mod help; -pub mod kick; -pub mod kill; -pub mod list; -pub mod me; -pub mod msg; -pub mod op; -pub mod pardon; -pub mod pardonip; -pub mod particle; -pub mod playsound; -pub mod plugin; -pub mod plugins; -pub mod pumpkin; -pub mod say; -pub mod seed; -pub mod setblock; -pub mod stop; -pub mod summon; -pub mod teleport; -pub mod time; -pub mod title; -pub mod transfer; -pub mod weather; -pub mod worldborder; +use pumpkin_util::PermissionLvl; + +use super::dispatcher::CommandDispatcher; + +mod ban; +mod banip; +mod banlist; +mod bossbar; +mod clear; +mod damage; +mod deop; +mod effect; +mod experience; +mod fill; +mod gamemode; +mod give; +mod help; +mod kick; +mod kill; +mod list; +mod me; +mod msg; +mod op; +mod pardon; +mod pardonip; +mod particle; +mod playsound; +mod plugin; +mod plugins; +mod pumpkin; +mod say; +mod seed; +mod setblock; +mod stop; +mod summon; +mod teleport; +mod time; +mod title; +mod transfer; +mod weather; +mod worldborder; + +#[must_use] +pub fn default_dispatcher() -> CommandDispatcher { + let mut dispatcher = CommandDispatcher::default(); + + // Zero + dispatcher.register(pumpkin::init_command_tree(), PermissionLvl::Zero); + dispatcher.register(help::init_command_tree(), PermissionLvl::Zero); + dispatcher.register(list::init_command_tree(), PermissionLvl::Zero); + dispatcher.register(transfer::init_command_tree(), PermissionLvl::Zero); + dispatcher.register(me::init_command_tree(), PermissionLvl::Zero); + dispatcher.register(msg::init_command_tree(), PermissionLvl::Zero); + // Two + dispatcher.register(kill::init_command_tree(), PermissionLvl::Two); + dispatcher.register(worldborder::init_command_tree(), PermissionLvl::Two); + dispatcher.register(effect::init_command_tree(), PermissionLvl::Two); + dispatcher.register(teleport::init_command_tree(), PermissionLvl::Two); + dispatcher.register(time::init_command_tree(), PermissionLvl::Two); + dispatcher.register(give::init_command_tree(), PermissionLvl::Two); + dispatcher.register(clear::init_command_tree(), PermissionLvl::Two); + dispatcher.register(setblock::init_command_tree(), PermissionLvl::Two); + dispatcher.register(seed::init_command_tree(), PermissionLvl::Two); + dispatcher.register(fill::init_command_tree(), PermissionLvl::Two); + dispatcher.register(playsound::init_command_tree(), PermissionLvl::Two); + dispatcher.register(title::init_command_tree(), PermissionLvl::Two); + dispatcher.register(summon::init_command_tree(), PermissionLvl::Two); + dispatcher.register(experience::init_command_tree(), PermissionLvl::Two); + dispatcher.register(weather::init_command_tree(), PermissionLvl::Two); + dispatcher.register(particle::init_command_tree(), PermissionLvl::Two); + dispatcher.register(damage::init_command_tree(), PermissionLvl::Two); + dispatcher.register(bossbar::init_command_tree(), PermissionLvl::Two); + dispatcher.register(say::init_command_tree(), PermissionLvl::Two); + dispatcher.register(gamemode::init_command_tree(), PermissionLvl::Two); + // Three + dispatcher.register(op::init_command_tree(), PermissionLvl::Three); + dispatcher.register(deop::init_command_tree(), PermissionLvl::Three); + dispatcher.register(kick::init_command_tree(), PermissionLvl::Three); + dispatcher.register(plugin::init_command_tree(), PermissionLvl::Three); + dispatcher.register(plugins::init_command_tree(), PermissionLvl::Three); + dispatcher.register(ban::init_command_tree(), PermissionLvl::Three); + dispatcher.register(banip::init_command_tree(), PermissionLvl::Three); + dispatcher.register(banlist::init_command_tree(), PermissionLvl::Three); + dispatcher.register(pardon::init_command_tree(), PermissionLvl::Three); + dispatcher.register(pardonip::init_command_tree(), PermissionLvl::Three); + // Four + dispatcher.register(stop::init_command_tree(), PermissionLvl::Four); + + dispatcher +} diff --git a/pumpkin/src/command/commands/msg.rs b/pumpkin/src/command/commands/msg.rs index 028aae52b..4ab14d899 100644 --- a/pumpkin/src/command/commands/msg.rs +++ b/pumpkin/src/command/commands/msg.rs @@ -19,10 +19,10 @@ const DESCRIPTION: &str = "Sends a private message to one or more players."; const ARG_MESSAGE: &str = "message"; -struct MsgExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for MsgExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -81,6 +81,6 @@ impl CommandExecutor for MsgExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).then( argument_default_name(PlayersArgumentConsumer) - .then(argument(ARG_MESSAGE, MsgArgConsumer).execute(MsgExecutor)), + .then(argument(ARG_MESSAGE, MsgArgConsumer).execute(Executor)), ) } diff --git a/pumpkin/src/command/commands/op.rs b/pumpkin/src/command/commands/op.rs index 56f430acf..6b656d328 100644 --- a/pumpkin/src/command/commands/op.rs +++ b/pumpkin/src/command/commands/op.rs @@ -16,10 +16,10 @@ const NAMES: [&str; 1] = ["op"]; const DESCRIPTION: &str = "Grants operator status to a player."; const ARG_TARGETS: &str = "targets"; -struct OpExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for OpExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -81,5 +81,5 @@ impl CommandExecutor for OpExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .then(argument(ARG_TARGETS, PlayersArgumentConsumer).execute(OpExecutor)) + .then(argument(ARG_TARGETS, PlayersArgumentConsumer).execute(Executor)) } diff --git a/pumpkin/src/command/commands/pardon.rs b/pumpkin/src/command/commands/pardon.rs index 46d928dbd..2d0e7c352 100644 --- a/pumpkin/src/command/commands/pardon.rs +++ b/pumpkin/src/command/commands/pardon.rs @@ -16,10 +16,10 @@ const DESCRIPTION: &str = "unbans a player"; const ARG_TARGET: &str = "player"; -struct PardonExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for PardonExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -60,5 +60,5 @@ impl CommandExecutor for PardonExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .then(argument(ARG_TARGET, SimpleArgConsumer).execute(PardonExecutor)) + .then(argument(ARG_TARGET, SimpleArgConsumer).execute(Executor)) } diff --git a/pumpkin/src/command/commands/pardonip.rs b/pumpkin/src/command/commands/pardonip.rs index b467d0fa3..e2ff01f6f 100644 --- a/pumpkin/src/command/commands/pardonip.rs +++ b/pumpkin/src/command/commands/pardonip.rs @@ -18,10 +18,10 @@ const DESCRIPTION: &str = "unbans a ip"; const ARG_TARGET: &str = "ip"; -struct PardonIpExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for PardonIpExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -64,5 +64,5 @@ impl CommandExecutor for PardonIpExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .then(argument(ARG_TARGET, SimpleArgConsumer).execute(PardonIpExecutor)) + .then(argument(ARG_TARGET, SimpleArgConsumer).execute(Executor)) } diff --git a/pumpkin/src/command/commands/particle.rs b/pumpkin/src/command/commands/particle.rs index 7e50b95ca..0bd5e1903 100644 --- a/pumpkin/src/command/commands/particle.rs +++ b/pumpkin/src/command/commands/particle.rs @@ -5,7 +5,7 @@ use crate::command::{ CommandError, CommandExecutor, CommandSender, args::{ ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer, - particle::ParticleArgumentConsumer, position_3d::Position3DArgumentConsumer, + position_3d::Position3DArgumentConsumer, resource::particle::ParticleArgumentConsumer, }, tree::{CommandTree, builder::argument}, }; @@ -20,10 +20,10 @@ const ARG_DELTA: &str = "delta"; const ARG_SPEED: &str = "speed"; const ARG_COUNT: &str = "count"; -struct ParticleExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for ParticleExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -64,25 +64,25 @@ impl CommandExecutor for ParticleExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).then( argument(ARG_NAME, ParticleArgumentConsumer) - .execute(ParticleExecutor) + .execute(Executor) .then( argument(ARG_POS, Position3DArgumentConsumer) - .execute(ParticleExecutor) + .execute(Executor) .then( argument(ARG_DELTA, Position3DArgumentConsumer) - .execute(ParticleExecutor) + .execute(Executor) .then( argument( ARG_SPEED, BoundedNumArgumentConsumer::::new().min(0.0), ) - .execute(ParticleExecutor) + .execute(Executor) .then( argument( ARG_COUNT, BoundedNumArgumentConsumer::::new().min(0), ) - .execute(ParticleExecutor), + .execute(Executor), ), ), ), diff --git a/pumpkin/src/command/commands/playsound.rs b/pumpkin/src/command/commands/playsound.rs index f1321eeaa..73a659a1b 100644 --- a/pumpkin/src/command/commands/playsound.rs +++ b/pumpkin/src/command/commands/playsound.rs @@ -57,10 +57,10 @@ fn min_volume_consumer() -> BoundedNumArgumentConsumer { .max(1.0) } -struct SoundExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for SoundExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -183,18 +183,18 @@ pub fn init_command_tree() -> CommandTree { ARG_MIN_VOLUME, min_volume_consumer(), ) - .execute(SoundExecutor), + .execute(Executor), ) - .execute(SoundExecutor), + .execute(Executor), ) - .execute(SoundExecutor), + .execute(Executor), ) - .execute(SoundExecutor), + .execute(Executor), ) - .execute(SoundExecutor), + .execute(Executor), ) - .execute(SoundExecutor), + .execute(Executor), ) - .execute(SoundExecutor), + .execute(Executor), ) } diff --git a/pumpkin/src/command/commands/plugins.rs b/pumpkin/src/command/commands/plugins.rs index a94400717..6025f5d1b 100644 --- a/pumpkin/src/command/commands/plugins.rs +++ b/pumpkin/src/command/commands/plugins.rs @@ -12,10 +12,10 @@ const NAMES: [&str; 1] = ["plugins"]; const DESCRIPTION: &str = "List all available plugins."; -struct ListExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for ListExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -63,5 +63,5 @@ impl CommandExecutor for ListExecutor { } pub fn init_command_tree() -> CommandTree { - CommandTree::new(NAMES, DESCRIPTION).execute(ListExecutor) + CommandTree::new(NAMES, DESCRIPTION).execute(Executor) } diff --git a/pumpkin/src/command/commands/pumpkin.rs b/pumpkin/src/command/commands/pumpkin.rs index fae9793cf..a7606f5fb 100644 --- a/pumpkin/src/command/commands/pumpkin.rs +++ b/pumpkin/src/command/commands/pumpkin.rs @@ -17,13 +17,13 @@ const NAMES: [&str; 2] = ["pumpkin", "version"]; const DESCRIPTION: &str = "Display information about Pumpkin."; -struct PumpkinExecutor; +struct Executor; const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); const CARGO_PKG_DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION"); #[async_trait] -impl CommandExecutor for PumpkinExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -98,5 +98,5 @@ impl CommandExecutor for PumpkinExecutor { } pub fn init_command_tree() -> CommandTree { - CommandTree::new(NAMES, DESCRIPTION).execute(PumpkinExecutor) + CommandTree::new(NAMES, DESCRIPTION).execute(Executor) } diff --git a/pumpkin/src/command/commands/say.rs b/pumpkin/src/command/commands/say.rs index fcea13ea7..6fb4e7a5d 100644 --- a/pumpkin/src/command/commands/say.rs +++ b/pumpkin/src/command/commands/say.rs @@ -16,10 +16,10 @@ const DESCRIPTION: &str = "Broadcast a message to all Players."; const ARG_MESSAGE: &str = "message"; -struct SayExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for SayExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -44,5 +44,5 @@ impl CommandExecutor for SayExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .then(argument(ARG_MESSAGE, MsgArgConsumer).execute(SayExecutor)) + .then(argument(ARG_MESSAGE, MsgArgConsumer).execute(Executor)) } diff --git a/pumpkin/src/command/commands/seed.rs b/pumpkin/src/command/commands/seed.rs index 7d908839f..0f6a167f8 100644 --- a/pumpkin/src/command/commands/seed.rs +++ b/pumpkin/src/command/commands/seed.rs @@ -11,10 +11,10 @@ const NAMES: [&str; 1] = ["seed"]; const DESCRIPTION: &str = "Displays the world seed."; -struct PumpkinExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for PumpkinExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -54,5 +54,5 @@ impl CommandExecutor for PumpkinExecutor { } pub fn init_command_tree() -> CommandTree { - CommandTree::new(NAMES, DESCRIPTION).execute(PumpkinExecutor) + CommandTree::new(NAMES, DESCRIPTION).execute(Executor) } diff --git a/pumpkin/src/command/commands/setblock.rs b/pumpkin/src/command/commands/setblock.rs index dfaf0edf0..f1474d1cc 100644 --- a/pumpkin/src/command/commands/setblock.rs +++ b/pumpkin/src/command/commands/setblock.rs @@ -27,10 +27,10 @@ enum Mode { Replace, } -struct SetblockExecutor(Mode); +struct Executor(Mode); #[async_trait] -impl CommandExecutor for SetblockExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -90,10 +90,10 @@ pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).then( argument(ARG_BLOCK_POS, BlockPosArgumentConsumer).then( argument(ARG_BLOCK, BlockArgumentConsumer) - .then(literal("replace").execute(SetblockExecutor(Mode::Replace))) - .then(literal("destroy").execute(SetblockExecutor(Mode::Destroy))) - .then(literal("keep").execute(SetblockExecutor(Mode::Keep))) - .execute(SetblockExecutor(Mode::Replace)), + .then(literal("replace").execute(Executor(Mode::Replace))) + .then(literal("destroy").execute(Executor(Mode::Destroy))) + .then(literal("keep").execute(Executor(Mode::Keep))) + .execute(Executor(Mode::Replace)), ), ) } diff --git a/pumpkin/src/command/commands/stop.rs b/pumpkin/src/command/commands/stop.rs index a452f6430..1b8f6df83 100644 --- a/pumpkin/src/command/commands/stop.rs +++ b/pumpkin/src/command/commands/stop.rs @@ -11,10 +11,10 @@ const NAMES: [&str; 1] = ["stop"]; const DESCRIPTION: &str = "Stop the server."; -struct StopExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for StopExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -32,5 +32,5 @@ impl CommandExecutor for StopExecutor { } pub fn init_command_tree() -> CommandTree { - CommandTree::new(NAMES, DESCRIPTION).execute(StopExecutor) + CommandTree::new(NAMES, DESCRIPTION).execute(Executor) } diff --git a/pumpkin/src/command/commands/summon.rs b/pumpkin/src/command/commands/summon.rs index 4dab2133e..ebd99ca65 100644 --- a/pumpkin/src/command/commands/summon.rs +++ b/pumpkin/src/command/commands/summon.rs @@ -21,10 +21,10 @@ const ARG_ENTITY: &str = "entity"; const ARG_POS: &str = "pos"; -struct SummonExecutor; +struct Executor; #[async_trait] -impl CommandExecutor for SummonExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -54,8 +54,8 @@ impl CommandExecutor for SummonExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).then( argument(ARG_ENTITY, SummonableEntitiesArgumentConsumer) - .execute(SummonExecutor) - .then(argument(ARG_POS, Position3DArgumentConsumer).execute(SummonExecutor)), + .execute(Executor) + .then(argument(ARG_POS, Position3DArgumentConsumer).execute(Executor)), // TODO: Add NBT ) } diff --git a/pumpkin/src/command/commands/teleport.rs b/pumpkin/src/command/commands/teleport.rs index cdb14e377..7d7048420 100644 --- a/pumpkin/src/command/commands/teleport.rs +++ b/pumpkin/src/command/commands/teleport.rs @@ -49,10 +49,10 @@ fn yaw_pitch_facing_position( (yaw_degrees as f32, pitch_degrees as f32) } -struct TpEntitiesToEntityExecutor; +struct EntitiesToEntityExecutor; #[async_trait] -impl CommandExecutor for TpEntitiesToEntityExecutor { +impl CommandExecutor for EntitiesToEntityExecutor { async fn execute<'a>( &self, _sender: &mut CommandSender<'a>, @@ -74,10 +74,10 @@ impl CommandExecutor for TpEntitiesToEntityExecutor { } } -struct TpEntitiesToPosFacingPosExecutor; +struct EntitiesToPosFacingPosExecutor; #[async_trait] -impl CommandExecutor for TpEntitiesToPosFacingPosExecutor { +impl CommandExecutor for EntitiesToPosFacingPosExecutor { async fn execute<'a>( &self, _sender: &mut CommandSender<'a>, @@ -99,10 +99,10 @@ impl CommandExecutor for TpEntitiesToPosFacingPosExecutor { } } -struct TpEntitiesToPosFacingEntityExecutor; +struct EntitiesToPosFacingEntityExecutor; #[async_trait] -impl CommandExecutor for TpEntitiesToPosFacingEntityExecutor { +impl CommandExecutor for EntitiesToPosFacingEntityExecutor { async fn execute<'a>( &self, _sender: &mut CommandSender<'a>, @@ -126,10 +126,10 @@ impl CommandExecutor for TpEntitiesToPosFacingEntityExecutor { } } -struct TpEntitiesToPosWithRotationExecutor; +struct EntitiesToPosWithRotationExecutor; #[async_trait] -impl CommandExecutor for TpEntitiesToPosWithRotationExecutor { +impl CommandExecutor for EntitiesToPosWithRotationExecutor { async fn execute<'a>( &self, _sender: &mut CommandSender<'a>, @@ -150,10 +150,10 @@ impl CommandExecutor for TpEntitiesToPosWithRotationExecutor { } } -struct TpEntitiesToPosExecutor; +struct EntitiesToPosExecutor; #[async_trait] -impl CommandExecutor for TpEntitiesToPosExecutor { +impl CommandExecutor for EntitiesToPosExecutor { async fn execute<'a>( &self, _sender: &mut CommandSender<'a>, @@ -174,10 +174,10 @@ impl CommandExecutor for TpEntitiesToPosExecutor { } } -struct TpSelfToEntityExecutor; +struct SelfToEntityExecutor; #[async_trait] -impl CommandExecutor for TpSelfToEntityExecutor { +impl CommandExecutor for SelfToEntityExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -204,10 +204,10 @@ impl CommandExecutor for TpSelfToEntityExecutor { } } -struct TpSelfToPosExecutor; +struct SelfToPosExecutor; #[async_trait] -impl CommandExecutor for TpSelfToPosExecutor { +impl CommandExecutor for SelfToPosExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -234,34 +234,34 @@ impl CommandExecutor for TpSelfToPosExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) - .then(argument(ARG_LOCATION, Position3DArgumentConsumer).execute(TpSelfToPosExecutor)) - .then(argument(ARG_DESTINATION, EntityArgumentConsumer).execute(TpSelfToEntityExecutor)) + .then(argument(ARG_LOCATION, Position3DArgumentConsumer).execute(SelfToPosExecutor)) + .then(argument(ARG_DESTINATION, EntityArgumentConsumer).execute(SelfToEntityExecutor)) .then( argument(ARG_TARGETS, EntitiesArgumentConsumer) .then( argument(ARG_LOCATION, Position3DArgumentConsumer) - .execute(TpEntitiesToPosExecutor) + .execute(EntitiesToPosExecutor) .then( argument(ARG_ROTATION, RotationArgumentConsumer) - .execute(TpEntitiesToPosWithRotationExecutor), + .execute(EntitiesToPosWithRotationExecutor), ) .then( literal("facing") .then( literal("entity").then( argument(ARG_FACING_ENTITY, EntityArgumentConsumer) - .execute(TpEntitiesToPosFacingEntityExecutor), + .execute(EntitiesToPosFacingEntityExecutor), ), ) .then( argument(ARG_FACING_LOCATION, Position3DArgumentConsumer) - .execute(TpEntitiesToPosFacingPosExecutor), + .execute(EntitiesToPosFacingPosExecutor), ), ), ) .then( argument(ARG_DESTINATION, EntityArgumentConsumer) - .execute(TpEntitiesToEntityExecutor), + .execute(EntitiesToEntityExecutor), ), ) } diff --git a/pumpkin/src/command/commands/time.rs b/pumpkin/src/command/commands/time.rs index 0a3fc1b13..7f2c2b3a6 100644 --- a/pumpkin/src/command/commands/time.rs +++ b/pumpkin/src/command/commands/time.rs @@ -44,10 +44,10 @@ enum QueryMode { Day, } -struct TimeQueryExecutor(QueryMode); +struct QueryExecutor(QueryMode); #[async_trait] -impl CommandExecutor for TimeQueryExecutor { +impl CommandExecutor for QueryExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -91,10 +91,10 @@ impl CommandExecutor for TimeQueryExecutor { } } -struct TimeChangeExecutor(Mode); +struct ChangeExecutor(Mode); #[async_trait] -impl CommandExecutor for TimeChangeExecutor { +impl CommandExecutor for ChangeExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -153,33 +153,27 @@ impl CommandExecutor for TimeChangeExecutor { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION) .then( - literal("add").then( - argument(ARG_TIME, TimeArgumentConsumer).execute(TimeChangeExecutor(Mode::Add)), - ), + literal("add") + .then(argument(ARG_TIME, TimeArgumentConsumer).execute(ChangeExecutor(Mode::Add))), ) .then( literal("query") - .then(literal("daytime").execute(TimeQueryExecutor(QueryMode::DayTime))) - .then(literal("gametime").execute(TimeQueryExecutor(QueryMode::GameTime))) - .then(literal("day").execute(TimeQueryExecutor(QueryMode::Day))), + .then(literal("daytime").execute(QueryExecutor(QueryMode::DayTime))) + .then(literal("gametime").execute(QueryExecutor(QueryMode::GameTime))) + .then(literal("day").execute(QueryExecutor(QueryMode::Day))), ) .then( literal("set") - .then(literal("day").execute(TimeChangeExecutor(Mode::Set(Some(PresetTime::Day))))) - .then( - literal("noon").execute(TimeChangeExecutor(Mode::Set(Some(PresetTime::Noon)))), - ) - .then( - literal("night") - .execute(TimeChangeExecutor(Mode::Set(Some(PresetTime::Night)))), - ) + .then(literal("day").execute(ChangeExecutor(Mode::Set(Some(PresetTime::Day))))) + .then(literal("noon").execute(ChangeExecutor(Mode::Set(Some(PresetTime::Noon))))) + .then(literal("night").execute(ChangeExecutor(Mode::Set(Some(PresetTime::Night))))) .then( literal("midnight") - .execute(TimeChangeExecutor(Mode::Set(Some(PresetTime::Midnight)))), + .execute(ChangeExecutor(Mode::Set(Some(PresetTime::Midnight)))), ) .then( argument(ARG_TIME, TimeArgumentConsumer) - .execute(TimeChangeExecutor(Mode::Set(None))), + .execute(ChangeExecutor(Mode::Set(None))), ), ) } diff --git a/pumpkin/src/command/commands/transfer.rs b/pumpkin/src/command/commands/transfer.rs index 13848e8f5..c767203bb 100644 --- a/pumpkin/src/command/commands/transfer.rs +++ b/pumpkin/src/command/commands/transfer.rs @@ -29,10 +29,10 @@ fn port_consumer() -> BoundedNumArgumentConsumer { .max(65535) } -struct TransferTargetSelf; +struct TargetSelfExecutor; #[async_trait] -impl CommandExecutor for TransferTargetSelf { +impl CommandExecutor for TargetSelfExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -71,10 +71,10 @@ impl CommandExecutor for TransferTargetSelf { } } -struct TransferTargetPlayer; +struct TargetPlayerExecutor; #[async_trait] -impl CommandExecutor for TransferTargetPlayer { +impl CommandExecutor for TargetPlayerExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -121,13 +121,13 @@ impl CommandExecutor for TransferTargetPlayer { pub fn init_command_tree() -> CommandTree { CommandTree::new(NAMES, DESCRIPTION).then( argument(ARG_HOSTNAME, SimpleArgConsumer) - .then(require(|sender| sender.is_player()).execute(TransferTargetSelf)) + .then(require(|sender| sender.is_player()).execute(TargetSelfExecutor)) .then( argument_default_name(port_consumer()) - .then(require(|sender| sender.is_player()).execute(TransferTargetSelf)) + .then(require(|sender| sender.is_player()).execute(TargetSelfExecutor)) .then( argument(ARG_PLAYERS, PlayersArgumentConsumer) - .execute(TransferTargetPlayer), + .execute(TargetPlayerExecutor), ), ), ) diff --git a/pumpkin/src/command/commands/weather.rs b/pumpkin/src/command/commands/weather.rs index 8a5726ec1..841f0beaf 100644 --- a/pumpkin/src/command/commands/weather.rs +++ b/pumpkin/src/command/commands/weather.rs @@ -12,7 +12,7 @@ const NAMES: [&str; 1] = ["weather"]; const DESCRIPTION: &str = "Changes the weather."; const ARG_DURATION: &str = "duration"; -struct WeatherExecutor { +struct Executor { mode: WeatherMode, } @@ -23,7 +23,7 @@ enum WeatherMode { } #[async_trait] -impl CommandExecutor for WeatherExecutor { +impl CommandExecutor for Executor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -73,33 +73,33 @@ pub fn init_command_tree() -> CommandTree { .then( literal("clear") .then( - argument(ARG_DURATION, TimeArgumentConsumer).execute(WeatherExecutor { + argument(ARG_DURATION, TimeArgumentConsumer).execute(Executor { mode: WeatherMode::Clear, }), ) - .execute(WeatherExecutor { + .execute(Executor { mode: WeatherMode::Clear, }), ) .then( literal("rain") .then( - argument(ARG_DURATION, TimeArgumentConsumer).execute(WeatherExecutor { + argument(ARG_DURATION, TimeArgumentConsumer).execute(Executor { mode: WeatherMode::Rain, }), ) - .execute(WeatherExecutor { + .execute(Executor { mode: WeatherMode::Rain, }), ) .then( literal("thunder") .then( - argument(ARG_DURATION, TimeArgumentConsumer).execute(WeatherExecutor { + argument(ARG_DURATION, TimeArgumentConsumer).execute(Executor { mode: WeatherMode::Thunder, }), ) - .execute(WeatherExecutor { + .execute(Executor { mode: WeatherMode::Thunder, }), ) diff --git a/pumpkin/src/command/commands/worldborder.rs b/pumpkin/src/command/commands/worldborder.rs index 4f534b00e..13435fffd 100644 --- a/pumpkin/src/command/commands/worldborder.rs +++ b/pumpkin/src/command/commands/worldborder.rs @@ -48,10 +48,10 @@ fn warning_distance_consumer() -> BoundedNumArgumentConsumer { BoundedNumArgumentConsumer::new().min(0).name("distance") } -struct WorldborderGetExecutor; +struct GetExecutor; #[async_trait] -impl CommandExecutor for WorldborderGetExecutor { +impl CommandExecutor for GetExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -76,10 +76,10 @@ impl CommandExecutor for WorldborderGetExecutor { } } -struct WorldborderSetExecutor; +struct SetExecutor; #[async_trait] -impl CommandExecutor for WorldborderSetExecutor { +impl CommandExecutor for SetExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -128,10 +128,10 @@ impl CommandExecutor for WorldborderSetExecutor { } } -struct WorldborderSetTimeExecutor; +struct SetTimeExecutor; #[async_trait] -impl CommandExecutor for WorldborderSetTimeExecutor { +impl CommandExecutor for SetTimeExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -213,10 +213,10 @@ impl CommandExecutor for WorldborderSetTimeExecutor { } } -struct WorldborderAddExecutor; +struct AddExecutor; #[async_trait] -impl CommandExecutor for WorldborderAddExecutor { +impl CommandExecutor for AddExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -267,10 +267,10 @@ impl CommandExecutor for WorldborderAddExecutor { } } -struct WorldborderAddTimeExecutor; +struct AddTimeExecutor; #[async_trait] -impl CommandExecutor for WorldborderAddTimeExecutor { +impl CommandExecutor for AddTimeExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -354,10 +354,10 @@ impl CommandExecutor for WorldborderAddTimeExecutor { } } -struct WorldborderCenterExecutor; +struct CenterExecutor; #[async_trait] -impl CommandExecutor for WorldborderCenterExecutor { +impl CommandExecutor for CenterExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -387,10 +387,10 @@ impl CommandExecutor for WorldborderCenterExecutor { } } -struct WorldborderDamageAmountExecutor; +struct DamageAmountExecutor; #[async_trait] -impl CommandExecutor for WorldborderDamageAmountExecutor { +impl CommandExecutor for DamageAmountExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -439,10 +439,10 @@ impl CommandExecutor for WorldborderDamageAmountExecutor { } } -struct WorldborderDamageBufferExecutor; +struct DamageBufferExecutor; #[async_trait] -impl CommandExecutor for WorldborderDamageBufferExecutor { +impl CommandExecutor for DamageBufferExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -491,10 +491,10 @@ impl CommandExecutor for WorldborderDamageBufferExecutor { } } -struct WorldborderWarningDistanceExecutor; +struct WarningDistanceExecutor; #[async_trait] -impl CommandExecutor for WorldborderWarningDistanceExecutor { +impl CommandExecutor for WarningDistanceExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -542,10 +542,10 @@ impl CommandExecutor for WorldborderWarningDistanceExecutor { } } -struct WorldborderWarningTimeExecutor; +struct WarningTimeExecutor; #[async_trait] -impl CommandExecutor for WorldborderWarningTimeExecutor { +impl CommandExecutor for WarningTimeExecutor { async fn execute<'a>( &self, sender: &mut CommandSender<'a>, @@ -598,38 +598,32 @@ pub fn init_command_tree() -> CommandTree { .then( literal("add").then( argument_default_name(distance_consumer()) - .execute(WorldborderAddExecutor) - .then( - argument_default_name(time_consumer()).execute(WorldborderAddTimeExecutor), - ), + .execute(AddExecutor) + .then(argument_default_name(time_consumer()).execute(AddTimeExecutor)), ), ) - .then(literal("center").then( - argument_default_name(Position2DArgumentConsumer).execute(WorldborderCenterExecutor), - )) + .then( + literal("center") + .then(argument_default_name(Position2DArgumentConsumer).execute(CenterExecutor)), + ) .then( literal("damage") .then( literal("amount").then( argument_default_name(damage_per_block_consumer()) - .execute(WorldborderDamageAmountExecutor), + .execute(DamageAmountExecutor), ), ) - .then( - literal("buffer").then( - argument_default_name(damage_buffer_consumer()) - .execute(WorldborderDamageBufferExecutor), - ), - ), + .then(literal("buffer").then( + argument_default_name(damage_buffer_consumer()).execute(DamageBufferExecutor), + )), ) - .then(literal("get").execute(WorldborderGetExecutor)) + .then(literal("get").execute(GetExecutor)) .then( literal("set").then( argument_default_name(distance_consumer()) - .execute(WorldborderSetExecutor) - .then( - argument_default_name(time_consumer()).execute(WorldborderSetTimeExecutor), - ), + .execute(SetExecutor) + .then(argument_default_name(time_consumer()).execute(SetTimeExecutor)), ), ) .then( @@ -637,11 +631,12 @@ pub fn init_command_tree() -> CommandTree { .then( literal("distance").then( argument_default_name(warning_distance_consumer()) - .execute(WorldborderWarningDistanceExecutor), + .execute(WarningDistanceExecutor), ), ) - .then(literal("time").then( - argument_default_name(time_consumer()).execute(WorldborderWarningTimeExecutor), - )), + .then( + literal("time") + .then(argument_default_name(time_consumer()).execute(WarningTimeExecutor)), + ), ) } diff --git a/pumpkin/src/command/dispatcher.rs b/pumpkin/src/command/dispatcher.rs index 45795ee63..5359d2c5e 100644 --- a/pumpkin/src/command/dispatcher.rs +++ b/pumpkin/src/command/dispatcher.rs @@ -349,7 +349,7 @@ impl CommandDispatcher { #[cfg(test)] mod test { - use crate::command::{default_dispatcher, tree::CommandTree}; + use crate::command::{commands::default_dispatcher, tree::CommandTree}; use pumpkin_util::permission::PermissionLvl; #[test] fn test_dynamic_command() { diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index d39f722d5..d960d292f 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -1,19 +1,12 @@ use std::fmt; use std::sync::Arc; -use crate::command::commands::seed; -use crate::command::commands::{bossbar, transfer}; -use crate::command::dispatcher::CommandDispatcher; use crate::entity::player::Player; use crate::server::Server; use crate::world::World; use args::ConsumedArgs; use async_trait::async_trait; -use commands::{ - ban, banip, banlist, clear, damage, deop, experience, fill, gamemode, give, help, kick, kill, - list, me, msg, op, pardon, pardonip, particle, playsound, plugin, plugins, pumpkin, say, - setblock, stop, summon, teleport, time, title, weather, worldborder, -}; + use dispatcher::CommandError; use pumpkin_util::math::vector3::Vector3; use pumpkin_util::permission::PermissionLvl; @@ -21,7 +14,7 @@ use pumpkin_util::text::TextComponent; pub mod args; pub mod client_suggestions; -mod commands; +pub mod commands; pub mod dispatcher; pub mod tree; @@ -106,54 +99,6 @@ impl CommandSender<'_> { } } -#[must_use] -pub fn default_dispatcher() -> CommandDispatcher { - let mut dispatcher = CommandDispatcher::default(); - - // Zero - dispatcher.register(pumpkin::init_command_tree(), PermissionLvl::Zero); - dispatcher.register(help::init_command_tree(), PermissionLvl::Zero); - dispatcher.register(list::init_command_tree(), PermissionLvl::Zero); - dispatcher.register(transfer::init_command_tree(), PermissionLvl::Zero); - dispatcher.register(me::init_command_tree(), PermissionLvl::Zero); - dispatcher.register(msg::init_command_tree(), PermissionLvl::Zero); - // Two - dispatcher.register(kill::init_command_tree(), PermissionLvl::Two); - dispatcher.register(worldborder::init_command_tree(), PermissionLvl::Two); - dispatcher.register(teleport::init_command_tree(), PermissionLvl::Two); - dispatcher.register(time::init_command_tree(), PermissionLvl::Two); - dispatcher.register(give::init_command_tree(), PermissionLvl::Two); - dispatcher.register(clear::init_command_tree(), PermissionLvl::Two); - dispatcher.register(setblock::init_command_tree(), PermissionLvl::Two); - dispatcher.register(seed::init_command_tree(), PermissionLvl::Two); - dispatcher.register(fill::init_command_tree(), PermissionLvl::Two); - dispatcher.register(playsound::init_command_tree(), PermissionLvl::Two); - dispatcher.register(title::init_command_tree(), PermissionLvl::Two); - dispatcher.register(summon::init_command_tree(), PermissionLvl::Two); - dispatcher.register(experience::init_command_tree(), PermissionLvl::Two); - dispatcher.register(weather::init_command_tree(), PermissionLvl::Two); - dispatcher.register(particle::init_command_tree(), PermissionLvl::Two); - dispatcher.register(damage::init_command_tree(), PermissionLvl::Two); - dispatcher.register(bossbar::init_command_tree(), PermissionLvl::Two); - dispatcher.register(say::init_command_tree(), PermissionLvl::Two); - dispatcher.register(gamemode::init_command_tree(), PermissionLvl::Two); - // Three - dispatcher.register(op::init_command_tree(), PermissionLvl::Three); - dispatcher.register(deop::init_command_tree(), PermissionLvl::Three); - dispatcher.register(kick::init_command_tree(), PermissionLvl::Three); - dispatcher.register(plugin::init_command_tree(), PermissionLvl::Three); - dispatcher.register(plugins::init_command_tree(), PermissionLvl::Three); - dispatcher.register(ban::init_command_tree(), PermissionLvl::Three); - dispatcher.register(banip::init_command_tree(), PermissionLvl::Three); - dispatcher.register(banlist::init_command_tree(), PermissionLvl::Three); - dispatcher.register(pardon::init_command_tree(), PermissionLvl::Three); - dispatcher.register(pardonip::init_command_tree(), PermissionLvl::Three); - // Four - dispatcher.register(stop::init_command_tree(), PermissionLvl::Four); - - dispatcher -} - #[async_trait] pub trait CommandExecutor: Sync { async fn execute<'a>( diff --git a/pumpkin/src/entity/effect/mod.rs b/pumpkin/src/entity/effect/mod.rs new file mode 100644 index 000000000..e88df8258 --- /dev/null +++ b/pumpkin/src/entity/effect/mod.rs @@ -0,0 +1,11 @@ +use pumpkin_data::entity::EffectType; + +#[derive(PartialEq, Eq, Hash, Clone)] +pub struct Effect { + pub r#type: EffectType, + pub duration: i32, + pub amplifier: u8, + pub ambient: bool, + pub show_particles: bool, + pub show_icon: bool, +} diff --git a/pumpkin/src/entity/living.rs b/pumpkin/src/entity/living.rs index 6a64286be..1f19e2959 100644 --- a/pumpkin/src/entity/living.rs +++ b/pumpkin/src/entity/living.rs @@ -1,7 +1,8 @@ -use std::sync::atomic::AtomicI32; +use std::{collections::HashMap, sync::atomic::AtomicI32}; use async_trait::async_trait; use crossbeam::atomic::AtomicCell; +use pumpkin_data::entity::EffectType; use pumpkin_data::{damage::DamageType, sound::Sound}; use pumpkin_nbt::tag::NbtTag; use pumpkin_protocol::{ @@ -12,8 +13,9 @@ use pumpkin_protocol::{ }; use pumpkin_util::math::vector3::Vector3; use pumpkin_world::item::ItemStack; +use tokio::sync::Mutex; -use super::{Entity, EntityId, NBTStorage}; +use super::{Entity, EntityId, NBTStorage, effect::Effect}; /// Represents a living entity within the game world. /// @@ -31,9 +33,10 @@ pub struct LivingEntity { pub health: AtomicCell, /// The distance the entity has been falling pub fall_distance: AtomicCell, + pub active_effects: Mutex>, } impl LivingEntity { - pub const fn new(entity: Entity) -> Self { + pub fn new(entity: Entity) -> Self { Self { entity, last_pos: AtomicCell::new(Vector3::new(0.0, 0.0, 0.0)), @@ -41,6 +44,7 @@ impl LivingEntity { last_damage_taken: AtomicCell::new(0.0), health: AtomicCell::new(20.0), fall_distance: AtomicCell::new(0.0), + active_effects: Mutex::new(HashMap::new()), } } @@ -131,6 +135,22 @@ impl LivingEntity { true } + pub async fn add_effect(&self, effect: Effect) { + let mut effects = self.active_effects.lock().await; + effects.insert(effect.r#type, effect); + // TODO broadcast metadata + } + + pub async fn has_effect(&self, effect: EffectType) -> bool { + let effects = self.active_effects.lock().await; + effects.contains_key(&effect) + } + + pub async fn get_effect(&self, effect: EffectType) -> Option { + let effects = self.active_effects.lock().await; + effects.get(&effect).cloned() + } + pub async fn damage(&self, amount: f32, damage_type: DamageType) -> bool { self.damage_with_context(amount, damage_type, None, None, None) .await diff --git a/pumpkin/src/entity/mod.rs b/pumpkin/src/entity/mod.rs index ed86115a7..c45737af7 100644 --- a/pumpkin/src/entity/mod.rs +++ b/pumpkin/src/entity/mod.rs @@ -32,6 +32,7 @@ use tokio::sync::RwLock; use crate::world::World; pub mod ai; +pub mod effect; pub mod hunger; pub mod item; pub mod living; diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index f21dd3473..1914c8c25 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -13,7 +13,7 @@ use crossbeam::atomic::AtomicCell; use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_data::{ damage::DamageType, - entity::EntityType, + entity::{EffectType, EntityType}, item::Operation, particle::Particle, sound::{Sound, SoundCategory}, @@ -27,7 +27,8 @@ use pumpkin_protocol::{ CAcknowledgeBlockChange, CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus, CGameEvent, CHurtAnimation, CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities, CPlayerInfoUpdate, CPlayerPosition, CRespawn, CSetExperience, CSetHealth, CSubtitle, - CSystemChatMessage, CTitleText, CUnloadChunk, GameEvent, MetaDataType, PlayerAction, + CSystemChatMessage, CTitleText, CUnloadChunk, CUpdateMobEffect, GameEvent, MetaDataType, + PlayerAction, }, server::play::{ SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd, @@ -66,6 +67,7 @@ use tokio::sync::{Mutex, Notify, RwLock}; use super::{ Entity, EntityBase, EntityId, NBTStorage, combat::{self, AttackType, player_attack_sound}, + effect::Effect, hunger::HungerManager, item::ItemEntity, }; @@ -958,13 +960,52 @@ impl Player { .await .get_mining_speed(block_name) .await; - // TODO: Handle effects + // Haste + if self.living_entity.has_effect(EffectType::Haste).await + || self + .living_entity + .has_effect(EffectType::ConduitPower) + .await + { + speed *= 1.0 + (self.get_haste_amplifier().await + 1) as f32 * 0.2; + } + // Fatigue + if let Some(fatigue) = self + .living_entity + .get_effect(EffectType::MiningFatigue) + .await + { + let fatigue_speed = match fatigue.amplifier { + 0 => 0.3, + 1 => 0.09, + 2 => 0.0027, + _ => 8.1E-4, + }; + speed *= fatigue_speed; + } + // TODO: Handle when in Water if !self.living_entity.entity.on_ground.load(Ordering::Relaxed) { speed /= 5.0; } speed } + async fn get_haste_amplifier(&self) -> u32 { + let mut i = 0; + let mut j = 0; + if let Some(effect) = self.living_entity.get_effect(EffectType::Haste).await { + i = effect.amplifier; + } + if let Some(effect) = self + .living_entity + .get_effect(EffectType::ConduitPower) + .await + { + j = effect.amplifier; + } + u32::from(i.max(j)) + } + pub async fn send_message( &self, message: &TextComponent, @@ -1049,6 +1090,34 @@ impl Player { self.set_experience(new_level, progress, points).await; } + pub async fn add_effect(&self, effect: Effect, keep_fading: bool) { + let mut flag: i8 = 0; + + if effect.ambient { + flag |= 1; + } + if effect.show_particles { + flag |= 2; + } + if effect.show_icon { + flag |= 4; + } + if keep_fading { + flag |= 8; + } + let effect_id = VarInt(effect.r#type as i32); + self.client + .send_packet(&CUpdateMobEffect::new( + self.entity_id().into(), + effect_id, + effect.amplifier.into(), + effect.duration.into(), + flag, + )) + .await; + self.living_entity.add_effect(effect).await; + } + /// Add experience levels to the player pub async fn add_experience_levels(&self, added_levels: i32) { let current_level = self.experience_level.load(Ordering::Relaxed); diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index d529435a9..ef07c8b1d 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -31,15 +31,13 @@ use tokio::sync::{Mutex, RwLock}; use crate::block::default_block_properties_manager; use crate::block::properties::BlockPropertiesManager; use crate::block::registry::BlockRegistry; +use crate::command::commands::default_dispatcher; use crate::entity::{Entity, EntityId}; use crate::item::registry::ItemRegistry; use crate::net::EncryptionError; use crate::world::custom_bossbar::CustomBossbars; use crate::{ - command::{default_dispatcher, dispatcher::CommandDispatcher}, - entity::player::Player, - net::Client, - world::World, + command::dispatcher::CommandDispatcher, entity::player::Player, net::Client, world::World, }; mod connection_cache; diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 245a3bc4d..b0e58f612 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -101,8 +101,8 @@ impl PumpkinError for GetBlockError { /// **Key Responsibilities:** /// /// - Manages the `Level` instance for handling chunk-related operations. -/// - Stores and tracks active `Player` entities within the world. -/// - Provides a central hub for interacting with the world's entities and environment. +/// - Active players and entities. +/// - World-related systems like the scoreboard, world border, weather, and time. pub struct World { /// The underlying level, responsible for chunk management and terrain generation. pub level: Arc, @@ -121,7 +121,6 @@ pub struct World { pub dimension_type: DimensionType, /// The world's weather, including rain and thunder levels pub weather: Mutex, - // TODO: entities } impl World {