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

View File

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

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_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 {

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 {
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"));

View File

@@ -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::*;

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
flate2 = "1.0"
lz4 = "1.28"
zstd = "0.13.2"
zstd = "0.13.3"
file-guard = "0.2"

View File

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

View File

@@ -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<Self::Data, CommandError> {
fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
match args.get(name) {
Some(Arg::DamageType(data)) => Ok(data),
_ => Err(CommandError::InvalidConsumption(Some(name.to_string()))),

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_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<Self::Data, CommandError> {
fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
match args.get(name) {
Some(Arg::Item(name)) => Item::from_name(&name.replace("minecraft:", "")).map_or_else(
|| {

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::{
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<Self::Data, CommandError> {
fn find_arg(args: &'a ConsumedArgs, name: &str) -> Result<Self::Data, CommandError> {
match args.get(name) {
Some(Arg::Particle(data)) => Ok(data),
_ => Err(CommandError::InvalidConsumption(Some(name.to_string()))),

View File

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

View File

@@ -36,10 +36,10 @@ async fn parse_ip(target: &str, server: &Server) -> Option<IpAddr> {
})
}
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)),
)
}

View File

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

View File

@@ -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<i32> {
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)),
),
),
),

View File

@@ -73,10 +73,10 @@ fn clear_command_text_output(item_count: usize, targets: &[Arc<Player>]) -> 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))
}

View File

@@ -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<f32> {
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)),
),
),
),

View File

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

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,
}
struct ExperienceExecutor {
struct Executor {
mode: Mode,
exp_type: Option<ExpType>,
}
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),
})),

View File

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

View File

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

View File

@@ -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<i32> {
.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)),
),
)
}

View File

@@ -25,10 +25,10 @@ fn page_number_consumer() -> BoundedNumArgumentConsumer<i32> {
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)
}

View File

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

View File

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

View File

@@ -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<Arc<Player>>) -> String {
}
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";
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))
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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::<f32>::new().min(0.0),
)
.execute(ParticleExecutor)
.execute(Executor)
.then(
argument(
ARG_COUNT,
BoundedNumArgumentConsumer::<i32>::new().min(0),
)
.execute(ParticleExecutor),
.execute(Executor),
),
),
),

View File

@@ -57,10 +57,10 @@ fn min_volume_consumer() -> BoundedNumArgumentConsumer<f32> {
.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),
)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -29,10 +29,10 @@ fn port_consumer() -> BoundedNumArgumentConsumer<i32> {
.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),
),
),
)

View File

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

View File

@@ -48,10 +48,10 @@ fn warning_distance_consumer() -> BoundedNumArgumentConsumer<i32> {
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)),
),
)
}

View File

@@ -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() {

View File

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

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 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<f32>,
/// The distance the entity has been falling
pub fall_distance: AtomicCell<f32>,
pub active_effects: Mutex<HashMap<EffectType, Effect>>,
}
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<Effect> {
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

View File

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

View File

@@ -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);

View File

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

View File

@@ -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<Level>,
@@ -121,7 +121,6 @@ pub struct World {
pub dimension_type: DimensionType,
/// The world's weather, including rain and thunder levels
pub weather: Mutex<Weather>,
// TODO: entities
}
impl World {