Add Effects

This commit is contained in:
Alexander Medvedev 2025-02-22 14:17:06 +01:00
parent 0fb1b76ea7
commit 995edfb3c2
59 changed files with 796 additions and 396 deletions

View File

@ -51,7 +51,7 @@ rayon = "1.10"
parking_lot = { version = "0.12", features = ["send_guard"] } parking_lot = { version = "0.12", features = ["send_guard"] }
crossbeam = "0.8" 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"] } derive_more = { version = "2.0", features = ["full"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"

View File

@ -72,6 +72,7 @@ and customizable experience. It prioritizes performance and player enjoyment whi
- [ ] Advancements - [ ] Advancements
- Entities - Entities
- [x] Non-Living (Minecart, Eggs...) - [x] Non-Living (Minecart, Eggs...)
- [x] Entity Effects
- [x] Players - [x] Players
- [x] Mobs - [x] Mobs
- [x] Animals - [x] Animals

View File

@ -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"
]

View File

@ -21,6 +21,7 @@ mod screen;
mod sound; mod sound;
mod sound_category; mod sound_category;
mod spawn_egg; mod spawn_egg;
mod status_effect;
mod world_event; mod world_event;
pub fn main() { pub fn main() {
@ -42,6 +43,7 @@ pub fn main() {
write_generated_file(spawn_egg::build(), "spawn_egg.rs"); write_generated_file(spawn_egg::build(), "spawn_egg.rs");
write_generated_file(item::build(), "item.rs"); write_generated_file(item::build(), "item.rs");
write_generated_file(fluid::build(), "fluid.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 { pub fn array_to_tokenstream(array: &[String]) -> TokenStream {

View File

@ -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
}
}
}
}
}

View File

@ -32,6 +32,7 @@ pub mod game_event {
} }
pub mod entity { pub mod entity {
include!(concat!(env!("OUT_DIR"), "/status_effect.rs"));
include!(concat!(env!("OUT_DIR"), "/spawn_egg.rs")); include!(concat!(env!("OUT_DIR"), "/spawn_egg.rs"));
include!(concat!(env!("OUT_DIR"), "/entity_type.rs")); include!(concat!(env!("OUT_DIR"), "/entity_type.rs"));
include!(concat!(env!("OUT_DIR"), "/entity_pose.rs")); include!(concat!(env!("OUT_DIR"), "/entity_pose.rs"));

View File

@ -71,6 +71,7 @@ mod unload_chunk;
mod update_entity_pos; mod update_entity_pos;
mod update_entity_pos_rot; mod update_entity_pos_rot;
mod update_entity_rot; mod update_entity_rot;
mod update_mob_effect;
mod update_objectives; mod update_objectives;
mod update_score; mod update_score;
mod worldevent; mod worldevent;
@ -148,6 +149,7 @@ pub use unload_chunk::*;
pub use update_entity_pos::*; pub use update_entity_pos::*;
pub use update_entity_pos_rot::*; pub use update_entity_pos_rot::*;
pub use update_entity_rot::*; pub use update_entity_rot::*;
pub use update_mob_effect::*;
pub use update_objectives::*; pub use update_objectives::*;
pub use update_score::*; pub use update_score::*;
pub use worldevent::*; pub use worldevent::*;

View File

@ -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,
}
}
}

View File

@ -30,7 +30,7 @@ num-traits = "0.2"
# Compression # Compression
flate2 = "1.0" flate2 = "1.0"
lz4 = "1.28" lz4 = "1.28"
zstd = "0.13.2" zstd = "0.13.3"
file-guard = "0.2" file-guard = "0.2"

View File

@ -3,6 +3,7 @@ use std::{collections::HashMap, hash::Hash, sync::Arc};
use async_trait::async_trait; use async_trait::async_trait;
use bounded_num::{NotInBounds, Number}; use bounded_num::{NotInBounds, Number};
use pumpkin_data::damage::DamageType; use pumpkin_data::damage::DamageType;
use pumpkin_data::entity::EffectType;
use pumpkin_data::particle::Particle; use pumpkin_data::particle::Particle;
use pumpkin_data::sound::SoundCategory; use pumpkin_data::sound::SoundCategory;
use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
@ -27,17 +28,15 @@ pub mod bossbar_style;
pub mod bounded_num; pub mod bounded_num;
pub mod command; pub mod command;
mod coordinate; mod coordinate;
pub mod damage_type;
pub mod entities; pub mod entities;
pub mod entity; pub mod entity;
pub mod gamemode; pub mod gamemode;
pub mod item;
pub mod message; pub mod message;
pub mod particle;
pub mod players; pub mod players;
pub mod position_2d; pub mod position_2d;
pub mod position_3d; pub mod position_3d;
pub mod position_block; pub mod position_block;
pub mod resource;
pub mod resource_location; pub mod resource_location;
pub mod rotation; pub mod rotation;
pub mod simple; pub mod simple;
@ -105,6 +104,7 @@ pub enum Arg<'a> {
Simple(&'a str), Simple(&'a str),
SoundCategory(SoundCategory), SoundCategory(SoundCategory),
DamageType(DamageType), DamageType(DamageType),
Effect(EffectType),
} }
/// see [`crate::commands::tree::builder::argument`] and [`CommandTree::execute`]/[`crate::commands::tree::builder::NonLeafNodeBuilder::execute`] /// see [`crate::commands::tree::builder::argument`] and [`CommandTree::execute`]/[`crate::commands::tree::builder::NonLeafNodeBuilder::execute`]

View File

@ -4,7 +4,10 @@ use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, Suggestion
use crate::command::{ use crate::command::{
CommandSender, CommandSender,
args::{Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser}, args::{
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
GetClientSideArgParser,
},
dispatcher::CommandError, dispatcher::CommandError,
tree::RawArgs, tree::RawArgs,
}; };
@ -59,7 +62,7 @@ impl DefaultNameArgConsumer for DamageTypeArgumentConsumer {
impl<'a> FindArg<'a> for DamageTypeArgumentConsumer { impl<'a> FindArg<'a> for DamageTypeArgumentConsumer {
type Data = &'a DamageType; 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) { match args.get(name) {
Some(Arg::DamageType(data)) => Ok(data), Some(Arg::DamageType(data)) => Ok(data),
_ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))),

View File

@ -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()))),
}
}
}

View File

@ -2,15 +2,16 @@ use async_trait::async_trait;
use pumpkin_data::item::Item; use pumpkin_data::item::Item;
use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders};
use crate::{command::dispatcher::CommandError, server::Server}; use crate::command::{
use super::{
super::{
CommandSender, CommandSender,
args::{ArgumentConsumer, RawArgs}, args::{
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
GetClientSideArgParser,
}, },
Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, dispatcher::CommandError,
tree::RawArgs,
}; };
use crate::server::Server;
pub struct ItemArgumentConsumer; pub struct ItemArgumentConsumer;
@ -55,7 +56,7 @@ impl DefaultNameArgConsumer for ItemArgumentConsumer {
impl<'a> FindArg<'a> for ItemArgumentConsumer { impl<'a> FindArg<'a> for ItemArgumentConsumer {
type Data = (&'a str, Item); 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) { match args.get(name) {
Some(Arg::Item(name)) => Item::from_name(&name.replace("minecraft:", "")).map_or_else( Some(Arg::Item(name)) => Item::from_name(&name.replace("minecraft:", "")).map_or_else(
|| { || {

View File

@ -0,0 +1,4 @@
pub mod damage_type;
pub mod effect;
pub mod item;
pub mod particle;

View File

@ -4,7 +4,10 @@ use pumpkin_protocol::client::play::{ArgumentType, CommandSuggestion, Suggestion
use crate::command::{ use crate::command::{
CommandSender, CommandSender,
args::{Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser}, args::{
Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg,
GetClientSideArgParser,
},
dispatcher::CommandError, dispatcher::CommandError,
tree::RawArgs, tree::RawArgs,
}; };
@ -59,7 +62,7 @@ impl DefaultNameArgConsumer for ParticleArgumentConsumer {
impl<'a> FindArg<'a> for ParticleArgumentConsumer { impl<'a> FindArg<'a> for ParticleArgumentConsumer {
type Data = &'a Particle; 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) { match args.get(name) {
Some(Arg::Particle(data)) => Ok(data), Some(Arg::Particle(data)) => Ok(data),
_ => Err(CommandError::InvalidConsumption(Some(name.to_string()))), _ => Err(CommandError::InvalidConsumption(Some(name.to_string()))),

View File

@ -20,10 +20,10 @@ const DESCRIPTION: &str = "bans a player";
const ARG_TARGET: &str = "player"; const ARG_TARGET: &str = "player";
const ARG_REASON: &str = "reason"; const ARG_REASON: &str = "reason";
struct BanNoReasonExecutor; struct NoReasonExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for BanNoReasonExecutor { impl CommandExecutor for NoReasonExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -39,10 +39,10 @@ impl CommandExecutor for BanNoReasonExecutor {
} }
} }
struct BanReasonExecutor; struct ReasonExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for BanReasonExecutor { impl CommandExecutor for ReasonExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -107,7 +107,7 @@ async fn ban_player(sender: &CommandSender<'_>, player: &Player, reason: Option<
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then( CommandTree::new(NAMES, DESCRIPTION).then(
argument(ARG_TARGET, PlayersArgumentConsumer) argument(ARG_TARGET, PlayersArgumentConsumer)
.execute(BanNoReasonExecutor) .execute(NoReasonExecutor)
.then(argument(ARG_REASON, MsgArgConsumer).execute(BanReasonExecutor)), .then(argument(ARG_REASON, MsgArgConsumer).execute(ReasonExecutor)),
) )
} }

View File

@ -36,10 +36,10 @@ async fn parse_ip(target: &str, server: &Server) -> Option<IpAddr> {
}) })
} }
struct BanIpNoReasonExecutor; struct NoReasonExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for BanIpNoReasonExecutor { impl CommandExecutor for NoReasonExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -55,10 +55,10 @@ impl CommandExecutor for BanIpNoReasonExecutor {
} }
} }
struct BanIpReasonExecutor; struct ReasonExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for BanIpReasonExecutor { impl CommandExecutor for ReasonExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, 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 { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then( CommandTree::new(NAMES, DESCRIPTION).then(
argument(ARG_TARGET, SimpleArgConsumer) argument(ARG_TARGET, SimpleArgConsumer)
.execute(BanIpNoReasonExecutor) .execute(NoReasonExecutor)
.then(argument(ARG_REASON, MsgArgConsumer).execute(BanIpReasonExecutor)), .then(argument(ARG_REASON, MsgArgConsumer).execute(ReasonExecutor)),
) )
} }

View File

@ -16,10 +16,10 @@ const DESCRIPTION: &str = "shows the banlist";
const ARG_LIST_TYPE: &str = "ips|players"; const ARG_LIST_TYPE: &str = "ips|players";
struct BanListExecutor; struct ListExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for BanListExecutor { impl CommandExecutor for ListExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -74,10 +74,10 @@ impl CommandExecutor for BanListExecutor {
} }
} }
struct BanListAllExecutor; struct ListAllExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for BanListAllExecutor { impl CommandExecutor for ListAllExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, 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 { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.execute(BanListAllExecutor) .execute(ListAllExecutor)
.then(argument(ARG_LIST_TYPE, SimpleArgConsumer).execute(BanListExecutor)) .then(argument(ARG_LIST_TYPE, SimpleArgConsumer).execute(ListExecutor))
} }

View File

@ -52,10 +52,10 @@ enum CommandValueSet {
Visible, Visible,
} }
struct BossbarAddExecuter; struct AddExecuter;
#[async_trait] #[async_trait]
impl CommandExecutor for BossbarAddExecuter { impl CommandExecutor for AddExecuter {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -101,10 +101,10 @@ impl CommandExecutor for BossbarAddExecuter {
} }
} }
struct BossbarGetExecuter(CommandValueGet); struct GetExecuter(CommandValueGet);
#[async_trait] #[async_trait]
impl CommandExecutor for BossbarGetExecuter { impl CommandExecutor for GetExecuter {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -179,10 +179,10 @@ impl CommandExecutor for BossbarGetExecuter {
} }
} }
struct BossbarListExecuter; struct ListExecuter;
#[async_trait] #[async_trait]
impl CommandExecutor for BossbarListExecuter { impl CommandExecutor for ListExecuter {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -238,10 +238,10 @@ impl CommandExecutor for BossbarListExecuter {
} }
} }
struct BossbarRemoveExecuter; struct RemoveExecuter;
#[async_trait] #[async_trait]
impl CommandExecutor for BossbarRemoveExecuter { impl CommandExecutor for RemoveExecuter {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -289,10 +289,10 @@ impl CommandExecutor for BossbarRemoveExecuter {
} }
} }
struct BossbarSetExecuter(CommandValueSet); struct SetExecuter(CommandValueSet);
#[async_trait] #[async_trait]
impl CommandExecutor for BossbarSetExecuter { impl CommandExecutor for SetExecuter {
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
async fn execute<'a>( async fn execute<'a>(
&self, &self,
@ -591,25 +591,24 @@ fn value_consumer() -> BoundedNumArgumentConsumer<i32> {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.then( .then(
literal("add") literal("add").then(
.then(argument_default_name(non_autocomplete_consumer()).then( argument_default_name(non_autocomplete_consumer())
argument(ARG_NAME, TextComponentArgConsumer).execute(BossbarAddExecuter), .then(argument(ARG_NAME, TextComponentArgConsumer).execute(AddExecuter)),
)), ),
) )
.then( .then(
literal("get").then( literal("get").then(
argument_default_name(autocomplete_consumer()) argument_default_name(autocomplete_consumer())
.then(literal("max").execute(BossbarGetExecuter(CommandValueGet::Max))) .then(literal("max").execute(GetExecuter(CommandValueGet::Max)))
.then(literal("players").execute(BossbarGetExecuter(CommandValueGet::Players))) .then(literal("players").execute(GetExecuter(CommandValueGet::Players)))
.then(literal("value").execute(BossbarGetExecuter(CommandValueGet::Value))) .then(literal("value").execute(GetExecuter(CommandValueGet::Value)))
.then(literal("visible").execute(BossbarGetExecuter(CommandValueGet::Visible))), .then(literal("visible").execute(GetExecuter(CommandValueGet::Visible))),
), ),
) )
.then(literal("list").execute(BossbarListExecuter)) .then(literal("list").execute(ListExecuter))
.then( .then(
literal("remove").then( literal("remove")
argument_default_name(autocomplete_consumer()).execute(BossbarRemoveExecuter), .then(argument_default_name(autocomplete_consumer()).execute(RemoveExecuter)),
),
) )
.then( .then(
literal("set").then( literal("set").then(
@ -617,45 +616,45 @@ pub fn init_command_tree() -> CommandTree {
.then( .then(
literal("color").then( literal("color").then(
argument_default_name(BossbarColorArgumentConsumer) argument_default_name(BossbarColorArgumentConsumer)
.execute(BossbarSetExecuter(CommandValueSet::Color)), .execute(SetExecuter(CommandValueSet::Color)),
), ),
) )
.then( .then(
literal("max").then( literal("max").then(
argument_default_name(max_value_consumer()) argument_default_name(max_value_consumer())
.execute(BossbarSetExecuter(CommandValueSet::Max)), .execute(SetExecuter(CommandValueSet::Max)),
), ),
) )
.then( .then(
literal("name").then( literal("name").then(
argument(ARG_NAME, TextComponentArgConsumer) argument(ARG_NAME, TextComponentArgConsumer)
.execute(BossbarSetExecuter(CommandValueSet::Name)), .execute(SetExecuter(CommandValueSet::Name)),
), ),
) )
.then( .then(
literal("players") literal("players")
.then( .then(
argument_default_name(PlayersArgumentConsumer) 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( .then(
literal("style").then( literal("style").then(
argument_default_name(BossbarStyleArgumentConsumer) argument_default_name(BossbarStyleArgumentConsumer)
.execute(BossbarSetExecuter(CommandValueSet::Style)), .execute(SetExecuter(CommandValueSet::Style)),
), ),
) )
.then( .then(
literal("value").then( literal("value").then(
argument_default_name(value_consumer()) argument_default_name(value_consumer())
.execute(BossbarSetExecuter(CommandValueSet::Value)), .execute(SetExecuter(CommandValueSet::Value)),
), ),
) )
.then( .then(
literal("visible").then( literal("visible").then(
argument(ARG_VISIBLE, BoolArgConsumer) argument(ARG_VISIBLE, BoolArgConsumer)
.execute(BossbarSetExecuter(CommandValueSet::Visible)), .execute(SetExecuter(CommandValueSet::Visible)),
), ),
), ),
), ),

View File

@ -73,10 +73,10 @@ fn clear_command_text_output(item_count: usize, targets: &[Arc<Player>]) -> Text
} }
} }
struct ClearExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for ClearExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -100,10 +100,10 @@ impl CommandExecutor for ClearExecutor {
} }
} }
struct ClearSelfExecutor; struct SelfExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for ClearSelfExecutor { impl CommandExecutor for SelfExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -126,6 +126,6 @@ impl CommandExecutor for ClearSelfExecutor {
#[allow(clippy::redundant_closure_for_method_calls)] // causes lifetime issues #[allow(clippy::redundant_closure_for_method_calls)] // causes lifetime issues
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.then(argument(ARG_TARGET, EntitiesArgumentConsumer).execute(ClearExecutor)) .then(argument(ARG_TARGET, EntitiesArgumentConsumer).execute(Executor))
.then(require(|sender| sender.is_player()).execute(ClearSelfExecutor)) .then(require(|sender| sender.is_player()).execute(SelfExecutor))
} }

View File

@ -9,11 +9,13 @@ use crate::command::{
CommandError, CommandExecutor, CommandSender, CommandError, CommandExecutor, CommandSender,
args::{ args::{
Arg, ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer, Arg, ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer,
damage_type::DamageTypeArgumentConsumer, entity::EntityArgumentConsumer, entity::EntityArgumentConsumer, position_3d::Position3DArgumentConsumer,
position_3d::Position3DArgumentConsumer, resource::damage_type::DamageTypeArgumentConsumer,
},
tree::{
CommandTree,
builder::{argument, literal},
}, },
tree::CommandTree,
tree::builder::{argument, literal},
}; };
const NAMES: [&str; 1] = ["damage"]; const NAMES: [&str; 1] = ["damage"];
@ -29,8 +31,8 @@ fn amount_consumer() -> BoundedNumArgumentConsumer<f32> {
BoundedNumArgumentConsumer::new().name(ARG_AMOUNT).min(0.0) BoundedNumArgumentConsumer::new().name(ARG_AMOUNT).min(0.0)
} }
struct DamageLocationExecutor; struct LocationExecutor;
struct DamageEntityExecutor(bool); struct EntityExecutor(bool);
async fn send_damage_result( async fn send_damage_result(
sender: &mut CommandSender<'_>, sender: &mut CommandSender<'_>,
@ -60,7 +62,7 @@ async fn send_damage_result(
} }
#[async_trait] #[async_trait]
impl CommandExecutor for DamageLocationExecutor { impl CommandExecutor for LocationExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -100,7 +102,7 @@ impl CommandExecutor for DamageLocationExecutor {
} }
#[async_trait] #[async_trait]
impl CommandExecutor for DamageEntityExecutor { impl CommandExecutor for EntityExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -155,28 +157,28 @@ pub fn init_command_tree() -> CommandTree {
argument(ARG_TARGET, EntityArgumentConsumer).then( argument(ARG_TARGET, EntityArgumentConsumer).then(
argument(ARG_AMOUNT, amount_consumer()) argument(ARG_AMOUNT, amount_consumer())
// Basic damage // Basic damage
.execute(DamageEntityExecutor(false)) .execute(EntityExecutor(false))
// With damage type // With damage type
.then( .then(
argument(ARG_DAMAGE_TYPE, DamageTypeArgumentConsumer) argument(ARG_DAMAGE_TYPE, DamageTypeArgumentConsumer)
.execute(DamageEntityExecutor(false)) .execute(EntityExecutor(false))
// At location // At location
.then( .then(
literal("at").then( literal("at").then(
argument(ARG_LOCATION, Position3DArgumentConsumer) argument(ARG_LOCATION, Position3DArgumentConsumer)
.execute(DamageLocationExecutor), .execute(LocationExecutor),
), ),
) )
// By entity // By entity
.then( .then(
literal("by").then( literal("by").then(
argument(ARG_ENTITY, EntityArgumentConsumer) argument(ARG_ENTITY, EntityArgumentConsumer)
.execute(DamageEntityExecutor(false)) .execute(EntityExecutor(false))
// From cause // From cause
.then( .then(
literal("from").then( literal("from").then(
argument(ARG_CAUSE, EntityArgumentConsumer) argument(ARG_CAUSE, EntityArgumentConsumer)
.execute(DamageEntityExecutor(true)), .execute(EntityExecutor(true)),
), ),
), ),
), ),

View File

@ -15,10 +15,10 @@ const NAMES: [&str; 1] = ["deop"];
const DESCRIPTION: &str = "Revokes operator status from a player."; const DESCRIPTION: &str = "Revokes operator status from a player.";
const ARG_TARGETS: &str = "targets"; const ARG_TARGETS: &str = "targets";
struct DeopExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for DeopExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -61,5 +61,5 @@ impl CommandExecutor for DeopExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.then(argument(ARG_TARGETS, PlayersArgumentConsumer).execute(DeopExecutor)) .then(argument(ARG_TARGETS, PlayersArgumentConsumer).execute(Executor))
} }

View File

@ -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
}

View File

@ -38,12 +38,12 @@ enum ExpType {
Levels, Levels,
} }
struct ExperienceExecutor { struct Executor {
mode: Mode, mode: Mode,
exp_type: Option<ExpType>, exp_type: Option<ExpType>,
} }
impl ExperienceExecutor { impl Executor {
async fn handle_query( async fn handle_query(
&self, &self,
sender: &mut CommandSender<'_>, sender: &mut CommandSender<'_>,
@ -202,7 +202,7 @@ impl ExperienceExecutor {
} }
#[async_trait] #[async_trait]
impl CommandExecutor for ExperienceExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -281,15 +281,15 @@ pub fn init_command_tree() -> CommandTree {
literal("add").then( literal("add").then(
argument(ARG_TARGETS, PlayersArgumentConsumer).then( argument(ARG_TARGETS, PlayersArgumentConsumer).then(
argument(ARG_AMOUNT, xp_amount()) argument(ARG_AMOUNT, xp_amount())
.then(literal("levels").execute(ExperienceExecutor { .then(literal("levels").execute(Executor {
mode: Mode::Add, mode: Mode::Add,
exp_type: Some(ExpType::Levels), exp_type: Some(ExpType::Levels),
})) }))
.then(literal("points").execute(ExperienceExecutor { .then(literal("points").execute(Executor {
mode: Mode::Add, mode: Mode::Add,
exp_type: Some(ExpType::Points), exp_type: Some(ExpType::Points),
})) }))
.execute(ExperienceExecutor { .execute(Executor {
mode: Mode::Add, mode: Mode::Add,
exp_type: Some(ExpType::Points), exp_type: Some(ExpType::Points),
}), }),
@ -300,15 +300,15 @@ pub fn init_command_tree() -> CommandTree {
literal("set").then( literal("set").then(
argument(ARG_TARGETS, PlayersArgumentConsumer).then( argument(ARG_TARGETS, PlayersArgumentConsumer).then(
argument(ARG_AMOUNT, xp_amount()) argument(ARG_AMOUNT, xp_amount())
.then(literal("levels").execute(ExperienceExecutor { .then(literal("levels").execute(Executor {
mode: Mode::Set, mode: Mode::Set,
exp_type: Some(ExpType::Levels), exp_type: Some(ExpType::Levels),
})) }))
.then(literal("points").execute(ExperienceExecutor { .then(literal("points").execute(Executor {
mode: Mode::Set, mode: Mode::Set,
exp_type: Some(ExpType::Points), exp_type: Some(ExpType::Points),
})) }))
.execute(ExperienceExecutor { .execute(Executor {
mode: Mode::Set, mode: Mode::Set,
exp_type: Some(ExpType::Points), exp_type: Some(ExpType::Points),
}), }),
@ -318,11 +318,11 @@ pub fn init_command_tree() -> CommandTree {
.then( .then(
literal("query").then( literal("query").then(
argument(ARG_TARGETS, PlayersArgumentConsumer) argument(ARG_TARGETS, PlayersArgumentConsumer)
.then(literal("levels").execute(ExperienceExecutor { .then(literal("levels").execute(Executor {
mode: Mode::Query, mode: Mode::Query,
exp_type: Some(ExpType::Levels), exp_type: Some(ExpType::Levels),
})) }))
.then(literal("points").execute(ExperienceExecutor { .then(literal("points").execute(Executor {
mode: Mode::Query, mode: Mode::Query,
exp_type: Some(ExpType::Points), exp_type: Some(ExpType::Points),
})), })),

View File

@ -33,11 +33,11 @@ enum Mode {
Replace, Replace,
} }
struct SetblockExecutor(Mode); struct Executor(Mode);
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
#[async_trait] #[async_trait]
impl CommandExecutor for SetblockExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -164,12 +164,12 @@ pub fn init_command_tree() -> CommandTree {
argument(ARG_FROM, BlockPosArgumentConsumer).then( argument(ARG_FROM, BlockPosArgumentConsumer).then(
argument(ARG_TO, BlockPosArgumentConsumer).then( argument(ARG_TO, BlockPosArgumentConsumer).then(
argument(ARG_BLOCK, BlockArgumentConsumer) argument(ARG_BLOCK, BlockArgumentConsumer)
.then(literal("destroy").execute(SetblockExecutor(Mode::Destroy))) .then(literal("destroy").execute(Executor(Mode::Destroy)))
.then(literal("hollow").execute(SetblockExecutor(Mode::Hollow))) .then(literal("hollow").execute(Executor(Mode::Hollow)))
.then(literal("keep").execute(SetblockExecutor(Mode::Keep))) .then(literal("keep").execute(Executor(Mode::Keep)))
.then(literal("outline").execute(SetblockExecutor(Mode::Outline))) .then(literal("outline").execute(Executor(Mode::Outline)))
.then(literal("replace").execute(SetblockExecutor(Mode::Replace))) .then(literal("replace").execute(Executor(Mode::Replace)))
.execute(SetblockExecutor(Mode::Replace)), .execute(Executor(Mode::Replace)),
), ),
), ),
) )

View File

@ -23,10 +23,10 @@ const DESCRIPTION: &str = "Change a player's gamemode.";
const ARG_GAMEMODE: &str = "gamemode"; const ARG_GAMEMODE: &str = "gamemode";
const ARG_TARGET: &str = "target"; const ARG_TARGET: &str = "target";
struct GamemodeTargetSelf; struct TargetSelfExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for GamemodeTargetSelf { impl CommandExecutor for TargetSelfExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -56,10 +56,10 @@ impl CommandExecutor for GamemodeTargetSelf {
} }
} }
struct GamemodeTargetPlayer; struct TargetPlayerExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for GamemodeTargetPlayer { impl CommandExecutor for TargetPlayerExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -108,7 +108,7 @@ impl CommandExecutor for GamemodeTargetPlayer {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then( CommandTree::new(NAMES, DESCRIPTION).then(
argument(ARG_GAMEMODE, GamemodeArgumentConsumer) argument(ARG_GAMEMODE, GamemodeArgumentConsumer)
.then(require(|sender| sender.is_player()).execute(GamemodeTargetSelf)) .then(require(|sender| sender.is_player()).execute(TargetSelfExecutor))
.then(argument(ARG_TARGET, PlayersArgumentConsumer).execute(GamemodeTargetPlayer)), .then(argument(ARG_TARGET, PlayersArgumentConsumer).execute(TargetPlayerExecutor)),
) )
} }

View File

@ -5,8 +5,8 @@ use pumpkin_util::text::color::{Color, NamedColor};
use pumpkin_util::text::hover::HoverEvent; use pumpkin_util::text::hover::HoverEvent;
use crate::command::args::bounded_num::BoundedNumArgumentConsumer; use crate::command::args::bounded_num::BoundedNumArgumentConsumer;
use crate::command::args::item::ItemArgumentConsumer;
use crate::command::args::players::PlayersArgumentConsumer; use crate::command::args::players::PlayersArgumentConsumer;
use crate::command::args::resource::item::ItemArgumentConsumer;
use crate::command::args::{ConsumedArgs, FindArg, FindArgDefaultName}; use crate::command::args::{ConsumedArgs, FindArg, FindArgDefaultName};
use crate::command::tree::CommandTree; use crate::command::tree::CommandTree;
use crate::command::tree::builder::{argument, argument_default_name}; use crate::command::tree::builder::{argument, argument_default_name};
@ -25,10 +25,10 @@ fn item_count_consumer() -> BoundedNumArgumentConsumer<i32> {
.max(i32::MAX) .max(i32::MAX)
} }
struct GiveExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for GiveExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -112,8 +112,8 @@ pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then( CommandTree::new(NAMES, DESCRIPTION).then(
argument_default_name(PlayersArgumentConsumer).then( argument_default_name(PlayersArgumentConsumer).then(
argument(ARG_ITEM, ItemArgumentConsumer) argument(ARG_ITEM, ItemArgumentConsumer)
.execute(GiveExecutor) .execute(Executor)
.then(argument_default_name(item_count_consumer()).execute(GiveExecutor)), .then(argument_default_name(item_count_consumer()).execute(Executor)),
), ),
) )
} }

View File

@ -25,10 +25,10 @@ fn page_number_consumer() -> BoundedNumArgumentConsumer<i32> {
BoundedNumArgumentConsumer::new().name("page").min(1) BoundedNumArgumentConsumer::new().name("page").min(1)
} }
struct CommandHelpExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for CommandHelpExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -222,7 +222,7 @@ impl CommandExecutor for BaseHelpExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) 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)) .then(argument_default_name(page_number_consumer()).execute(BaseHelpExecutor))
.execute(BaseHelpExecutor) .execute(BaseHelpExecutor)
} }

View File

@ -18,10 +18,10 @@ const ARG_TARGETS: &str = "targets";
const ARG_REASON: &str = "reason"; const ARG_REASON: &str = "reason";
struct KickExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for KickExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -52,7 +52,7 @@ impl CommandExecutor for KickExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then( CommandTree::new(NAMES, DESCRIPTION).then(
argument(ARG_TARGETS, PlayersArgumentConsumer) argument(ARG_TARGETS, PlayersArgumentConsumer)
.execute(KickExecutor) .execute(Executor)
.then(argument(ARG_REASON, MsgArgConsumer).execute(KickExecutor)), .then(argument(ARG_REASON, MsgArgConsumer).execute(Executor)),
) )
} }

View File

@ -16,10 +16,10 @@ const DESCRIPTION: &str = "Kills all target entities.";
const ARG_TARGET: &str = "target"; const ARG_TARGET: &str = "target";
struct KillExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for KillExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -66,10 +66,10 @@ impl CommandExecutor for KillExecutor {
} }
} }
struct KillSelfExecutor; struct SelfExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for KillSelfExecutor { impl CommandExecutor for SelfExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -104,6 +104,6 @@ impl CommandExecutor for KillSelfExecutor {
#[allow(clippy::redundant_closure_for_method_calls)] // causes lifetime issues #[allow(clippy::redundant_closure_for_method_calls)] // causes lifetime issues
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.then(argument(ARG_TARGET, EntitiesArgumentConsumer).execute(KillExecutor)) .then(argument(ARG_TARGET, EntitiesArgumentConsumer).execute(Executor))
.then(require(|sender| sender.is_player()).execute(KillSelfExecutor)) .then(require(|sender| sender.is_player()).execute(SelfExecutor))
} }

View File

@ -15,10 +15,10 @@ const NAMES: [&str; 1] = ["list"];
const DESCRIPTION: &str = "Print the list of online players."; const DESCRIPTION: &str = "Print the list of online players.";
struct ListExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for ListExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -54,5 +54,5 @@ fn get_player_names(players: Vec<Arc<Player>>) -> String {
} }
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).execute(ListExecutor) CommandTree::new(NAMES, DESCRIPTION).execute(Executor)
} }

View File

@ -16,10 +16,10 @@ const DESCRIPTION: &str = "Broadcasts a narrative message about yourself.";
const ARG_MESSAGE: &str = "action"; const ARG_MESSAGE: &str = "action";
struct MeExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for MeExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -44,5 +44,5 @@ impl CommandExecutor for MeExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.then(argument(ARG_MESSAGE, MsgArgConsumer).execute(MeExecutor)) .then(argument(ARG_MESSAGE, MsgArgConsumer).execute(Executor))
} }

View File

@ -1,36 +1,90 @@
pub mod ban; use pumpkin_util::PermissionLvl;
pub mod banip;
pub mod banlist; use super::dispatcher::CommandDispatcher;
pub mod bossbar;
pub mod clear; mod ban;
pub mod damage; mod banip;
pub mod deop; mod banlist;
pub mod experience; mod bossbar;
pub mod fill; mod clear;
pub mod gamemode; mod damage;
pub mod give; mod deop;
pub mod help; mod effect;
pub mod kick; mod experience;
pub mod kill; mod fill;
pub mod list; mod gamemode;
pub mod me; mod give;
pub mod msg; mod help;
pub mod op; mod kick;
pub mod pardon; mod kill;
pub mod pardonip; mod list;
pub mod particle; mod me;
pub mod playsound; mod msg;
pub mod plugin; mod op;
pub mod plugins; mod pardon;
pub mod pumpkin; mod pardonip;
pub mod say; mod particle;
pub mod seed; mod playsound;
pub mod setblock; mod plugin;
pub mod stop; mod plugins;
pub mod summon; mod pumpkin;
pub mod teleport; mod say;
pub mod time; mod seed;
pub mod title; mod setblock;
pub mod transfer; mod stop;
pub mod weather; mod summon;
pub mod worldborder; 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
}

View File

@ -19,10 +19,10 @@ const DESCRIPTION: &str = "Sends a private message to one or more players.";
const ARG_MESSAGE: &str = "message"; const ARG_MESSAGE: &str = "message";
struct MsgExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for MsgExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -81,6 +81,6 @@ impl CommandExecutor for MsgExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then( CommandTree::new(NAMES, DESCRIPTION).then(
argument_default_name(PlayersArgumentConsumer) argument_default_name(PlayersArgumentConsumer)
.then(argument(ARG_MESSAGE, MsgArgConsumer).execute(MsgExecutor)), .then(argument(ARG_MESSAGE, MsgArgConsumer).execute(Executor)),
) )
} }

View File

@ -16,10 +16,10 @@ const NAMES: [&str; 1] = ["op"];
const DESCRIPTION: &str = "Grants operator status to a player."; const DESCRIPTION: &str = "Grants operator status to a player.";
const ARG_TARGETS: &str = "targets"; const ARG_TARGETS: &str = "targets";
struct OpExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for OpExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -81,5 +81,5 @@ impl CommandExecutor for OpExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.then(argument(ARG_TARGETS, PlayersArgumentConsumer).execute(OpExecutor)) .then(argument(ARG_TARGETS, PlayersArgumentConsumer).execute(Executor))
} }

View File

@ -16,10 +16,10 @@ const DESCRIPTION: &str = "unbans a player";
const ARG_TARGET: &str = "player"; const ARG_TARGET: &str = "player";
struct PardonExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for PardonExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -60,5 +60,5 @@ impl CommandExecutor for PardonExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.then(argument(ARG_TARGET, SimpleArgConsumer).execute(PardonExecutor)) .then(argument(ARG_TARGET, SimpleArgConsumer).execute(Executor))
} }

View File

@ -18,10 +18,10 @@ const DESCRIPTION: &str = "unbans a ip";
const ARG_TARGET: &str = "ip"; const ARG_TARGET: &str = "ip";
struct PardonIpExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for PardonIpExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -64,5 +64,5 @@ impl CommandExecutor for PardonIpExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.then(argument(ARG_TARGET, SimpleArgConsumer).execute(PardonIpExecutor)) .then(argument(ARG_TARGET, SimpleArgConsumer).execute(Executor))
} }

View File

@ -5,7 +5,7 @@ use crate::command::{
CommandError, CommandExecutor, CommandSender, CommandError, CommandExecutor, CommandSender,
args::{ args::{
ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer, ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer,
particle::ParticleArgumentConsumer, position_3d::Position3DArgumentConsumer, position_3d::Position3DArgumentConsumer, resource::particle::ParticleArgumentConsumer,
}, },
tree::{CommandTree, builder::argument}, tree::{CommandTree, builder::argument},
}; };
@ -20,10 +20,10 @@ const ARG_DELTA: &str = "delta";
const ARG_SPEED: &str = "speed"; const ARG_SPEED: &str = "speed";
const ARG_COUNT: &str = "count"; const ARG_COUNT: &str = "count";
struct ParticleExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for ParticleExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -64,25 +64,25 @@ impl CommandExecutor for ParticleExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then( CommandTree::new(NAMES, DESCRIPTION).then(
argument(ARG_NAME, ParticleArgumentConsumer) argument(ARG_NAME, ParticleArgumentConsumer)
.execute(ParticleExecutor) .execute(Executor)
.then( .then(
argument(ARG_POS, Position3DArgumentConsumer) argument(ARG_POS, Position3DArgumentConsumer)
.execute(ParticleExecutor) .execute(Executor)
.then( .then(
argument(ARG_DELTA, Position3DArgumentConsumer) argument(ARG_DELTA, Position3DArgumentConsumer)
.execute(ParticleExecutor) .execute(Executor)
.then( .then(
argument( argument(
ARG_SPEED, ARG_SPEED,
BoundedNumArgumentConsumer::<f32>::new().min(0.0), BoundedNumArgumentConsumer::<f32>::new().min(0.0),
) )
.execute(ParticleExecutor) .execute(Executor)
.then( .then(
argument( argument(
ARG_COUNT, ARG_COUNT,
BoundedNumArgumentConsumer::<i32>::new().min(0), BoundedNumArgumentConsumer::<i32>::new().min(0),
) )
.execute(ParticleExecutor), .execute(Executor),
), ),
), ),
), ),

View File

@ -57,10 +57,10 @@ fn min_volume_consumer() -> BoundedNumArgumentConsumer<f32> {
.max(1.0) .max(1.0)
} }
struct SoundExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for SoundExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -183,18 +183,18 @@ pub fn init_command_tree() -> CommandTree {
ARG_MIN_VOLUME, ARG_MIN_VOLUME,
min_volume_consumer(), 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),
) )
} }

View File

@ -12,10 +12,10 @@ const NAMES: [&str; 1] = ["plugins"];
const DESCRIPTION: &str = "List all available plugins."; const DESCRIPTION: &str = "List all available plugins.";
struct ListExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for ListExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -63,5 +63,5 @@ impl CommandExecutor for ListExecutor {
} }
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).execute(ListExecutor) CommandTree::new(NAMES, DESCRIPTION).execute(Executor)
} }

View File

@ -17,13 +17,13 @@ const NAMES: [&str; 2] = ["pumpkin", "version"];
const DESCRIPTION: &str = "Display information about Pumpkin."; const DESCRIPTION: &str = "Display information about Pumpkin.";
struct PumpkinExecutor; struct Executor;
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
const CARGO_PKG_DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION"); const CARGO_PKG_DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION");
#[async_trait] #[async_trait]
impl CommandExecutor for PumpkinExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -98,5 +98,5 @@ impl CommandExecutor for PumpkinExecutor {
} }
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).execute(PumpkinExecutor) CommandTree::new(NAMES, DESCRIPTION).execute(Executor)
} }

View File

@ -16,10 +16,10 @@ const DESCRIPTION: &str = "Broadcast a message to all Players.";
const ARG_MESSAGE: &str = "message"; const ARG_MESSAGE: &str = "message";
struct SayExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for SayExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -44,5 +44,5 @@ impl CommandExecutor for SayExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.then(argument(ARG_MESSAGE, MsgArgConsumer).execute(SayExecutor)) .then(argument(ARG_MESSAGE, MsgArgConsumer).execute(Executor))
} }

View File

@ -11,10 +11,10 @@ const NAMES: [&str; 1] = ["seed"];
const DESCRIPTION: &str = "Displays the world seed."; const DESCRIPTION: &str = "Displays the world seed.";
struct PumpkinExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for PumpkinExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -54,5 +54,5 @@ impl CommandExecutor for PumpkinExecutor {
} }
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).execute(PumpkinExecutor) CommandTree::new(NAMES, DESCRIPTION).execute(Executor)
} }

View File

@ -27,10 +27,10 @@ enum Mode {
Replace, Replace,
} }
struct SetblockExecutor(Mode); struct Executor(Mode);
#[async_trait] #[async_trait]
impl CommandExecutor for SetblockExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -90,10 +90,10 @@ pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then( CommandTree::new(NAMES, DESCRIPTION).then(
argument(ARG_BLOCK_POS, BlockPosArgumentConsumer).then( argument(ARG_BLOCK_POS, BlockPosArgumentConsumer).then(
argument(ARG_BLOCK, BlockArgumentConsumer) argument(ARG_BLOCK, BlockArgumentConsumer)
.then(literal("replace").execute(SetblockExecutor(Mode::Replace))) .then(literal("replace").execute(Executor(Mode::Replace)))
.then(literal("destroy").execute(SetblockExecutor(Mode::Destroy))) .then(literal("destroy").execute(Executor(Mode::Destroy)))
.then(literal("keep").execute(SetblockExecutor(Mode::Keep))) .then(literal("keep").execute(Executor(Mode::Keep)))
.execute(SetblockExecutor(Mode::Replace)), .execute(Executor(Mode::Replace)),
), ),
) )
} }

View File

@ -11,10 +11,10 @@ const NAMES: [&str; 1] = ["stop"];
const DESCRIPTION: &str = "Stop the server."; const DESCRIPTION: &str = "Stop the server.";
struct StopExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for StopExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -32,5 +32,5 @@ impl CommandExecutor for StopExecutor {
} }
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).execute(StopExecutor) CommandTree::new(NAMES, DESCRIPTION).execute(Executor)
} }

View File

@ -21,10 +21,10 @@ const ARG_ENTITY: &str = "entity";
const ARG_POS: &str = "pos"; const ARG_POS: &str = "pos";
struct SummonExecutor; struct Executor;
#[async_trait] #[async_trait]
impl CommandExecutor for SummonExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -54,8 +54,8 @@ impl CommandExecutor for SummonExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then( CommandTree::new(NAMES, DESCRIPTION).then(
argument(ARG_ENTITY, SummonableEntitiesArgumentConsumer) argument(ARG_ENTITY, SummonableEntitiesArgumentConsumer)
.execute(SummonExecutor) .execute(Executor)
.then(argument(ARG_POS, Position3DArgumentConsumer).execute(SummonExecutor)), .then(argument(ARG_POS, Position3DArgumentConsumer).execute(Executor)),
// TODO: Add NBT // TODO: Add NBT
) )
} }

View File

@ -49,10 +49,10 @@ fn yaw_pitch_facing_position(
(yaw_degrees as f32, pitch_degrees as f32) (yaw_degrees as f32, pitch_degrees as f32)
} }
struct TpEntitiesToEntityExecutor; struct EntitiesToEntityExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for TpEntitiesToEntityExecutor { impl CommandExecutor for EntitiesToEntityExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
_sender: &mut CommandSender<'a>, _sender: &mut CommandSender<'a>,
@ -74,10 +74,10 @@ impl CommandExecutor for TpEntitiesToEntityExecutor {
} }
} }
struct TpEntitiesToPosFacingPosExecutor; struct EntitiesToPosFacingPosExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for TpEntitiesToPosFacingPosExecutor { impl CommandExecutor for EntitiesToPosFacingPosExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
_sender: &mut CommandSender<'a>, _sender: &mut CommandSender<'a>,
@ -99,10 +99,10 @@ impl CommandExecutor for TpEntitiesToPosFacingPosExecutor {
} }
} }
struct TpEntitiesToPosFacingEntityExecutor; struct EntitiesToPosFacingEntityExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for TpEntitiesToPosFacingEntityExecutor { impl CommandExecutor for EntitiesToPosFacingEntityExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
_sender: &mut CommandSender<'a>, _sender: &mut CommandSender<'a>,
@ -126,10 +126,10 @@ impl CommandExecutor for TpEntitiesToPosFacingEntityExecutor {
} }
} }
struct TpEntitiesToPosWithRotationExecutor; struct EntitiesToPosWithRotationExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for TpEntitiesToPosWithRotationExecutor { impl CommandExecutor for EntitiesToPosWithRotationExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
_sender: &mut CommandSender<'a>, _sender: &mut CommandSender<'a>,
@ -150,10 +150,10 @@ impl CommandExecutor for TpEntitiesToPosWithRotationExecutor {
} }
} }
struct TpEntitiesToPosExecutor; struct EntitiesToPosExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for TpEntitiesToPosExecutor { impl CommandExecutor for EntitiesToPosExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
_sender: &mut CommandSender<'a>, _sender: &mut CommandSender<'a>,
@ -174,10 +174,10 @@ impl CommandExecutor for TpEntitiesToPosExecutor {
} }
} }
struct TpSelfToEntityExecutor; struct SelfToEntityExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for TpSelfToEntityExecutor { impl CommandExecutor for SelfToEntityExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -204,10 +204,10 @@ impl CommandExecutor for TpSelfToEntityExecutor {
} }
} }
struct TpSelfToPosExecutor; struct SelfToPosExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for TpSelfToPosExecutor { impl CommandExecutor for SelfToPosExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -234,34 +234,34 @@ impl CommandExecutor for TpSelfToPosExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.then(argument(ARG_LOCATION, Position3DArgumentConsumer).execute(TpSelfToPosExecutor)) .then(argument(ARG_LOCATION, Position3DArgumentConsumer).execute(SelfToPosExecutor))
.then(argument(ARG_DESTINATION, EntityArgumentConsumer).execute(TpSelfToEntityExecutor)) .then(argument(ARG_DESTINATION, EntityArgumentConsumer).execute(SelfToEntityExecutor))
.then( .then(
argument(ARG_TARGETS, EntitiesArgumentConsumer) argument(ARG_TARGETS, EntitiesArgumentConsumer)
.then( .then(
argument(ARG_LOCATION, Position3DArgumentConsumer) argument(ARG_LOCATION, Position3DArgumentConsumer)
.execute(TpEntitiesToPosExecutor) .execute(EntitiesToPosExecutor)
.then( .then(
argument(ARG_ROTATION, RotationArgumentConsumer) argument(ARG_ROTATION, RotationArgumentConsumer)
.execute(TpEntitiesToPosWithRotationExecutor), .execute(EntitiesToPosWithRotationExecutor),
) )
.then( .then(
literal("facing") literal("facing")
.then( .then(
literal("entity").then( literal("entity").then(
argument(ARG_FACING_ENTITY, EntityArgumentConsumer) argument(ARG_FACING_ENTITY, EntityArgumentConsumer)
.execute(TpEntitiesToPosFacingEntityExecutor), .execute(EntitiesToPosFacingEntityExecutor),
), ),
) )
.then( .then(
argument(ARG_FACING_LOCATION, Position3DArgumentConsumer) argument(ARG_FACING_LOCATION, Position3DArgumentConsumer)
.execute(TpEntitiesToPosFacingPosExecutor), .execute(EntitiesToPosFacingPosExecutor),
), ),
), ),
) )
.then( .then(
argument(ARG_DESTINATION, EntityArgumentConsumer) argument(ARG_DESTINATION, EntityArgumentConsumer)
.execute(TpEntitiesToEntityExecutor), .execute(EntitiesToEntityExecutor),
), ),
) )
} }

View File

@ -44,10 +44,10 @@ enum QueryMode {
Day, Day,
} }
struct TimeQueryExecutor(QueryMode); struct QueryExecutor(QueryMode);
#[async_trait] #[async_trait]
impl CommandExecutor for TimeQueryExecutor { impl CommandExecutor for QueryExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -91,10 +91,10 @@ impl CommandExecutor for TimeQueryExecutor {
} }
} }
struct TimeChangeExecutor(Mode); struct ChangeExecutor(Mode);
#[async_trait] #[async_trait]
impl CommandExecutor for TimeChangeExecutor { impl CommandExecutor for ChangeExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -153,33 +153,27 @@ impl CommandExecutor for TimeChangeExecutor {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION) CommandTree::new(NAMES, DESCRIPTION)
.then( .then(
literal("add").then( literal("add")
argument(ARG_TIME, TimeArgumentConsumer).execute(TimeChangeExecutor(Mode::Add)), .then(argument(ARG_TIME, TimeArgumentConsumer).execute(ChangeExecutor(Mode::Add))),
),
) )
.then( .then(
literal("query") literal("query")
.then(literal("daytime").execute(TimeQueryExecutor(QueryMode::DayTime))) .then(literal("daytime").execute(QueryExecutor(QueryMode::DayTime)))
.then(literal("gametime").execute(TimeQueryExecutor(QueryMode::GameTime))) .then(literal("gametime").execute(QueryExecutor(QueryMode::GameTime)))
.then(literal("day").execute(TimeQueryExecutor(QueryMode::Day))), .then(literal("day").execute(QueryExecutor(QueryMode::Day))),
) )
.then( .then(
literal("set") literal("set")
.then(literal("day").execute(TimeChangeExecutor(Mode::Set(Some(PresetTime::Day))))) .then(literal("day").execute(ChangeExecutor(Mode::Set(Some(PresetTime::Day)))))
.then( .then(literal("noon").execute(ChangeExecutor(Mode::Set(Some(PresetTime::Noon)))))
literal("noon").execute(TimeChangeExecutor(Mode::Set(Some(PresetTime::Noon)))), .then(literal("night").execute(ChangeExecutor(Mode::Set(Some(PresetTime::Night)))))
)
.then(
literal("night")
.execute(TimeChangeExecutor(Mode::Set(Some(PresetTime::Night)))),
)
.then( .then(
literal("midnight") literal("midnight")
.execute(TimeChangeExecutor(Mode::Set(Some(PresetTime::Midnight)))), .execute(ChangeExecutor(Mode::Set(Some(PresetTime::Midnight)))),
) )
.then( .then(
argument(ARG_TIME, TimeArgumentConsumer) argument(ARG_TIME, TimeArgumentConsumer)
.execute(TimeChangeExecutor(Mode::Set(None))), .execute(ChangeExecutor(Mode::Set(None))),
), ),
) )
} }

View File

@ -29,10 +29,10 @@ fn port_consumer() -> BoundedNumArgumentConsumer<i32> {
.max(65535) .max(65535)
} }
struct TransferTargetSelf; struct TargetSelfExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for TransferTargetSelf { impl CommandExecutor for TargetSelfExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -71,10 +71,10 @@ impl CommandExecutor for TransferTargetSelf {
} }
} }
struct TransferTargetPlayer; struct TargetPlayerExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for TransferTargetPlayer { impl CommandExecutor for TargetPlayerExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -121,13 +121,13 @@ impl CommandExecutor for TransferTargetPlayer {
pub fn init_command_tree() -> CommandTree { pub fn init_command_tree() -> CommandTree {
CommandTree::new(NAMES, DESCRIPTION).then( CommandTree::new(NAMES, DESCRIPTION).then(
argument(ARG_HOSTNAME, SimpleArgConsumer) argument(ARG_HOSTNAME, SimpleArgConsumer)
.then(require(|sender| sender.is_player()).execute(TransferTargetSelf)) .then(require(|sender| sender.is_player()).execute(TargetSelfExecutor))
.then( .then(
argument_default_name(port_consumer()) argument_default_name(port_consumer())
.then(require(|sender| sender.is_player()).execute(TransferTargetSelf)) .then(require(|sender| sender.is_player()).execute(TargetSelfExecutor))
.then( .then(
argument(ARG_PLAYERS, PlayersArgumentConsumer) argument(ARG_PLAYERS, PlayersArgumentConsumer)
.execute(TransferTargetPlayer), .execute(TargetPlayerExecutor),
), ),
), ),
) )

View File

@ -12,7 +12,7 @@ const NAMES: [&str; 1] = ["weather"];
const DESCRIPTION: &str = "Changes the weather."; const DESCRIPTION: &str = "Changes the weather.";
const ARG_DURATION: &str = "duration"; const ARG_DURATION: &str = "duration";
struct WeatherExecutor { struct Executor {
mode: WeatherMode, mode: WeatherMode,
} }
@ -23,7 +23,7 @@ enum WeatherMode {
} }
#[async_trait] #[async_trait]
impl CommandExecutor for WeatherExecutor { impl CommandExecutor for Executor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -73,33 +73,33 @@ pub fn init_command_tree() -> CommandTree {
.then( .then(
literal("clear") literal("clear")
.then( .then(
argument(ARG_DURATION, TimeArgumentConsumer).execute(WeatherExecutor { argument(ARG_DURATION, TimeArgumentConsumer).execute(Executor {
mode: WeatherMode::Clear, mode: WeatherMode::Clear,
}), }),
) )
.execute(WeatherExecutor { .execute(Executor {
mode: WeatherMode::Clear, mode: WeatherMode::Clear,
}), }),
) )
.then( .then(
literal("rain") literal("rain")
.then( .then(
argument(ARG_DURATION, TimeArgumentConsumer).execute(WeatherExecutor { argument(ARG_DURATION, TimeArgumentConsumer).execute(Executor {
mode: WeatherMode::Rain, mode: WeatherMode::Rain,
}), }),
) )
.execute(WeatherExecutor { .execute(Executor {
mode: WeatherMode::Rain, mode: WeatherMode::Rain,
}), }),
) )
.then( .then(
literal("thunder") literal("thunder")
.then( .then(
argument(ARG_DURATION, TimeArgumentConsumer).execute(WeatherExecutor { argument(ARG_DURATION, TimeArgumentConsumer).execute(Executor {
mode: WeatherMode::Thunder, mode: WeatherMode::Thunder,
}), }),
) )
.execute(WeatherExecutor { .execute(Executor {
mode: WeatherMode::Thunder, mode: WeatherMode::Thunder,
}), }),
) )

View File

@ -48,10 +48,10 @@ fn warning_distance_consumer() -> BoundedNumArgumentConsumer<i32> {
BoundedNumArgumentConsumer::new().min(0).name("distance") BoundedNumArgumentConsumer::new().min(0).name("distance")
} }
struct WorldborderGetExecutor; struct GetExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for WorldborderGetExecutor { impl CommandExecutor for GetExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -76,10 +76,10 @@ impl CommandExecutor for WorldborderGetExecutor {
} }
} }
struct WorldborderSetExecutor; struct SetExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for WorldborderSetExecutor { impl CommandExecutor for SetExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -128,10 +128,10 @@ impl CommandExecutor for WorldborderSetExecutor {
} }
} }
struct WorldborderSetTimeExecutor; struct SetTimeExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for WorldborderSetTimeExecutor { impl CommandExecutor for SetTimeExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -213,10 +213,10 @@ impl CommandExecutor for WorldborderSetTimeExecutor {
} }
} }
struct WorldborderAddExecutor; struct AddExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for WorldborderAddExecutor { impl CommandExecutor for AddExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -267,10 +267,10 @@ impl CommandExecutor for WorldborderAddExecutor {
} }
} }
struct WorldborderAddTimeExecutor; struct AddTimeExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for WorldborderAddTimeExecutor { impl CommandExecutor for AddTimeExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -354,10 +354,10 @@ impl CommandExecutor for WorldborderAddTimeExecutor {
} }
} }
struct WorldborderCenterExecutor; struct CenterExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for WorldborderCenterExecutor { impl CommandExecutor for CenterExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -387,10 +387,10 @@ impl CommandExecutor for WorldborderCenterExecutor {
} }
} }
struct WorldborderDamageAmountExecutor; struct DamageAmountExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for WorldborderDamageAmountExecutor { impl CommandExecutor for DamageAmountExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -439,10 +439,10 @@ impl CommandExecutor for WorldborderDamageAmountExecutor {
} }
} }
struct WorldborderDamageBufferExecutor; struct DamageBufferExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for WorldborderDamageBufferExecutor { impl CommandExecutor for DamageBufferExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -491,10 +491,10 @@ impl CommandExecutor for WorldborderDamageBufferExecutor {
} }
} }
struct WorldborderWarningDistanceExecutor; struct WarningDistanceExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for WorldborderWarningDistanceExecutor { impl CommandExecutor for WarningDistanceExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -542,10 +542,10 @@ impl CommandExecutor for WorldborderWarningDistanceExecutor {
} }
} }
struct WorldborderWarningTimeExecutor; struct WarningTimeExecutor;
#[async_trait] #[async_trait]
impl CommandExecutor for WorldborderWarningTimeExecutor { impl CommandExecutor for WarningTimeExecutor {
async fn execute<'a>( async fn execute<'a>(
&self, &self,
sender: &mut CommandSender<'a>, sender: &mut CommandSender<'a>,
@ -598,38 +598,32 @@ pub fn init_command_tree() -> CommandTree {
.then( .then(
literal("add").then( literal("add").then(
argument_default_name(distance_consumer()) argument_default_name(distance_consumer())
.execute(WorldborderAddExecutor) .execute(AddExecutor)
.then( .then(argument_default_name(time_consumer()).execute(AddTimeExecutor)),
argument_default_name(time_consumer()).execute(WorldborderAddTimeExecutor),
),
), ),
) )
.then(literal("center").then( .then(
argument_default_name(Position2DArgumentConsumer).execute(WorldborderCenterExecutor), literal("center")
)) .then(argument_default_name(Position2DArgumentConsumer).execute(CenterExecutor)),
)
.then( .then(
literal("damage") literal("damage")
.then( .then(
literal("amount").then( literal("amount").then(
argument_default_name(damage_per_block_consumer()) argument_default_name(damage_per_block_consumer())
.execute(WorldborderDamageAmountExecutor), .execute(DamageAmountExecutor),
), ),
) )
.then( .then(literal("buffer").then(
literal("buffer").then( argument_default_name(damage_buffer_consumer()).execute(DamageBufferExecutor),
argument_default_name(damage_buffer_consumer()) )),
.execute(WorldborderDamageBufferExecutor),
),
),
) )
.then(literal("get").execute(WorldborderGetExecutor)) .then(literal("get").execute(GetExecutor))
.then( .then(
literal("set").then( literal("set").then(
argument_default_name(distance_consumer()) argument_default_name(distance_consumer())
.execute(WorldborderSetExecutor) .execute(SetExecutor)
.then( .then(argument_default_name(time_consumer()).execute(SetTimeExecutor)),
argument_default_name(time_consumer()).execute(WorldborderSetTimeExecutor),
),
), ),
) )
.then( .then(
@ -637,11 +631,12 @@ pub fn init_command_tree() -> CommandTree {
.then( .then(
literal("distance").then( literal("distance").then(
argument_default_name(warning_distance_consumer()) argument_default_name(warning_distance_consumer())
.execute(WorldborderWarningDistanceExecutor), .execute(WarningDistanceExecutor),
), ),
) )
.then(literal("time").then( .then(
argument_default_name(time_consumer()).execute(WorldborderWarningTimeExecutor), literal("time")
)), .then(argument_default_name(time_consumer()).execute(WarningTimeExecutor)),
),
) )
} }

View File

@ -349,7 +349,7 @@ impl CommandDispatcher {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::command::{default_dispatcher, tree::CommandTree}; use crate::command::{commands::default_dispatcher, tree::CommandTree};
use pumpkin_util::permission::PermissionLvl; use pumpkin_util::permission::PermissionLvl;
#[test] #[test]
fn test_dynamic_command() { fn test_dynamic_command() {

View File

@ -1,19 +1,12 @@
use std::fmt; use std::fmt;
use std::sync::Arc; 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::entity::player::Player;
use crate::server::Server; use crate::server::Server;
use crate::world::World; use crate::world::World;
use args::ConsumedArgs; use args::ConsumedArgs;
use async_trait::async_trait; 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 dispatcher::CommandError;
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_util::permission::PermissionLvl; use pumpkin_util::permission::PermissionLvl;
@ -21,7 +14,7 @@ use pumpkin_util::text::TextComponent;
pub mod args; pub mod args;
pub mod client_suggestions; pub mod client_suggestions;
mod commands; pub mod commands;
pub mod dispatcher; pub mod dispatcher;
pub mod tree; 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] #[async_trait]
pub trait CommandExecutor: Sync { pub trait CommandExecutor: Sync {
async fn execute<'a>( async fn execute<'a>(

View File

@ -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,
}

View File

@ -1,7 +1,8 @@
use std::sync::atomic::AtomicI32; use std::{collections::HashMap, sync::atomic::AtomicI32};
use async_trait::async_trait; use async_trait::async_trait;
use crossbeam::atomic::AtomicCell; use crossbeam::atomic::AtomicCell;
use pumpkin_data::entity::EffectType;
use pumpkin_data::{damage::DamageType, sound::Sound}; use pumpkin_data::{damage::DamageType, sound::Sound};
use pumpkin_nbt::tag::NbtTag; use pumpkin_nbt::tag::NbtTag;
use pumpkin_protocol::{ use pumpkin_protocol::{
@ -12,8 +13,9 @@ use pumpkin_protocol::{
}; };
use pumpkin_util::math::vector3::Vector3; use pumpkin_util::math::vector3::Vector3;
use pumpkin_world::item::ItemStack; 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. /// Represents a living entity within the game world.
/// ///
@ -31,9 +33,10 @@ pub struct LivingEntity {
pub health: AtomicCell<f32>, pub health: AtomicCell<f32>,
/// The distance the entity has been falling /// The distance the entity has been falling
pub fall_distance: AtomicCell<f32>, pub fall_distance: AtomicCell<f32>,
pub active_effects: Mutex<HashMap<EffectType, Effect>>,
} }
impl LivingEntity { impl LivingEntity {
pub const fn new(entity: Entity) -> Self { pub fn new(entity: Entity) -> Self {
Self { Self {
entity, entity,
last_pos: AtomicCell::new(Vector3::new(0.0, 0.0, 0.0)), 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), last_damage_taken: AtomicCell::new(0.0),
health: AtomicCell::new(20.0), health: AtomicCell::new(20.0),
fall_distance: AtomicCell::new(0.0), fall_distance: AtomicCell::new(0.0),
active_effects: Mutex::new(HashMap::new()),
} }
} }
@ -131,6 +135,22 @@ impl LivingEntity {
true 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<Effect> {
let effects = self.active_effects.lock().await;
effects.get(&effect).cloned()
}
pub async fn damage(&self, amount: f32, damage_type: DamageType) -> bool { pub async fn damage(&self, amount: f32, damage_type: DamageType) -> bool {
self.damage_with_context(amount, damage_type, None, None, None) self.damage_with_context(amount, damage_type, None, None, None)
.await .await

View File

@ -32,6 +32,7 @@ use tokio::sync::RwLock;
use crate::world::World; use crate::world::World;
pub mod ai; pub mod ai;
pub mod effect;
pub mod hunger; pub mod hunger;
pub mod item; pub mod item;
pub mod living; pub mod living;

View File

@ -13,7 +13,7 @@ use crossbeam::atomic::AtomicCell;
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG}; use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
use pumpkin_data::{ use pumpkin_data::{
damage::DamageType, damage::DamageType,
entity::EntityType, entity::{EffectType, EntityType},
item::Operation, item::Operation,
particle::Particle, particle::Particle,
sound::{Sound, SoundCategory}, sound::{Sound, SoundCategory},
@ -27,7 +27,8 @@ use pumpkin_protocol::{
CAcknowledgeBlockChange, CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus, CAcknowledgeBlockChange, CActionBar, CCombatDeath, CDisguisedChatMessage, CEntityStatus,
CGameEvent, CHurtAnimation, CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities, CGameEvent, CHurtAnimation, CKeepAlive, CParticle, CPlayDisconnect, CPlayerAbilities,
CPlayerInfoUpdate, CPlayerPosition, CRespawn, CSetExperience, CSetHealth, CSubtitle, CPlayerInfoUpdate, CPlayerPosition, CRespawn, CSetExperience, CSetHealth, CSubtitle,
CSystemChatMessage, CTitleText, CUnloadChunk, GameEvent, MetaDataType, PlayerAction, CSystemChatMessage, CTitleText, CUnloadChunk, CUpdateMobEffect, GameEvent, MetaDataType,
PlayerAction,
}, },
server::play::{ server::play::{
SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd, SChatCommand, SChatMessage, SClientCommand, SClientInformationPlay, SClientTickEnd,
@ -66,6 +67,7 @@ use tokio::sync::{Mutex, Notify, RwLock};
use super::{ use super::{
Entity, EntityBase, EntityId, NBTStorage, Entity, EntityBase, EntityId, NBTStorage,
combat::{self, AttackType, player_attack_sound}, combat::{self, AttackType, player_attack_sound},
effect::Effect,
hunger::HungerManager, hunger::HungerManager,
item::ItemEntity, item::ItemEntity,
}; };
@ -958,13 +960,52 @@ impl Player {
.await .await
.get_mining_speed(block_name) .get_mining_speed(block_name)
.await; .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) { if !self.living_entity.entity.on_ground.load(Ordering::Relaxed) {
speed /= 5.0; speed /= 5.0;
} }
speed 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( pub async fn send_message(
&self, &self,
message: &TextComponent, message: &TextComponent,
@ -1049,6 +1090,34 @@ impl Player {
self.set_experience(new_level, progress, points).await; 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 /// Add experience levels to the player
pub async fn add_experience_levels(&self, added_levels: i32) { pub async fn add_experience_levels(&self, added_levels: i32) {
let current_level = self.experience_level.load(Ordering::Relaxed); let current_level = self.experience_level.load(Ordering::Relaxed);

View File

@ -31,15 +31,13 @@ use tokio::sync::{Mutex, RwLock};
use crate::block::default_block_properties_manager; use crate::block::default_block_properties_manager;
use crate::block::properties::BlockPropertiesManager; use crate::block::properties::BlockPropertiesManager;
use crate::block::registry::BlockRegistry; use crate::block::registry::BlockRegistry;
use crate::command::commands::default_dispatcher;
use crate::entity::{Entity, EntityId}; use crate::entity::{Entity, EntityId};
use crate::item::registry::ItemRegistry; use crate::item::registry::ItemRegistry;
use crate::net::EncryptionError; use crate::net::EncryptionError;
use crate::world::custom_bossbar::CustomBossbars; use crate::world::custom_bossbar::CustomBossbars;
use crate::{ use crate::{
command::{default_dispatcher, dispatcher::CommandDispatcher}, command::dispatcher::CommandDispatcher, entity::player::Player, net::Client, world::World,
entity::player::Player,
net::Client,
world::World,
}; };
mod connection_cache; mod connection_cache;

View File

@ -101,8 +101,8 @@ impl PumpkinError for GetBlockError {
/// **Key Responsibilities:** /// **Key Responsibilities:**
/// ///
/// - Manages the `Level` instance for handling chunk-related operations. /// - Manages the `Level` instance for handling chunk-related operations.
/// - Stores and tracks active `Player` entities within the world. /// - Active players and entities.
/// - Provides a central hub for interacting with the world's entities and environment. /// - World-related systems like the scoreboard, world border, weather, and time.
pub struct World { pub struct World {
/// The underlying level, responsible for chunk management and terrain generation. /// The underlying level, responsible for chunk management and terrain generation.
pub level: Arc<Level>, pub level: Arc<Level>,
@ -121,7 +121,6 @@ pub struct World {
pub dimension_type: DimensionType, pub dimension_type: DimensionType,
/// The world's weather, including rain and thunder levels /// The world's weather, including rain and thunder levels
pub weather: Mutex<Weather>, pub weather: Mutex<Weather>,
// TODO: entities
} }
impl World { impl World {