diff --git a/Cargo.lock b/Cargo.lock index d354cf574..1cf09ca52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -493,9 +493,9 @@ checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e" [[package]] name = "convert_case" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" dependencies = [ "unicode-segmentation", ] @@ -812,22 +812,23 @@ dependencies = [ [[package]] name = "derive_more" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" +checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" dependencies = [ "derive_more-impl", ] [[package]] name = "derive_more-impl" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" +checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" dependencies = [ "convert_case", "proc-macro2", "quote", + "rustc_version", "syn", ] @@ -1373,9 +1374,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" dependencies = [ "bytes", "futures-channel", @@ -1587,9 +1588,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.177" +version = "0.2.178" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" [[package]] name = "libloading" @@ -1636,9 +1637,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lru" @@ -2132,9 +2133,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.14.2" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "101fec8d036f8d9d4a1e8ebf90d566d1d798f3b1aa379d2576a54a0d9acea5bd" +checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" dependencies = [ "bytes", "prost-derive", @@ -2142,9 +2143,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.14.2" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d93e596a829ebe00afa41c3a056e6308d6b8a4c7d869edf184e2c91b1ba564" +checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" dependencies = [ "anyhow", "itertools 0.14.0", @@ -2155,9 +2156,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.14.2" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d7b7346e150de32340ae3390b8b3ffa37ad93ec31fb5dad86afe817619e4e7" +checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" dependencies = [ "prost", ] @@ -2555,6 +2556,15 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + [[package]] name = "rustix" version = "1.1.2" @@ -2668,6 +2678,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" version = "1.0.228" @@ -3441,14 +3457,14 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.18.1" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a" dependencies = [ "getrandom 0.3.4", "js-sys", "md-5", - "serde", + "serde_core", "wasm-bindgen", ] diff --git a/Cargo.toml b/Cargo.toml index 4f84896b4..2a3d2006d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,7 +43,7 @@ futures = { version = "0.3", default-features = false, features = ["executor"] } rayon = "1.11" crossbeam = "0.8" -uuid = { version = "1.18", features = ["serde", "v3", "v4"] } +uuid = { version = "1.19", features = ["serde", "v3", "v4"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/pumpkin/src/command/args/block.rs b/pumpkin/src/command/args/block.rs index 05626b36b..07408a6e5 100644 --- a/pumpkin/src/command/args/block.rs +++ b/pumpkin/src/command/args/block.rs @@ -1,9 +1,9 @@ -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_data::tag::{RegistryKey, get_tag_ids}; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::text::TextComponent; +use crate::command::args::ConsumeResult; use crate::{command::dispatcher::CommandError, server::Server}; use super::{ @@ -26,25 +26,18 @@ impl GetClientSideArgParser for BlockArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for BlockArgumentConsumer { - async fn consume<'a>( + fn consume<'a>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; - Some(Arg::Block(s)) - } - - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + ) -> ConsumeResult<'a> { + let block = args.pop(); + match block { + Some(s) => Box::pin(async move { Some(Arg::Block(s)) }), + None => Box::pin(async move { None }), + } } } @@ -101,25 +94,18 @@ impl GetClientSideArgParser for BlockPredicateArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for BlockPredicateArgumentConsumer { - async fn consume<'a>( + fn consume<'a>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; - Some(Arg::BlockPredicate(s)) - } - - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + ) -> ConsumeResult<'a> { + let block = args.pop(); + match block { + Some(s) => Box::pin(async move { Some(Arg::BlockPredicate(s)) }), + None => Box::pin(async move { None }), + } } } diff --git a/pumpkin/src/command/args/bool.rs b/pumpkin/src/command/args/bool.rs index 68d2ce140..8a22343e0 100644 --- a/pumpkin/src/command/args/bool.rs +++ b/pumpkin/src/command/args/bool.rs @@ -1,10 +1,9 @@ use crate::command::CommandSender; -use crate::command::args::{Arg, ArgumentConsumer, FindArg, GetClientSideArgParser}; +use crate::command::args::{Arg, ArgumentConsumer, ConsumeResult, FindArg, GetClientSideArgParser}; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::server::Server; -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; pub struct BoolArgConsumer; @@ -18,30 +17,25 @@ impl GetClientSideArgParser for BoolArgConsumer { } } -#[async_trait] impl ArgumentConsumer for BoolArgConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - match s { - "false" => Some(Arg::Bool(false)), - "true" => Some(Arg::Bool(true)), - _ => None, - } - } + let result: Option> = s_opt.map_or_else( + || None, + |s| match s { + "false" => Some(Arg::Bool(false)), + "true" => Some(Arg::Bool(true)), + _ => None, + }, + ); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/args/bossbar_color.rs b/pumpkin/src/command/args/bossbar_color.rs index 94e09ae9e..d37fa13c8 100644 --- a/pumpkin/src/command/args/bossbar_color.rs +++ b/pumpkin/src/command/args/bossbar_color.rs @@ -1,12 +1,12 @@ use crate::command::CommandSender; use crate::command::args::{ - Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, + Arg, ArgumentConsumer, ConsumeResult, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, + SuggestResult, }; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::server::Server; use crate::world::bossbar::BossbarColor; -use async_trait::async_trait; use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; pub struct BossbarColorArgumentConsumer; @@ -22,42 +22,50 @@ impl GetClientSideArgParser for BossbarColorArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for BossbarColorArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - let color = match s { - "blue" => Some(BossbarColor::Blue), - "green" => Some(BossbarColor::Green), - "pink" => Some(BossbarColor::Pink), - "purple" => Some(BossbarColor::Purple), - "red" => Some(BossbarColor::Red), - "white" => Some(BossbarColor::White), - "yellow" => Some(BossbarColor::Yellow), - _ => None, - }; + let result: Option> = s_opt.map_or_else( + || None, + |s| { + let color = match s { + "blue" => Some(BossbarColor::Blue), + "green" => Some(BossbarColor::Green), + "pink" => Some(BossbarColor::Pink), + "purple" => Some(BossbarColor::Purple), + "red" => Some(BossbarColor::Red), + "white" => Some(BossbarColor::White), + "yellow" => Some(BossbarColor::Yellow), + _ => None, + }; - color.map(Arg::BossbarColor) + color.map(Arg::BossbarColor) + }, + ); + + Box::pin(async move { result }) } - async fn suggest<'a>( + fn suggest<'a>( &'a self, _sender: &CommandSender, _server: &'a Server, _input: &'a str, - ) -> Result>, CommandError> { - let colors = ["blue", "green", "pink", "purple", "red", "white", "yellow"]; - let suggestions: Vec = colors - .iter() - .map(|color| CommandSuggestion::new((*color).to_string(), None)) - .collect(); - Ok(Some(suggestions)) + ) -> SuggestResult<'a> { + Box::pin(async move { + let colors = ["blue", "green", "pink", "purple", "red", "white", "yellow"]; + let suggestions: Vec = colors + .iter() + .map(|color| CommandSuggestion::new((*color).to_string(), None)) + .collect(); + Ok(Some(suggestions)) + }) } } diff --git a/pumpkin/src/command/args/bossbar_style.rs b/pumpkin/src/command/args/bossbar_style.rs index 7e926d6b9..f6797e932 100644 --- a/pumpkin/src/command/args/bossbar_style.rs +++ b/pumpkin/src/command/args/bossbar_style.rs @@ -1,12 +1,12 @@ use crate::command::CommandSender; use crate::command::args::{ - Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, + Arg, ArgumentConsumer, ConsumeResult, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, + SuggestResult, }; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::server::Server; use crate::world::bossbar::BossbarDivisions; -use async_trait::async_trait; use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; pub struct BossbarStyleArgumentConsumer; @@ -22,46 +22,54 @@ impl GetClientSideArgParser for BossbarStyleArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for BossbarStyleArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - let style = match s { - "notched_10" => Some(BossbarDivisions::Notches10), - "notched_12" => Some(BossbarDivisions::Notches12), - "notched_20" => Some(BossbarDivisions::Notches20), - "notched_6" => Some(BossbarDivisions::Notches6), - "progress" => Some(BossbarDivisions::NoDivision), - _ => None, - }; + let result: Option> = s_opt.map_or_else( + || None, + |s| { + let style = match s { + "notched_10" => Some(BossbarDivisions::Notches10), + "notched_12" => Some(BossbarDivisions::Notches12), + "notched_20" => Some(BossbarDivisions::Notches20), + "notched_6" => Some(BossbarDivisions::Notches6), + "progress" => Some(BossbarDivisions::NoDivision), + _ => None, + }; - style.map(Arg::BossbarStyle) + style.map(Arg::BossbarStyle) + }, + ); + + Box::pin(async move { result }) } - async fn suggest<'a>( + fn suggest<'a>( &'a self, _sender: &CommandSender, _server: &'a Server, _input: &'a str, - ) -> Result>, CommandError> { - let styles = [ - "notched_10", - "notched_12", - "notched_20", - "notched_6", - "progress", - ]; - let suggestions: Vec = styles - .iter() - .map(|style| CommandSuggestion::new((*style).to_string(), None)) - .collect(); - Ok(Some(suggestions)) + ) -> SuggestResult<'a> { + Box::pin(async move { + let styles = [ + "notched_10", + "notched_12", + "notched_20", + "notched_6", + "progress", + ]; + let suggestions: Vec = styles + .iter() + .map(|style| CommandSuggestion::new((*style).to_string(), None)) + .collect(); + Ok(Some(suggestions)) + }) } } diff --git a/pumpkin/src/command/args/bounded_num.rs b/pumpkin/src/command/args/bounded_num.rs index 2a0f8139f..7cbd2ddb2 100644 --- a/pumpkin/src/command/args/bounded_num.rs +++ b/pumpkin/src/command/args/bounded_num.rs @@ -2,11 +2,11 @@ use core::f64; use std::fmt::{Display, Formatter}; use std::str::FromStr; -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion}; +use pumpkin_protocol::java::client::play::ArgumentType; use pumpkin_util::text::TextComponent; use crate::command::CommandSender; +use crate::command::args::ConsumeResult; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::server::Server; @@ -21,47 +21,44 @@ pub struct BoundedNumArgumentConsumer { name: Option<&'static str>, } -#[async_trait] impl ArgumentConsumer for BoundedNumArgumentConsumer where Self: GetClientSideArgParser, { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _src: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let x = args.pop()?.parse::().ok()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + // 1. Perform the synchronous mutable operation (pop) outside the Future. + let s_opt: Option<&'a str> = args.pop(); - if let Some(max) = self.max_inclusive - && x > max - { - return Some(Arg::Num(Err(NotInBounds::UpperBound( - x.to_number(), - max.to_number(), - )))); - } + // 2. Process the entire argument and check bounds synchronously. + let result: Option> = s_opt + // Replace args.pop()?.parse::().ok()? + .and_then(|s| s.parse::().ok()) + .map(|x| { + // Check Upper Bound (max_inclusive) + if let Some(max) = self.max_inclusive + && x > max + { + return Arg::Num(Err(NotInBounds::UpperBound(x.to_number(), max.to_number()))); + } - if let Some(min) = self.min_inclusive - && x < min - { - return Some(Arg::Num(Err(NotInBounds::LowerBound( - x.to_number(), - min.to_number(), - )))); - } + // Check Lower Bound (min_inclusive) + if let Some(min) = self.min_inclusive + && x < min + { + return Arg::Num(Err(NotInBounds::LowerBound(x.to_number(), min.to_number()))); + } - Some(Arg::Num(Ok(x.to_number()))) - } + // Success case + Arg::Num(Ok(x.to_number())) + }); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + // 3. Return a Future that immediately resolves to the calculated result. + Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/args/command.rs b/pumpkin/src/command/args/command.rs index 587f65e72..755e26a89 100644 --- a/pumpkin/src/command/args/command.rs +++ b/pumpkin/src/command/args/command.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_protocol::java::client::play::{ ArgumentType, CommandSuggestion, StringProtoArgBehavior, SuggestionProviders, }; @@ -6,7 +5,7 @@ use pumpkin_protocol::java::client::play::{ use crate::{ command::{ CommandSender, - args::SplitSingleWhitespaceIncludingEmptyParts, + args::{ConsumeResult, SplitSingleWhitespaceIncludingEmptyParts, SuggestResult}, dispatcher::CommandError, tree::{CommandTree, RawArgs}, }, @@ -27,40 +26,49 @@ impl GetClientSideArgParser for CommandTreeArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for CommandTreeArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - let dispatcher = server.command_dispatcher.read().await; - dispatcher - .get_tree(s) - .map_or_else(|_| None, |tree| Some(Arg::CommandTree(tree.clone()))) + let Some(s) = s_opt else { + return Box::pin(async move { None }); + }; + + Box::pin(async move { + let dispatcher = server.command_dispatcher.read().await; + + dispatcher + .get_tree(s) + .ok() + .map(|tree| Arg::CommandTree(tree.clone())) + }) } - async fn suggest<'a>( + fn suggest<'a>( &'a self, _sender: &CommandSender, server: &'a Server, input: &'a str, - ) -> Result>, CommandError> { - let Some(input) = input.split_single_whitespace_including_empty_parts().last() else { - return Ok(None); - }; + ) -> SuggestResult<'a> { + Box::pin(async move { + let Some(input) = input.split_single_whitespace_including_empty_parts().last() else { + return Ok(None); + }; - let dispatcher = server.command_dispatcher.read().await; - let suggestions = dispatcher - .commands - .keys() - .filter(|suggestion| suggestion.starts_with(input)) - .map(|suggestion| CommandSuggestion::new(suggestion.clone(), None)) - .collect(); - Ok(Some(suggestions)) + let dispatcher = server.command_dispatcher.read().await; + let suggestions = dispatcher + .commands + .keys() + .filter(|suggestion| suggestion.starts_with(input)) + .map(|suggestion| CommandSuggestion::new(suggestion.clone(), None)) + .collect(); + Ok(Some(suggestions)) + }) } } diff --git a/pumpkin/src/command/args/difficulty.rs b/pumpkin/src/command/args/difficulty.rs index 299481835..6720c7a03 100644 --- a/pumpkin/src/command/args/difficulty.rs +++ b/pumpkin/src/command/args/difficulty.rs @@ -1,11 +1,15 @@ use std::str::FromStr; -use async_trait::async_trait; use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; use pumpkin_util::Difficulty; use crate::{ - command::{CommandSender, dispatcher::CommandError, tree::RawArgs}, + command::{ + CommandSender, + args::{ConsumeResult, SuggestResult}, + dispatcher::CommandError, + tree::RawArgs, + }, server::Server, }; @@ -25,32 +29,35 @@ impl GetClientSideArgParser for DifficultyArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for DifficultyArgumentConsumer { - async fn consume<'a>( + fn consume<'a>( &'a self, _sender: &CommandSender, _server: &'a Server, args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - Difficulty::from_str(s) - .map_or_else(|_| None, |difficulty| Some(Arg::Difficulty(difficulty))) + let result: Option> = + s_opt.and_then(|s| Difficulty::from_str(s).map(Arg::Difficulty).ok()); + + Box::pin(async move { result }) } - async fn suggest<'a>( + fn suggest<'a>( &'a self, _sender: &CommandSender, _server: &'a Server, _input: &'a str, - ) -> Result>, CommandError> { - let difficulties = ["easy", "normal", "hard", "peaceful"]; - let suggestions: Vec = difficulties - .iter() - .map(|difficulty| CommandSuggestion::new((*difficulty).to_string(), None)) - .collect(); - Ok(Some(suggestions)) + ) -> SuggestResult<'a> { + Box::pin(async move { + let difficulties = ["easy", "normal", "hard", "peaceful"]; + let suggestions: Vec = difficulties + .iter() + .map(|difficulty| CommandSuggestion::new((*difficulty).to_string(), None)) + .collect(); + Ok(Some(suggestions)) + }) } } diff --git a/pumpkin/src/command/args/entities.rs b/pumpkin/src/command/args/entities.rs index 164851b9c..d72a0da4e 100644 --- a/pumpkin/src/command/args/entities.rs +++ b/pumpkin/src/command/args/entities.rs @@ -2,14 +2,14 @@ use std::str::FromStr; use std::sync::Arc; use crate::command::CommandSender; +use crate::command::args::ConsumeResult; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::entity::EntityBase; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::entity::EntityType; use pumpkin_nbt::compound::NbtCompound; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::GameMode; use uuid::Uuid; @@ -250,35 +250,34 @@ impl GetClientSideArgParser for EntitiesArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for EntitiesArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - src: &CommandSender, + sender: &'a CommandSender, server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); + + let Some(s) = s_opt else { + return Box::pin(async move { None }); + }; + let entity_selector = match s.parse::() { Ok(selector) => selector, Err(e) => { log::debug!("Failed to parse target selector '{s}': {e}"); - return None; + return Box::pin(async move { None }); // Return a Future resolving to None } }; - // todo: command context - let entities = server.select_entities(&entity_selector, Some(src)).await; - Some(Arg::Entities(entities)) - } + Box::pin(async move { + // todo: command context + // This is the required asynchronous operation. + let entities = server.select_entities(&entity_selector, Some(sender)).await; - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Some(Arg::Entities(entities)) + }) } } diff --git a/pumpkin/src/command/args/entity.rs b/pumpkin/src/command/args/entity.rs index 2e3480f54..bebac0fe6 100644 --- a/pumpkin/src/command/args/entity.rs +++ b/pumpkin/src/command/args/entity.rs @@ -1,13 +1,13 @@ use std::sync::Arc; use crate::command::CommandSender; +use crate::command::args::ConsumeResult; use crate::command::args::entities::TargetSelector; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::entity::EntityBase; use crate::server::Server; -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use super::super::args::ArgumentConsumer; use super::{Arg, DefaultNameArgConsumer, FindArg, GetClientSideArgParser}; @@ -32,41 +32,38 @@ impl GetClientSideArgParser for EntityArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for EntityArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - src: &CommandSender, + sender: &'a CommandSender, server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); + + let Some(s) = s_opt else { + return Box::pin(async move { None }); + }; let entity_selector = match s.parse::() { Ok(selector) => selector, Err(e) => { log::debug!("Failed to parse target selector '{s}': {e}"); - return None; + return Box::pin(async move { None }); } }; + if entity_selector.get_limit() > 1 { log::debug!("Target selector '{s}' has limit > 1, expected single entity"); - return None; + return Box::pin(async move { None }); } - // todo: command context - let entities = server.select_entities(&entity_selector, Some(src)).await; - // Take first - entities.into_iter().next().map(Arg::Entity) - } + Box::pin(async move { + // todo: command context + let entities = server.select_entities(&entity_selector, Some(sender)).await; - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + entities.into_iter().next().map(Arg::Entity) + }) } } diff --git a/pumpkin/src/command/args/gamemode.rs b/pumpkin/src/command/args/gamemode.rs index 56335a30e..33321d4bc 100644 --- a/pumpkin/src/command/args/gamemode.rs +++ b/pumpkin/src/command/args/gamemode.rs @@ -1,11 +1,10 @@ use std::str::FromStr; -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::GameMode; use crate::{ - command::{CommandSender, dispatcher::CommandError, tree::RawArgs}, + command::{CommandSender, args::ConsumeResult, dispatcher::CommandError, tree::RawArgs}, server::Server, }; @@ -23,32 +22,26 @@ impl GetClientSideArgParser for GamemodeArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for GamemodeArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - if let Ok(id) = s.parse::() - && let Ok(gamemode) = GameMode::try_from(id) - { - return Some(Arg::GameMode(gamemode)); - } + let result: Option> = s_opt.and_then(|s| { + if let Ok(id) = s.parse::() + && let Ok(gamemode) = GameMode::try_from(id) + { + return Some(Arg::GameMode(gamemode)); + } - GameMode::from_str(s).map_or_else(|_| None, |gamemode| Some(Arg::GameMode(gamemode))) - } + GameMode::from_str(s).map(Arg::GameMode).ok() // Convert Result to Option + }); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/args/message.rs b/pumpkin/src/command/args/message.rs index 236cf0ece..d6db3316f 100644 --- a/pumpkin/src/command/args/message.rs +++ b/pumpkin/src/command/args/message.rs @@ -1,9 +1,11 @@ -use async_trait::async_trait; use pumpkin_protocol::java::client::play::{ - ArgumentType, CommandSuggestion, StringProtoArgBehavior, SuggestionProviders, + ArgumentType, StringProtoArgBehavior, SuggestionProviders, }; -use crate::{command::dispatcher::CommandError, server::Server}; +use crate::{ + command::{args::ConsumeResult, dispatcher::CommandError}, + server::Server, +}; use super::{ super::{ @@ -26,31 +28,26 @@ impl GetClientSideArgParser for MsgArgConsumer { } } -#[async_trait] impl ArgumentConsumer for MsgArgConsumer { - async fn consume<'a>( + fn consume<'a>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, args: &mut RawArgs<'a>, - ) -> Option> { - let mut msg = args.pop()?.to_string(); + ) -> ConsumeResult<'a> { + let first_word_opt = args.pop(); + + let mut msg = match first_word_opt { + Some(word) => word.to_string(), + None => return Box::pin(async move { None }), + }; while let Some(word) = args.pop() { msg.push(' '); msg.push_str(word); } - Some(Arg::Msg(msg)) - } - - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { Some(Arg::Msg(msg)) }) } } diff --git a/pumpkin/src/command/args/mod.rs b/pumpkin/src/command/args/mod.rs index cef717620..d75ecb240 100644 --- a/pumpkin/src/command/args/mod.rs +++ b/pumpkin/src/command/args/mod.rs @@ -1,6 +1,6 @@ +use std::pin::Pin; use std::{collections::HashMap, hash::Hash, sync::Arc}; -use async_trait::async_trait; use bounded_num::{NotInBounds, Number}; use pumpkin_data::Enchantment; use pumpkin_data::damage::DamageType; @@ -51,24 +51,30 @@ pub mod textcomponent; pub mod time; /// see [`crate::commands::tree::builder::argument`] -#[async_trait] -pub trait ArgumentConsumer: Sync + GetClientSideArgParser { - async fn consume<'a>( +pub type ConsumeResult<'a> = Pin>> + Send + 'a>>; + +pub type SuggestResult<'a> = + Pin>, CommandError>> + Send + 'a>>; + +pub trait ArgumentConsumer: Sync + Send + GetClientSideArgParser { + fn consume<'a>( &'a self, - sender: &CommandSender, + sender: &'a CommandSender, server: &'a Server, args: &mut RawArgs<'a>, - ) -> Option; + ) -> ConsumeResult<'a>; /// Used for tab completion (but only if argument suggestion type is "`minecraft:ask_server`"!). /// /// NOTE: This is called after this consumer's [`ArgumentConsumer::consume`] method returned None, so if args is used here, make sure [`ArgumentConsumer::consume`] never returns None after mutating args. - async fn suggest<'a>( + fn suggest<'a>( &'a self, - sender: &CommandSender, - server: &'a Server, - input: &'a str, - ) -> Result>, CommandError>; + _sender: &CommandSender, + _server: &'a Server, + _input: &'a str, + ) -> SuggestResult<'a> { + Box::pin(async move { Ok(None) }) + } } pub trait GetClientSideArgParser { diff --git a/pumpkin/src/command/args/players.rs b/pumpkin/src/command/args/players.rs index eef3bffa2..b9f47f281 100644 --- a/pumpkin/src/command/args/players.rs +++ b/pumpkin/src/command/args/players.rs @@ -1,9 +1,9 @@ use std::sync::Arc; -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use crate::command::CommandSender; +use crate::command::args::ConsumeResult; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::entity::player::Player; @@ -28,45 +28,49 @@ impl GetClientSideArgParser for PlayersArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for PlayersArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - src: &CommandSender, + sender: &'a CommandSender, server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - let players = match s { - "@s" => match src { + let Some(s) = s_opt else { + return Box::pin(async move { None }); + }; + + let sync_result: Option>> = match s { + "@s" => match sender { CommandSender::Player(p) => Some(vec![p.clone()]), _ => None, }, #[allow(clippy::match_same_arms)] // todo: implement for non-players and remove this line - "@n" | "@p" => match src { + "@n" | "@p" => match sender { CommandSender::Player(p) => Some(vec![p.clone()]), - // todo: implement for non-players: how should this behave when sender is console/rcon? _ => None, }, - "@r" => { - (server.get_random_player().await).map_or_else(|| Some(vec![]), |p| Some(vec![p])) - } - "@a" | "@e" => Some(server.get_all_players().await), - name => server.get_player_by_name(name).await.map(|p| vec![p]), + _ => None, }; - players.map(Arg::Players) - } + if let Some(players) = sync_result { + return Box::pin(async move { Some(Arg::Players(players)) }); + } - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { + let players = match s { + "@r" => server + .get_random_player() + .await + .map_or_else(|| Some(vec![]), |p| Some(vec![p])), + "@a" | "@e" => Some(server.get_all_players().await), + name => server.get_player_by_name(name).await.map(|p| vec![p]), + }; + + players.map(Arg::Players) + }) } } diff --git a/pumpkin/src/command/args/position_2d.rs b/pumpkin/src/command/args/position_2d.rs index 1d13da85b..9f3c264fc 100644 --- a/pumpkin/src/command/args/position_2d.rs +++ b/pumpkin/src/command/args/position_2d.rs @@ -1,9 +1,9 @@ -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::math::vector2::Vector2; use pumpkin_util::math::vector3::Vector3; use crate::command::CommandSender; +use crate::command::args::ConsumeResult; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::server::Server; @@ -27,28 +27,25 @@ impl GetClientSideArgParser for Position2DArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for Position2DArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - src: &CommandSender, + sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let pos = MaybeRelativePosition2D::try_new(args.pop()?, args.pop()?)?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let x_str_opt = args.pop(); + let z_str_opt = args.pop(); - let vec2 = pos.try_to_absolute(src.position())?; + let (Some(x_str), Some(z_str)) = (x_str_opt, z_str_opt) else { + return Box::pin(async move { None }); + }; - Some(Arg::Pos2D(vec2)) - } + let result: Option> = MaybeRelativePosition2D::try_new(x_str, z_str) + .and_then(|pos| pos.try_to_absolute(sender.position())) + .map(Arg::Pos2D); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/args/position_3d.rs b/pumpkin/src/command/args/position_3d.rs index ef32840dd..411902629 100644 --- a/pumpkin/src/command/args/position_3d.rs +++ b/pumpkin/src/command/args/position_3d.rs @@ -1,8 +1,8 @@ -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::math::vector3::Vector3; use crate::command::CommandSender; +use crate::command::args::ConsumeResult; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::server::Server; @@ -24,28 +24,26 @@ impl GetClientSideArgParser for Position3DArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for Position3DArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - src: &CommandSender, + sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let pos = MaybeRelativePosition3D::try_new(args.pop()?, args.pop()?, args.pop()?)?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let x_str_opt = args.pop(); + let y_str_opt = args.pop(); + let z_str_opt = args.pop(); - let vec3 = pos.try_to_absolute(src.position())?; + let (Some(x_str), Some(y_str), Some(z_str)) = (x_str_opt, y_str_opt, z_str_opt) else { + return Box::pin(async move { None }); + }; - Some(Arg::Pos3D(vec3)) - } + let result: Option> = MaybeRelativePosition3D::try_new(x_str, y_str, z_str) + .and_then(|pos| pos.try_to_absolute(sender.position())) + .map(Arg::Pos3D); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/args/position_block.rs b/pumpkin/src/command/args/position_block.rs index 85cce6263..0a3944aae 100644 --- a/pumpkin/src/command/args/position_block.rs +++ b/pumpkin/src/command/args/position_block.rs @@ -1,9 +1,9 @@ -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; use crate::command::CommandSender; +use crate::command::args::ConsumeResult; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::server::Server; @@ -25,28 +25,26 @@ impl GetClientSideArgParser for BlockPosArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for BlockPosArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - src: &CommandSender, + sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let pos = MaybeRelativeBlockPos::try_new(args.pop()?, args.pop()?, args.pop()?)?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let x_str_opt = args.pop(); + let y_str_opt = args.pop(); + let z_str_opt = args.pop(); - let vec3 = pos.try_to_absolute(src.position())?; + let (Some(x_str), Some(y_str), Some(z_str)) = (x_str_opt, y_str_opt, z_str_opt) else { + return Box::pin(async move { None }); + }; - Some(Arg::BlockPos(vec3)) - } + let result: Option> = MaybeRelativeBlockPos::try_new(x_str, y_str, z_str) + .and_then(|pos| pos.try_to_absolute(sender.position())) + .map(Arg::BlockPos); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/args/resource/damage_type.rs b/pumpkin/src/command/args/resource/damage_type.rs index e16d310e6..48480dca4 100644 --- a/pumpkin/src/command/args/resource/damage_type.rs +++ b/pumpkin/src/command/args/resource/damage_type.rs @@ -1,11 +1,10 @@ -use async_trait::async_trait; use pumpkin_data::damage::DamageType; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use crate::command::{ CommandSender, args::{ - Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg, + Arg, ArgumentConsumer, ConsumeResult, ConsumedArgs, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, }, dispatcher::CommandError, @@ -27,30 +26,24 @@ impl GetClientSideArgParser for DamageTypeArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for DamageTypeArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let name = args.pop()?; - let name = name.strip_prefix("minecraft:").unwrap_or(name); + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let name_opt: Option<&'a str> = args.pop(); - // Create a static damage type first - let damage_type = DamageType::from_name(name)?; - // Find matching static damage type from values array - Some(Arg::DamageType(damage_type)) - } + let result: Option> = name_opt.map_or_else( + || None, + |name| { + let name = name.strip_prefix("minecraft:").unwrap_or(name); + DamageType::from_name(name).map(Arg::DamageType) + }, + ); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/args/resource/effect.rs b/pumpkin/src/command/args/resource/effect.rs index e1faa8e5c..48caeef39 100644 --- a/pumpkin/src/command/args/resource/effect.rs +++ b/pumpkin/src/command/args/resource/effect.rs @@ -1,11 +1,11 @@ use async_trait::async_trait; use pumpkin_data::effect::StatusEffect; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use crate::command::{ CommandSender, args::{ - Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg, + Arg, ArgumentConsumer, ConsumeResult, ConsumedArgs, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, }, dispatcher::CommandError, @@ -29,27 +29,22 @@ impl GetClientSideArgParser for EffectTypeArgumentConsumer { #[async_trait] impl ArgumentConsumer for EffectTypeArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let name = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let status_effect: Option<&'a str> = args.pop(); - // Create a static damage type first - let damage_type = StatusEffect::from_name(name.strip_prefix("minecraft:").unwrap_or(name))?; - // Find matching static damage type from values array - Some(Arg::Effect(damage_type)) - } - - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + match status_effect { + Some(name) => Box::pin(async move { + let status_effect = + StatusEffect::from_name(name.strip_prefix("minecraft:").unwrap_or(name))?; + Some(Arg::Effect(status_effect)) + }), + None => Box::pin(async move { None }), + } } } diff --git a/pumpkin/src/command/args/resource/enchantment.rs b/pumpkin/src/command/args/resource/enchantment.rs index b0e2af678..9dc0fa2a5 100644 --- a/pumpkin/src/command/args/resource/enchantment.rs +++ b/pumpkin/src/command/args/resource/enchantment.rs @@ -1,11 +1,11 @@ use async_trait::async_trait; use pumpkin_data::Enchantment; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use crate::command::{ CommandSender, args::{ - Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg, + Arg, ArgumentConsumer, ConsumeResult, ConsumedArgs, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, }, dispatcher::CommandError, @@ -29,27 +29,19 @@ impl GetClientSideArgParser for EnchantmentArgumentConsumer { #[async_trait] impl ArgumentConsumer for EnchantmentArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let name = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let name_opt: Option<&'a str> = args.pop(); - // Create a static damage type first - let enchantment = Enchantment::from_name(name)?; - // Find matching static damage type from values array - Some(Arg::Enchantment(enchantment)) - } - - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + let result: Option> = name_opt.map_or_else( + || None, + |name| Enchantment::from_name(name).map(Arg::Enchantment), + ); + Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/args/resource/item.rs b/pumpkin/src/command/args/resource/item.rs index 3f4cb8c9b..6ec703674 100644 --- a/pumpkin/src/command/args/resource/item.rs +++ b/pumpkin/src/command/args/resource/item.rs @@ -1,12 +1,12 @@ use async_trait::async_trait; use pumpkin_data::item::Item; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::text::TextComponent; use crate::command::{ CommandSender, args::{ - Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg, + Arg, ArgumentConsumer, ConsumeResult, ConsumedArgs, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, }, dispatcher::CommandError, @@ -28,23 +28,17 @@ impl GetClientSideArgParser for ItemArgumentConsumer { #[async_trait] impl ArgumentConsumer for ItemArgumentConsumer { - async fn consume<'a>( + fn consume<'a>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, args: &mut RawArgs<'a>, - ) -> Option> { - // todo: get an actual item - Some(Arg::Item(args.pop()?)) - } - - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + ) -> ConsumeResult<'a> { + let item = args.pop(); + match item { + Some(s) => Box::pin(async move { Some(Arg::Item(s)) }), + None => Box::pin(async move { None }), + } } } diff --git a/pumpkin/src/command/args/resource/particle.rs b/pumpkin/src/command/args/resource/particle.rs index b80c7ef11..6627c13b3 100644 --- a/pumpkin/src/command/args/resource/particle.rs +++ b/pumpkin/src/command/args/resource/particle.rs @@ -1,11 +1,11 @@ use async_trait::async_trait; use pumpkin_data::particle::Particle; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use crate::command::{ CommandSender, args::{ - Arg, ArgumentConsumer, ConsumedArgs, DefaultNameArgConsumer, FindArg, + Arg, ArgumentConsumer, ConsumeResult, ConsumedArgs, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, }, dispatcher::CommandError, @@ -29,27 +29,23 @@ impl GetClientSideArgParser for ParticleArgumentConsumer { #[async_trait] impl ArgumentConsumer for ParticleArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let name = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let name_opt: Option<&'a str> = args.pop(); - // Create a static damage type first - let particle = Particle::from_name(name.strip_prefix("minecraft:").unwrap_or(name))?; - // Find matching static damage type from values array - Some(Arg::Particle(particle)) - } + let result: Option> = name_opt.map_or_else( + || None, + |name| { + Particle::from_name(name.strip_prefix("minecraft:").unwrap_or(name)) + .map(Arg::Particle) + }, + ); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/args/resource_location.rs b/pumpkin/src/command/args/resource_location.rs index 3309f7186..0ada07200 100644 --- a/pumpkin/src/command/args/resource_location.rs +++ b/pumpkin/src/command/args/resource_location.rs @@ -1,12 +1,11 @@ use crate::command::CommandSender; use crate::command::args::{ - Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, + Arg, ArgumentConsumer, ConsumeResult, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, }; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::server::Server; -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; pub struct ResourceLocationArgumentConsumer { autocomplete: bool, @@ -22,39 +21,43 @@ impl GetClientSideArgParser for ResourceLocationArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for ResourceLocationArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - Some(Arg::ResourceLocation(args.pop()?)) - } - - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { if !self.autocomplete { - return Ok(None); + return Box::pin(async move { None }); } - // TODO + let s_opt: Option<&'a str> = args.pop(); - // let suggestions = server - // .bossbars - // .lock() - // .await - // .custom_bossbars - // .keys() - // .map(|suggestion| CommandSuggestion::new(suggestion, None)) - // .collect(); - - Ok(None) + Box::pin(async move { s_opt.map(Arg::ResourceLocation) }) } + + // async fn suggest<'a>( + // &'a self, + // _sender: &CommandSender, + // _server: &'a Server, + // _input: &'a str, + // ) -> Result>, CommandError> { + // if !self.autocomplete { + // return Ok(None); + // } + // // TODO + + // // let suggestions = server + // // .bossbars + // // .lock() + // // .await + // // .custom_bossbars + // // .keys() + // // .map(|suggestion| CommandSuggestion::new(suggestion, None)) + // // .collect(); + + // Ok(None) + // } } impl DefaultNameArgConsumer for ResourceLocationArgumentConsumer { diff --git a/pumpkin/src/command/args/rotation.rs b/pumpkin/src/command/args/rotation.rs index baa949756..02d31fdbb 100644 --- a/pumpkin/src/command/args/rotation.rs +++ b/pumpkin/src/command/args/rotation.rs @@ -1,7 +1,7 @@ -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use crate::command::CommandSender; +use crate::command::args::ConsumeResult; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::server::Server; @@ -22,39 +22,36 @@ impl GetClientSideArgParser for RotationArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for RotationArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _src: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let yaw = args.pop()?; - let pitch = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let yaw_str_opt = args.pop(); + let pitch_str_opt = args.pop(); - let mut yaw = yaw.parse::().ok()?; - let mut pitch = pitch.parse::().ok()?; + let (Some(yaw_str), Some(pitch_str)) = (yaw_str_opt, pitch_str_opt) else { + return Box::pin(async move { None }); + }; - yaw %= 360.0; - if yaw >= 180.0 { - yaw -= 360.0; - } - pitch %= 360.0; - if pitch >= 180.0 { - pitch -= 360.0; - } + let result: Option> = yaw_str.parse::().ok().and_then(|mut yaw| { + pitch_str.parse::().ok().map(|mut pitch| { + yaw %= 360.0; + if yaw >= 180.0 { + yaw -= 360.0; + } + pitch %= 360.0; + if pitch >= 180.0 { + pitch -= 360.0; + } - Some(Arg::Rotation(yaw, pitch)) - } + Arg::Rotation(yaw, pitch) + }) + }); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/args/simple.rs b/pumpkin/src/command/args/simple.rs index b04a4f31c..75d6f0dce 100644 --- a/pumpkin/src/command/args/simple.rs +++ b/pumpkin/src/command/args/simple.rs @@ -1,9 +1,11 @@ -use async_trait::async_trait; use pumpkin_protocol::java::client::play::{ - ArgumentType, CommandSuggestion, StringProtoArgBehavior, SuggestionProviders, + ArgumentType, StringProtoArgBehavior, SuggestionProviders, }; -use crate::{command::dispatcher::CommandError, server::Server}; +use crate::{ + command::{args::ConsumeResult, dispatcher::CommandError}, + server::Server, +}; use super::{ super::{ @@ -27,24 +29,16 @@ impl GetClientSideArgParser for SimpleArgConsumer { } } -#[async_trait] impl ArgumentConsumer for SimpleArgConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - Some(Arg::Simple(args.pop()?)) - } + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { s_opt.map(Arg::Simple) }) } } diff --git a/pumpkin/src/command/args/sound.rs b/pumpkin/src/command/args/sound.rs index 3b47374ec..f207ac43a 100644 --- a/pumpkin/src/command/args/sound.rs +++ b/pumpkin/src/command/args/sound.rs @@ -1,9 +1,11 @@ -use async_trait::async_trait; use pumpkin_data::sound::Sound; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::text::TextComponent; -use crate::{command::dispatcher::CommandError, server::Server}; +use crate::{ + command::{args::ConsumeResult, dispatcher::CommandError}, + server::Server, +}; use super::{ super::{ @@ -25,25 +27,16 @@ impl GetClientSideArgParser for SoundArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for SoundArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; - Some(Arg::Block(s)) - } + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { s_opt.map(Arg::Block) }) } } diff --git a/pumpkin/src/command/args/sound_category.rs b/pumpkin/src/command/args/sound_category.rs index cff990422..780ce019b 100644 --- a/pumpkin/src/command/args/sound_category.rs +++ b/pumpkin/src/command/args/sound_category.rs @@ -1,11 +1,11 @@ use crate::command::CommandSender; use crate::command::args::{ - Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, + Arg, ArgumentConsumer, ConsumeResult, DefaultNameArgConsumer, FindArg, GetClientSideArgParser, + SuggestResult, }; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::sound::SoundCategory; use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; @@ -24,51 +24,54 @@ impl GetClientSideArgParser for SoundCategoryArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for SoundCategoryArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - // Convert string input to SoundCategory enum - // Uses lowercase to make the command case-insensitive - let category = match s.to_lowercase().as_str() { - "master" => Some(SoundCategory::Master), // Default category, affects all sounds - "music" => Some(SoundCategory::Music), // Background music - // i don't use SoundCategory::from_name because its is record and not records :c - "record" => Some(SoundCategory::Records), // Music discs - "weather" => Some(SoundCategory::Weather), // Rain, thunder - "block" => Some(SoundCategory::Blocks), // Block sounds - "hostile" => Some(SoundCategory::Hostile), // Hostile mob sounds - "neutral" => Some(SoundCategory::Neutral), // Neutral mob sounds - "player" => Some(SoundCategory::Players), // Player sounds - "ambient" => Some(SoundCategory::Ambient), // Ambient environment - "voice" => Some(SoundCategory::Voice), // Voice/speech - _ => None, - }; + let result: Option> = s_opt.and_then(|s| { + let category = match s.to_lowercase().as_str() { + "master" => Some(SoundCategory::Master), + "music" => Some(SoundCategory::Music), + // i don't use SoundCategory::from_name because its is record and not records :c + "record" => Some(SoundCategory::Records), + "weather" => Some(SoundCategory::Weather), + "block" => Some(SoundCategory::Blocks), + "hostile" => Some(SoundCategory::Hostile), + "neutral" => Some(SoundCategory::Neutral), + "player" => Some(SoundCategory::Players), + "ambient" => Some(SoundCategory::Ambient), + "voice" => Some(SoundCategory::Voice), + _ => None, + }; - category.map(Arg::SoundCategory) // Simplified by removing redundant closure + category.map(Arg::SoundCategory) + }); + + Box::pin(async move { result }) } - async fn suggest<'a>( + fn suggest<'a>( &'a self, _sender: &CommandSender, _server: &'a Server, _input: &'a str, - ) -> Result>, CommandError> { - let categories = [ - "master", "music", "record", "weather", "block", "hostile", "neutral", "player", - "ambient", "voice", - ]; - let suggestions: Vec = categories - .iter() - .map(|cat| CommandSuggestion::new((*cat).to_string(), None)) - .collect(); - Ok(Some(suggestions)) + ) -> SuggestResult<'a> { + Box::pin(async move { + let categories = [ + "master", "music", "record", "weather", "block", "hostile", "neutral", "player", + "ambient", "voice", + ]; + let suggestions: Vec = categories + .iter() + .map(|cat| CommandSuggestion::new((*cat).to_string(), None)) + .collect(); + Ok(Some(suggestions)) + }) } } diff --git a/pumpkin/src/command/args/summonable_entities.rs b/pumpkin/src/command/args/summonable_entities.rs index 6395f666f..0ebb8607f 100644 --- a/pumpkin/src/command/args/summonable_entities.rs +++ b/pumpkin/src/command/args/summonable_entities.rs @@ -1,9 +1,12 @@ use async_trait::async_trait; use pumpkin_data::entity::EntityType; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::text::TextComponent; -use crate::{command::dispatcher::CommandError, server::Server}; +use crate::{ + command::{args::ConsumeResult, dispatcher::CommandError}, + server::Server, +}; use super::{ super::{ @@ -27,23 +30,15 @@ impl GetClientSideArgParser for SummonableEntitiesArgumentConsumer { #[async_trait] impl ArgumentConsumer for SummonableEntitiesArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; - Some(Arg::Block(s)) - } + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Box::pin(async move { s_opt.map(Arg::Block) }) } } diff --git a/pumpkin/src/command/args/textcomponent.rs b/pumpkin/src/command/args/textcomponent.rs index f77e17250..165999120 100644 --- a/pumpkin/src/command/args/textcomponent.rs +++ b/pumpkin/src/command/args/textcomponent.rs @@ -1,10 +1,9 @@ use crate::command::CommandSender; -use crate::command::args::{Arg, ArgumentConsumer, FindArg, GetClientSideArgParser}; +use crate::command::args::{Arg, ArgumentConsumer, ConsumeResult, FindArg, GetClientSideArgParser}; use crate::command::dispatcher::CommandError; use crate::command::tree::RawArgs; use crate::server::Server; -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use pumpkin_util::text::TextComponent; pub struct TextComponentArgConsumer; @@ -19,36 +18,32 @@ impl GetClientSideArgParser for TextComponentArgConsumer { } } -#[async_trait] impl ArgumentConsumer for TextComponentArgConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - let text_component = parse_text_component(s); - - let Some(text_component) = text_component else { - if s.starts_with('"') && s.ends_with('"') { - let s = s.replace('"', ""); - return Some(Arg::TextComponent(TextComponent::text(s))); - } - return None; + let Some(s) = s_opt else { + return Box::pin(async move { None }); }; - Some(Arg::TextComponent(text_component)) - } + let text_component_opt = parse_text_component(s); - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + let final_arg: Option> = text_component_opt.map_or_else( + || { + (s.starts_with('"') && s.ends_with('"')).then(|| { + let s_owned = s.replace('"', ""); + Arg::TextComponent(TextComponent::text(s_owned)) + }) + }, + |text_component| Some(Arg::TextComponent(text_component)), + ); + + Box::pin(async move { final_arg }) } } diff --git a/pumpkin/src/command/args/time.rs b/pumpkin/src/command/args/time.rs index db020be70..b6f85d75f 100644 --- a/pumpkin/src/command/args/time.rs +++ b/pumpkin/src/command/args/time.rs @@ -1,9 +1,11 @@ -use async_trait::async_trait; -use pumpkin_protocol::java::client::play::{ArgumentType, CommandSuggestion, SuggestionProviders}; +use pumpkin_protocol::java::client::play::{ArgumentType, SuggestionProviders}; use crate::command::{ CommandSender, - args::{Arg, ArgumentConsumer, DefaultNameArgConsumer, FindArg, GetClientSideArgParser}, + args::{ + Arg, ArgumentConsumer, ConsumeResult, DefaultNameArgConsumer, FindArg, + GetClientSideArgParser, + }, dispatcher::CommandError, tree::RawArgs, }; @@ -21,47 +23,39 @@ impl GetClientSideArgParser for TimeArgumentConsumer { } } -#[async_trait] impl ArgumentConsumer for TimeArgumentConsumer { - async fn consume<'a>( + fn consume<'a, 'b>( &'a self, - _sender: &CommandSender, + _sender: &'a CommandSender, _server: &'a Server, - args: &mut RawArgs<'a>, - ) -> Option> { - let s = args.pop()?; + args: &'b mut RawArgs<'a>, + ) -> ConsumeResult<'a> { + let s_opt: Option<&'a str> = args.pop(); - // Parse number and unit - let (num_str, unit) = s - .find(|c: char| c.is_alphabetic()) - .map_or((s, "t"), |pos| (&s[..pos], &s[pos..])); + let result: Option> = s_opt.and_then(|s| { + let (num_str, unit) = s + .find(|c: char| c.is_alphabetic()) + .map_or((s, "t"), |pos| (&s[..pos], &s[pos..])); - let number = num_str.parse::().ok()?; - if number < 0.0 { - return None; - } + let number = num_str.parse::().ok()?; // Replaces .ok()? - // Convert to ticks based on unit - let ticks = match unit { - "d" => number * 24000.0, - "s" => number * 20.0, - "t" => number, - _ => return None, - }; + if number < 0.0 { + return None; + } - // Round to nearest integer - let ticks = ticks.round() as i32; + let ticks = match unit { + "d" => number * 24000.0, + "s" => number * 20.0, + "t" => number, + _ => return None, + }; - Some(Arg::Time(ticks)) - } + let ticks = ticks.round() as i32; - async fn suggest<'a>( - &'a self, - _sender: &CommandSender, - _server: &'a Server, - _input: &'a str, - ) -> Result>, CommandError> { - Ok(None) + Some(Arg::Time(ticks)) + }); + + Box::pin(async move { result }) } } diff --git a/pumpkin/src/command/commands/ban.rs b/pumpkin/src/command/commands/ban.rs index 527a18270..3b0d83aa9 100644 --- a/pumpkin/src/command/commands/ban.rs +++ b/pumpkin/src/command/commands/ban.rs @@ -1,3 +1,4 @@ +use crate::command::CommandResult; use crate::entity::EntityBase; use crate::{ command::{ @@ -13,7 +14,6 @@ use crate::{ net::DisconnectReason, }; use CommandError::InvalidConsumption; -use async_trait::async_trait; use pumpkin_util::text::TextComponent; const NAMES: [&str; 1] = ["ban"]; @@ -24,43 +24,45 @@ const ARG_REASON: &str = "reason"; struct NoReasonExecutor; -#[async_trait] impl CommandExecutor for NoReasonExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else { - return Err(InvalidConsumption(Some(ARG_TARGET.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); + }; - ban_player(sender, &targets[0], None).await; - Ok(()) + ban_player(sender, &targets[0], None).await; + Ok(()) + }) } } struct ReasonExecutor; -#[async_trait] impl CommandExecutor for ReasonExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else { - return Err(InvalidConsumption(Some(ARG_TARGET.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); + }; - let Some(Arg::Msg(reason)) = args.get(ARG_REASON) else { - return Err(InvalidConsumption(Some(ARG_REASON.into()))); - }; + let Some(Arg::Msg(reason)) = args.get(ARG_REASON) else { + return Err(InvalidConsumption(Some(ARG_REASON.into()))); + }; - ban_player(sender, &targets[0], Some(reason.clone())).await; - Ok(()) + ban_player(sender, &targets[0], Some(reason.clone())).await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/banip.rs b/pumpkin/src/command/commands/banip.rs index 791a743c1..9f6a3943e 100644 --- a/pumpkin/src/command/commands/banip.rs +++ b/pumpkin/src/command/commands/banip.rs @@ -2,7 +2,7 @@ use std::{net::IpAddr, str::FromStr}; use crate::{ command::{ - CommandError, CommandExecutor, CommandSender, + CommandError, CommandExecutor, CommandResult, CommandSender, args::{Arg, ConsumedArgs, message::MsgArgConsumer, simple::SimpleArgConsumer}, tree::{CommandTree, builder::argument}, }, @@ -13,7 +13,6 @@ use crate::{ server::Server, }; use CommandError::InvalidConsumption; -use async_trait::async_trait; use pumpkin_util::text::TextComponent; const NAMES: [&str; 1] = ["ban-ip"]; @@ -37,43 +36,45 @@ async fn parse_ip(target: &str, server: &Server) -> Option { struct NoReasonExecutor; -#[async_trait] impl CommandExecutor for NoReasonExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else { - return Err(InvalidConsumption(Some(ARG_TARGET.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); + }; - ban_ip(sender, server, target, None).await; - Ok(()) + ban_ip(sender, server, target, None).await; + Ok(()) + }) } } struct ReasonExecutor; -#[async_trait] impl CommandExecutor for ReasonExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else { - return Err(InvalidConsumption(Some(ARG_TARGET.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); + }; - let Some(Arg::Msg(reason)) = args.get(ARG_REASON) else { - return Err(InvalidConsumption(Some(ARG_REASON.into()))); - }; + let Some(Arg::Msg(reason)) = args.get(ARG_REASON) else { + return Err(InvalidConsumption(Some(ARG_REASON.into()))); + }; - ban_ip(sender, server, target, Some(reason.clone())).await; - Ok(()) + ban_ip(sender, server, target, Some(reason.clone())).await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/banlist.rs b/pumpkin/src/command/commands/banlist.rs index 29611c9fe..f06d4aee1 100644 --- a/pumpkin/src/command/commands/banlist.rs +++ b/pumpkin/src/command/commands/banlist.rs @@ -1,14 +1,12 @@ use crate::{ command::{ - CommandError, CommandExecutor, CommandSender, + CommandError, CommandExecutor, CommandResult, CommandSender, args::{Arg, ConsumedArgs, simple::SimpleArgConsumer}, - tree::CommandTree, - tree::builder::argument, + tree::{CommandTree, builder::argument}, }, data::{banned_ip_data::BANNED_IP_LIST, banned_player_data::BANNED_PLAYER_LIST}, }; use CommandError::InvalidConsumption; -use async_trait::async_trait; use pumpkin_util::text::TextComponent; const NAMES: [&str; 1] = ["banlist"]; @@ -18,91 +16,93 @@ const ARG_LIST_TYPE: &str = "ips|players"; struct ListExecutor; -#[async_trait] impl CommandExecutor for ListExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Simple(list_type)) = args.get(&ARG_LIST_TYPE) else { - return Err(InvalidConsumption(Some(ARG_LIST_TYPE.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Simple(list_type)) = args.get(&ARG_LIST_TYPE) else { + return Err(InvalidConsumption(Some(ARG_LIST_TYPE.into()))); + }; - match *list_type { - "ips" => { - let lock = &BANNED_IP_LIST.read().await; - let entries = lock - .banned_ips - .iter() - .map(|entry| { - ( - entry.ip.to_string(), - entry.source.clone(), - entry.reason.clone(), - ) - }) - .collect(); + match *list_type { + "ips" => { + let lock = &BANNED_IP_LIST.read().await; + let entries = lock + .banned_ips + .iter() + .map(|entry| { + ( + entry.ip.to_string(), + entry.source.clone(), + entry.reason.clone(), + ) + }) + .collect(); - handle_banlist(entries, sender).await; + handle_banlist(entries, sender).await; + } + "players" => { + let lock = &BANNED_PLAYER_LIST.read().await; + let entries = lock + .banned_players + .iter() + .map(|entry| { + ( + entry.name.clone(), + entry.source.clone(), + entry.reason.clone(), + ) + }) + .collect(); + + handle_banlist(entries, sender).await; + } + _ => { + return Err(CommandError::CommandFailed(Box::new(TextComponent::text( + "Incorrect argument for command".to_string(), + )))); + } } - "players" => { - let lock = &BANNED_PLAYER_LIST.read().await; - let entries = lock - .banned_players - .iter() - .map(|entry| { - ( - entry.name.clone(), - entry.source.clone(), - entry.reason.clone(), - ) - }) - .collect(); - handle_banlist(entries, sender).await; - } - _ => { - return Err(CommandError::CommandFailed(Box::new(TextComponent::text( - "Incorrect argument for command".to_string(), - )))); - } - } - - Ok(()) + Ok(()) + }) } } struct ListAllExecutor; -#[async_trait] impl CommandExecutor for ListAllExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let mut entries = Vec::new(); - for entry in &BANNED_PLAYER_LIST.read().await.banned_players { - entries.push(( - entry.name.clone(), - entry.source.clone(), - entry.reason.clone(), - )); - } + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let mut entries = Vec::new(); + for entry in &BANNED_PLAYER_LIST.read().await.banned_players { + entries.push(( + entry.name.clone(), + entry.source.clone(), + entry.reason.clone(), + )); + } - for entry in &BANNED_IP_LIST.read().await.banned_ips { - entries.push(( - entry.ip.to_string(), - entry.source.clone(), - entry.reason.clone(), - )); - } + for entry in &BANNED_IP_LIST.read().await.banned_ips { + entries.push(( + entry.ip.to_string(), + entry.source.clone(), + entry.reason.clone(), + )); + } - handle_banlist(entries, sender).await; - Ok(()) + handle_banlist(entries, sender).await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/bossbar.rs b/pumpkin/src/command/commands/bossbar.rs index 3c8b2e95d..ab7068767 100644 --- a/pumpkin/src/command/commands/bossbar.rs +++ b/pumpkin/src/command/commands/bossbar.rs @@ -6,16 +6,13 @@ use crate::command::args::players::PlayersArgumentConsumer; use crate::command::args::resource_location::ResourceLocationArgumentConsumer; use crate::command::args::{ConsumedArgs, FindArg, FindArgDefaultName}; -use crate::command::dispatcher::CommandError; use crate::command::args::textcomponent::TextComponentArgConsumer; use crate::command::tree::CommandTree; use crate::command::tree::builder::{argument, argument_default_name, literal}; -use crate::command::{CommandExecutor, CommandSender}; -use crate::server::Server; +use crate::command::{CommandExecutor, CommandResult, CommandSender}; use crate::world::bossbar::Bossbar; use crate::world::custom_bossbar::BossbarUpdateError; -use async_trait::async_trait; use pumpkin_util::text::TextComponent; use pumpkin_util::text::color::{Color, NamedColor}; use pumpkin_util::text::hover::HoverEvent; @@ -55,361 +52,276 @@ enum CommandValueSet { struct AddExecuter; -#[async_trait] impl CommandExecutor for AddExecuter { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let mut namespace = non_autocomplete_consumer() - .find_arg_default_name(args)? - .to_string(); - if !namespace.contains(':') { - namespace = format!("minecraft:{namespace}"); - } + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let mut namespace = non_autocomplete_consumer() + .find_arg_default_name(args)? + .to_string(); + if !namespace.contains(':') { + namespace = format!("minecraft:{namespace}"); + } - let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?; + let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?; - if server.bossbars.lock().await.has_bossbar(&namespace) { - send_error_message( - sender, - TextComponent::translate( - "commands.bossbar.create.failed", - [TextComponent::text(namespace.clone())], - ), - ) - .await; - return Ok(()); - } + if server.bossbars.lock().await.has_bossbar(&namespace) { + send_error_message( + sender, + TextComponent::translate( + "commands.bossbar.create.failed", + [TextComponent::text(namespace.clone())], + ), + ) + .await; + return Ok(()); + } - let bossbar = Bossbar::new(text_component); - server - .bossbars - .lock() - .await - .create_bossbar(namespace.clone(), bossbar.clone()); + let bossbar = Bossbar::new(text_component); + server + .bossbars + .lock() + .await + .create_bossbar(namespace.clone(), bossbar.clone()); - sender - .send_message(TextComponent::translate( - "commands.bossbar.create.success", - [bossbar_prefix(bossbar.title.clone(), namespace.clone())], - )) - .await; + sender + .send_message(TextComponent::translate( + "commands.bossbar.create.success", + [bossbar_prefix(bossbar.title.clone(), namespace.clone())], + )) + .await; - Ok(()) + Ok(()) + }) } } struct GetExecuter(CommandValueGet); -#[async_trait] impl CommandExecutor for GetExecuter { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let namespace = autocomplete_consumer() - .find_arg_default_name(args)? - .to_string(); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let namespace = autocomplete_consumer() + .find_arg_default_name(args)? + .to_string(); - let Some(bossbar) = server.bossbars.lock().await.get_bossbar(&namespace) else { - handle_bossbar_error( - sender, - BossbarUpdateError::InvalidResourceLocation(namespace.clone()), - ) - .await; - return Ok(()); - }; + let Some(bossbar) = server.bossbars.lock().await.get_bossbar(&namespace) else { + handle_bossbar_error( + sender, + BossbarUpdateError::InvalidResourceLocation(namespace.clone()), + ) + .await; + return Ok(()); + }; - match self.0 { - CommandValueGet::Max => { - sender - .send_message(TextComponent::translate( - "commands.bossbar.get.max", - [ - bossbar_prefix(bossbar.bossbar_data.title.clone(), namespace.clone()), - TextComponent::text(bossbar.max.to_string()), - ], - )) - .await; - return Ok(()); + match self.0 { + CommandValueGet::Max => { + sender + .send_message(TextComponent::translate( + "commands.bossbar.get.max", + [ + bossbar_prefix( + bossbar.bossbar_data.title.clone(), + namespace.clone(), + ), + TextComponent::text(bossbar.max.to_string()), + ], + )) + .await; + return Ok(()); + } + CommandValueGet::Players => {} + CommandValueGet::Value => { + sender + .send_message(TextComponent::translate( + "commands.bossbar.get.value", + [ + bossbar_prefix( + bossbar.bossbar_data.title.clone(), + namespace.clone(), + ), + TextComponent::text(bossbar.value.to_string()), + ], + )) + .await; + return Ok(()); + } + CommandValueGet::Visible => { + let state = if bossbar.visible { + "commands.bossbar.get.visible.visible" + } else { + "commands.bossbar.get.visible.hidden" + }; + sender + .send_message(TextComponent::translate( + state, + [bossbar_prefix( + bossbar.bossbar_data.title.clone(), + namespace.clone(), + )], + )) + .await; + return Ok(()); + } } - CommandValueGet::Players => {} - CommandValueGet::Value => { - sender - .send_message(TextComponent::translate( - "commands.bossbar.get.value", - [ - bossbar_prefix(bossbar.bossbar_data.title.clone(), namespace.clone()), - TextComponent::text(bossbar.value.to_string()), - ], - )) - .await; - return Ok(()); - } - CommandValueGet::Visible => { - let state = if bossbar.visible { - "commands.bossbar.get.visible.visible" - } else { - "commands.bossbar.get.visible.hidden" - }; - sender - .send_message(TextComponent::translate( - state, - [bossbar_prefix( - bossbar.bossbar_data.title.clone(), - namespace.clone(), - )], - )) - .await; - return Ok(()); - } - } - Ok(()) + Ok(()) + }) } } struct ListExecuter; -#[async_trait] impl CommandExecutor for ListExecuter { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let bossbars = server.bossbars.lock().await.get_all_bossbars(); - let Some(bossbars) = bossbars else { - sender - .send_message(TextComponent::translate( - "commands.bossbar.list.bars.none", - [], - )) - .await; - return Ok(()); - }; - if bossbars.is_empty() { - sender - .send_message(TextComponent::translate( - "commands.bossbar.list.bars.none", - [], - )) - .await; - return Ok(()); - } + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let bossbars = server.bossbars.lock().await.get_all_bossbars(); + let Some(bossbars) = bossbars else { + sender + .send_message(TextComponent::translate( + "commands.bossbar.list.bars.none", + [], + )) + .await; + return Ok(()); + }; + if bossbars.is_empty() { + sender + .send_message(TextComponent::translate( + "commands.bossbar.list.bars.none", + [], + )) + .await; + return Ok(()); + } - let mut bossbars_text = TextComponent::text(""); - for (i, bossbar) in bossbars.iter().enumerate() { - if i == 0 { - bossbars_text = bossbars_text.add_child(bossbar_prefix( - bossbar.bossbar_data.title.clone(), - bossbar.namespace.clone(), - )); - } else { - bossbars_text = - bossbars_text.add_child(TextComponent::text(", ").add_child(bossbar_prefix( + let mut bossbars_text = TextComponent::text(""); + for (i, bossbar) in bossbars.iter().enumerate() { + if i == 0 { + bossbars_text = bossbars_text.add_child(bossbar_prefix( bossbar.bossbar_data.title.clone(), bossbar.namespace.clone(), - ))); + )); + } else { + bossbars_text = bossbars_text.add_child(TextComponent::text(", ").add_child( + bossbar_prefix( + bossbar.bossbar_data.title.clone(), + bossbar.namespace.clone(), + ), + )); + } } - } - sender - .send_message(TextComponent::translate( - "commands.bossbar.list.bars.some", - [ - TextComponent::text(bossbars.len().to_string()), - bossbars_text, - ], - )) - .await; - Ok(()) + sender + .send_message(TextComponent::translate( + "commands.bossbar.list.bars.some", + [ + TextComponent::text(bossbars.len().to_string()), + bossbars_text, + ], + )) + .await; + Ok(()) + }) } } struct RemoveExecuter; -#[async_trait] impl CommandExecutor for RemoveExecuter { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let namespace = autocomplete_consumer() - .find_arg_default_name(args)? - .to_string(); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let namespace = autocomplete_consumer() + .find_arg_default_name(args)? + .to_string(); - let Some(bossbar) = server.bossbars.lock().await.get_bossbar(&namespace) else { - handle_bossbar_error( - sender, - BossbarUpdateError::InvalidResourceLocation(namespace), - ) - .await; - return Ok(()); - }; - - sender - .send_message(TextComponent::translate( - "commands.bossbar.remove.success", - [bossbar_prefix( - bossbar.bossbar_data.title.clone(), - namespace.clone(), - )], - )) - .await; - - match server - .bossbars - .lock() - .await - .remove_bossbar(server, namespace.clone()) - .await - { - Ok(()) => {} - Err(err) => { - handle_bossbar_error(sender, err).await; + let Some(bossbar) = server.bossbars.lock().await.get_bossbar(&namespace) else { + handle_bossbar_error( + sender, + BossbarUpdateError::InvalidResourceLocation(namespace), + ) + .await; return Ok(()); - } - } + }; - Ok(()) + sender + .send_message(TextComponent::translate( + "commands.bossbar.remove.success", + [bossbar_prefix( + bossbar.bossbar_data.title.clone(), + namespace.clone(), + )], + )) + .await; + + match server + .bossbars + .lock() + .await + .remove_bossbar(server, namespace.clone()) + .await + { + Ok(()) => {} + Err(err) => { + handle_bossbar_error(sender, err).await; + return Ok(()); + } + } + + Ok(()) + }) } } struct SetExecuter(CommandValueSet); -#[async_trait] impl CommandExecutor for SetExecuter { #[expect(clippy::too_many_lines)] - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let namespace = autocomplete_consumer().find_arg_default_name(args)?; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let namespace = autocomplete_consumer().find_arg_default_name(args)?; - let Some(bossbar) = server.bossbars.lock().await.get_bossbar(namespace) else { - handle_bossbar_error( - sender, - BossbarUpdateError::InvalidResourceLocation(namespace.to_string()), - ) - .await; - return Ok(()); - }; + let Some(bossbar) = server.bossbars.lock().await.get_bossbar(namespace) else { + handle_bossbar_error( + sender, + BossbarUpdateError::InvalidResourceLocation(namespace.to_string()), + ) + .await; + return Ok(()); + }; - match self.0 { - CommandValueSet::Color => { - let color = BossbarColorArgumentConsumer.find_arg_default_name(args)?; - match server - .bossbars - .lock() - .await - .update_color(server, namespace.to_string(), color.clone()) - .await - { - Ok(()) => {} - Err(err) => { - handle_bossbar_error(sender, err).await; - return Ok(()); - } - } - sender - .send_message(TextComponent::translate( - "commands.bossbar.set.color.success", - [bossbar_prefix( - bossbar.bossbar_data.title.clone(), - namespace.to_string(), - )], - )) - .await; - Ok(()) - } - CommandValueSet::Max => { - let Ok(max_value) = max_value_consumer().find_arg_default_name(args)? else { - send_error_message( - sender, - TextComponent::translate( - "parsing.int.invalid", - [TextComponent::text(i32::MAX.to_string())], - ), - ) - .await; - return Ok(()); - }; - - match server - .bossbars - .lock() - .await - .update_health( - server, - namespace.to_string(), - max_value as u32, - bossbar.value, - ) - .await - { - Ok(()) => {} - Err(err) => { - handle_bossbar_error(sender, err).await; - return Ok(()); - } - } - - sender - .send_message(TextComponent::translate( - "commands.bossbar.set.max.success", - [ - bossbar_prefix( - bossbar.bossbar_data.title.clone(), - namespace.to_string(), - ), - TextComponent::text(max_value.to_string()), - ], - )) - .await; - Ok(()) - } - CommandValueSet::Name => { - let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?; - match server - .bossbars - .lock() - .await - .update_name(server, namespace, text_component.clone()) - .await - { - Ok(()) => {} - Err(err) => { - handle_bossbar_error(sender, err).await; - return Ok(()); - } - } - - sender - .send_message(TextComponent::translate( - "commands.bossbar.set.name.success", - [bossbar_prefix(text_component, namespace.to_string())], - )) - .await; - Ok(()) - } - CommandValueSet::Players(has_players) => { - if !has_players { + match self.0 { + CommandValueSet::Color => { + let color = BossbarColorArgumentConsumer.find_arg_default_name(args)?; match server .bossbars .lock() .await - .update_players(server, namespace.to_string(), vec![]) + .update_color(server, namespace.to_string(), color.clone()) .await { Ok(()) => {} @@ -420,158 +332,255 @@ impl CommandExecutor for SetExecuter { } sender .send_message(TextComponent::translate( - "commands.bossbar.set.players.success.none", + "commands.bossbar.set.color.success", [bossbar_prefix( bossbar.bossbar_data.title.clone(), namespace.to_string(), )], )) .await; - return Ok(()); + Ok(()) } + CommandValueSet::Max => { + let Ok(max_value) = max_value_consumer().find_arg_default_name(args)? else { + send_error_message( + sender, + TextComponent::translate( + "parsing.int.invalid", + [TextComponent::text(i32::MAX.to_string())], + ), + ) + .await; + return Ok(()); + }; - //TODO: Confirm that this is the vanilla way - let targets = PlayersArgumentConsumer.find_arg_default_name(args)?; - let players: Vec = - targets.iter().map(|player| player.gameprofile.id).collect(); - let count = players.len(); + match server + .bossbars + .lock() + .await + .update_health( + server, + namespace.to_string(), + max_value as u32, + bossbar.value, + ) + .await + { + Ok(()) => {} + Err(err) => { + handle_bossbar_error(sender, err).await; + return Ok(()); + } + } - match server - .bossbars - .lock() - .await - .update_players(server, namespace.to_string(), players) - .await - { - Ok(()) => {} - Err(err) => { - handle_bossbar_error(sender, err).await; + sender + .send_message(TextComponent::translate( + "commands.bossbar.set.max.success", + [ + bossbar_prefix( + bossbar.bossbar_data.title.clone(), + namespace.to_string(), + ), + TextComponent::text(max_value.to_string()), + ], + )) + .await; + Ok(()) + } + CommandValueSet::Name => { + let text_component = TextComponentArgConsumer::find_arg(args, ARG_NAME)?; + match server + .bossbars + .lock() + .await + .update_name(server, namespace, text_component.clone()) + .await + { + Ok(()) => {} + Err(err) => { + handle_bossbar_error(sender, err).await; + return Ok(()); + } + } + + sender + .send_message(TextComponent::translate( + "commands.bossbar.set.name.success", + [bossbar_prefix(text_component, namespace.to_string())], + )) + .await; + Ok(()) + } + CommandValueSet::Players(has_players) => { + if !has_players { + match server + .bossbars + .lock() + .await + .update_players(server, namespace.to_string(), vec![]) + .await + { + Ok(()) => {} + Err(err) => { + handle_bossbar_error(sender, err).await; + return Ok(()); + } + } + sender + .send_message(TextComponent::translate( + "commands.bossbar.set.players.success.none", + [bossbar_prefix( + bossbar.bossbar_data.title.clone(), + namespace.to_string(), + )], + )) + .await; return Ok(()); } + + //TODO: Confirm that this is the vanilla way + let targets = PlayersArgumentConsumer.find_arg_default_name(args)?; + let players: Vec = + targets.iter().map(|player| player.gameprofile.id).collect(); + let count = players.len(); + + match server + .bossbars + .lock() + .await + .update_players(server, namespace.to_string(), players) + .await + { + Ok(()) => {} + Err(err) => { + handle_bossbar_error(sender, err).await; + return Ok(()); + } + } + + let player_names: Vec = targets + .iter() + .map(|player| player.gameprofile.name.clone()) + .collect(); + + sender + .send_message(TextComponent::translate( + "commands.bossbar.set.players.success.some", + [ + bossbar_prefix( + bossbar.bossbar_data.title.clone(), + namespace.to_string(), + ), + TextComponent::text(count.to_string()), + TextComponent::text(player_names.join(", ").clone()), + ], + )) + .await; + Ok(()) } - - let player_names: Vec = targets - .iter() - .map(|player| player.gameprofile.name.clone()) - .collect(); - - sender - .send_message(TextComponent::translate( - "commands.bossbar.set.players.success.some", - [ - bossbar_prefix( + CommandValueSet::Style => { + let style = BossbarStyleArgumentConsumer.find_arg_default_name(args)?; + match server + .bossbars + .lock() + .await + .update_division(server, namespace.to_string(), style.clone()) + .await + { + Ok(()) => {} + Err(err) => { + handle_bossbar_error(sender, err).await; + return Ok(()); + } + } + sender + .send_message(TextComponent::translate( + "commands.bossbar.set.style.success", + [bossbar_prefix( bossbar.bossbar_data.title.clone(), namespace.to_string(), + )], + )) + .await; + Ok(()) + } + CommandValueSet::Value => { + let Ok(value) = value_consumer().find_arg_default_name(args)? else { + send_error_message( + sender, + TextComponent::translate( + "parsing.int.invalid", + [TextComponent::text(i32::MAX.to_string())], ), - TextComponent::text(count.to_string()), - TextComponent::text(player_names.join(", ").clone()), - ], - )) - .await; - Ok(()) - } - CommandValueSet::Style => { - let style = BossbarStyleArgumentConsumer.find_arg_default_name(args)?; - match server - .bossbars - .lock() - .await - .update_division(server, namespace.to_string(), style.clone()) - .await - { - Ok(()) => {} - Err(err) => { - handle_bossbar_error(sender, err).await; + ) + .await; return Ok(()); - } - } - sender - .send_message(TextComponent::translate( - "commands.bossbar.set.style.success", - [bossbar_prefix( - bossbar.bossbar_data.title.clone(), - namespace.to_string(), - )], - )) - .await; - Ok(()) - } - CommandValueSet::Value => { - let Ok(value) = value_consumer().find_arg_default_name(args)? else { - send_error_message( - sender, - TextComponent::translate( - "parsing.int.invalid", - [TextComponent::text(i32::MAX.to_string())], - ), - ) - .await; - return Ok(()); - }; + }; - match server - .bossbars - .lock() - .await - .update_health(server, namespace.to_string(), bossbar.max, value as u32) - .await - { - Ok(()) => {} - Err(err) => { - handle_bossbar_error(sender, err).await; - return Ok(()); + match server + .bossbars + .lock() + .await + .update_health(server, namespace.to_string(), bossbar.max, value as u32) + .await + { + Ok(()) => {} + Err(err) => { + handle_bossbar_error(sender, err).await; + return Ok(()); + } } - } - sender - .send_message(TextComponent::translate( - "commands.bossbar.set.value.success", - [ - bossbar_prefix( + sender + .send_message(TextComponent::translate( + "commands.bossbar.set.value.success", + [ + bossbar_prefix( + bossbar.bossbar_data.title.clone(), + namespace.to_string(), + ), + TextComponent::text(value.to_string()), + ], + )) + .await; + Ok(()) + } + CommandValueSet::Visible => { + let visibility = BoolArgConsumer::find_arg(args, ARG_VISIBLE)?; + + match server + .bossbars + .lock() + .await + .update_visibility(server, namespace.to_string(), visibility) + .await + { + Ok(()) => {} + Err(err) => { + handle_bossbar_error(sender, err).await; + return Ok(()); + } + } + + let state = if visibility { + "commands.bossbar.set.visible.success.visible" + } else { + "commands.bossbar.set.visible.success.hidden" + }; + + sender + .send_message(TextComponent::translate( + state, + [bossbar_prefix( bossbar.bossbar_data.title.clone(), namespace.to_string(), - ), - TextComponent::text(value.to_string()), - ], - )) - .await; - Ok(()) - } - CommandValueSet::Visible => { - let visibility = BoolArgConsumer::find_arg(args, ARG_VISIBLE)?; - - match server - .bossbars - .lock() - .await - .update_visibility(server, namespace.to_string(), visibility) - .await - { - Ok(()) => {} - Err(err) => { - handle_bossbar_error(sender, err).await; - return Ok(()); - } + )], + )) + .await; + Ok(()) } - - let state = if visibility { - "commands.bossbar.set.visible.success.visible" - } else { - "commands.bossbar.set.visible.success.hidden" - }; - - sender - .send_message(TextComponent::translate( - state, - [bossbar_prefix( - bossbar.bossbar_data.title.clone(), - namespace.to_string(), - )], - )) - .await; - Ok(()) } - } + }) } } diff --git a/pumpkin/src/command/commands/clear.rs b/pumpkin/src/command/commands/clear.rs index 24d37857d..321dfad6b 100644 --- a/pumpkin/src/command/commands/clear.rs +++ b/pumpkin/src/command/commands/clear.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_util::text::TextComponent; use pumpkin_util::text::color::NamedColor; use pumpkin_world::item::ItemStack; @@ -9,7 +8,7 @@ use crate::command::args::players::PlayersArgumentConsumer; use crate::command::args::{Arg, ConsumedArgs}; use crate::command::tree::CommandTree; use crate::command::tree::builder::{argument, require}; -use crate::command::{CommandError, CommandExecutor, CommandSender}; +use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender}; use crate::entity::EntityBase; use crate::entity::player::Player; use CommandError::InvalidConsumption; @@ -71,57 +70,59 @@ async fn clear_command_text_output(item_count: u64, targets: &[Arc]) -> struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else { - return Err(InvalidConsumption(Some(ARG_TARGET.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Players(targets)) = args.get(&ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); + }; - let mut item_count = 0; - for target in targets { - item_count += clear_player(target).await; - } + let mut item_count = 0; + for target in targets { + item_count += clear_player(target).await; + } - let msg = clear_command_text_output(item_count, targets).await; + let msg = clear_command_text_output(item_count, targets).await; - sender.send_message(msg).await; + sender.send_message(msg).await; - Ok(()) + Ok(()) + }) } } struct SelfExecutor; -#[async_trait] impl CommandExecutor for SelfExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?; - let item_count = clear_player(&target).await; + let item_count = clear_player(&target).await; - let hold_target = [target]; - let msg = clear_command_text_output(item_count, &hold_target).await; + let hold_target = [target]; + let msg = clear_command_text_output(item_count, &hold_target).await; - sender.send_message(msg).await; + sender.send_message(msg).await; - Ok(()) + Ok(()) + }) } } -#[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 { CommandTree::new(NAMES, DESCRIPTION) .then(argument(ARG_TARGET, PlayersArgumentConsumer).execute(Executor)) - .then(require(|sender| sender.is_player()).execute(SelfExecutor)) + .then(require(super::super::CommandSender::is_player).execute(SelfExecutor)) } diff --git a/pumpkin/src/command/commands/damage.rs b/pumpkin/src/command/commands/damage.rs index fc81894d7..5e1a003b9 100644 --- a/pumpkin/src/command/commands/damage.rs +++ b/pumpkin/src/command/commands/damage.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::damage::DamageType; use pumpkin_util::text::{ TextComponent, @@ -6,7 +5,7 @@ use pumpkin_util::text::{ }; use crate::command::{ - CommandError, CommandExecutor, CommandSender, + CommandExecutor, CommandResult, CommandSender, args::{ Arg, ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer, entity::EntityArgumentConsumer, position_3d::Position3DArgumentConsumer, @@ -59,100 +58,104 @@ async fn send_damage_result( .await; } -#[async_trait] impl CommandExecutor for LocationExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let target = EntityArgumentConsumer::find_arg(args, ARG_TARGET)?; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let target = EntityArgumentConsumer::find_arg(args, ARG_TARGET)?; - let Ok(Ok(amount)) = BoundedNumArgumentConsumer::::find_arg(args, ARG_AMOUNT) else { - sender - .send_message( - TextComponent::text("Invalid damage amount") - .color(Color::Named(NamedColor::Red)), + let Ok(Ok(amount)) = BoundedNumArgumentConsumer::::find_arg(args, ARG_AMOUNT) + else { + sender + .send_message( + TextComponent::text("Invalid damage amount") + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + }; + + let damage_type = + args.get(ARG_DAMAGE_TYPE) + .map_or(DamageType::GENERIC, |arg| match arg { + Arg::DamageType(dt) => *dt, + _ => DamageType::GENERIC, + }); + + let location = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; + + let success = target + .damage_with_context( + target.clone(), + amount, + damage_type, + Some(location), + None, + None, ) .await; - return Ok(()); - }; - let damage_type = args - .get(ARG_DAMAGE_TYPE) - .map_or(DamageType::GENERIC, |arg| match arg { - Arg::DamageType(dt) => *dt, - _ => DamageType::GENERIC, - }); + send_damage_result(sender, success, amount, target.get_display_name().await).await; - let location = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; - - let success = target - .damage_with_context( - target.clone(), - amount, - damage_type, - Some(location), - None, - None, - ) - .await; - - send_damage_result(sender, success, amount, target.get_display_name().await).await; - - Ok(()) + Ok(()) + }) } } -#[async_trait] impl CommandExecutor for EntityExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let target = EntityArgumentConsumer::find_arg(args, ARG_TARGET)?; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let target = EntityArgumentConsumer::find_arg(args, ARG_TARGET)?; - let Ok(Ok(amount)) = BoundedNumArgumentConsumer::::find_arg(args, ARG_AMOUNT) else { - sender - .send_message( - TextComponent::text("Invalid damage amount") - .color(Color::Named(NamedColor::Red)), + let Ok(Ok(amount)) = BoundedNumArgumentConsumer::::find_arg(args, ARG_AMOUNT) + else { + sender + .send_message( + TextComponent::text("Invalid damage amount") + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + }; + + let damage_type = + args.get(ARG_DAMAGE_TYPE) + .map_or(DamageType::GENERIC, |arg| match arg { + Arg::DamageType(dt) => *dt, + _ => DamageType::GENERIC, + }); + + let source = EntityArgumentConsumer::find_arg(args, ARG_ENTITY).ok(); + let cause = if self.0 { + EntityArgumentConsumer::find_arg(args, ARG_CAUSE).ok() + } else { + None + }; + + let success = target + .damage_with_context( + target.clone(), + amount, + damage_type, + None, + source.as_ref().map(|e| e.as_ref() as &dyn EntityBase), + cause.as_ref().map(|e| e.as_ref() as &dyn EntityBase), ) .await; - return Ok(()); - }; - let damage_type = args - .get(ARG_DAMAGE_TYPE) - .map_or(DamageType::GENERIC, |arg| match arg { - Arg::DamageType(dt) => *dt, - _ => DamageType::GENERIC, - }); + send_damage_result(sender, success, amount, target.get_display_name().await).await; - let source = EntityArgumentConsumer::find_arg(args, ARG_ENTITY).ok(); - let cause = if self.0 { - EntityArgumentConsumer::find_arg(args, ARG_CAUSE).ok() - } else { - None - }; - - let success = target - .damage_with_context( - target.clone(), - amount, - damage_type, - None, - source.as_ref().map(|e| e.as_ref() as &dyn EntityBase), - cause.as_ref().map(|e| e.as_ref() as &dyn EntityBase), - ) - .await; - - send_damage_result(sender, success, amount, target.get_display_name().await).await; - - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/data.rs b/pumpkin/src/command/commands/data.rs index dd4772c2e..101496e78 100644 --- a/pumpkin/src/command/commands/data.rs +++ b/pumpkin/src/command/commands/data.rs @@ -1,3 +1,4 @@ +use crate::command::CommandResult; use crate::command::args::entity::EntityArgumentConsumer; use crate::command::tree::builder::literal; use crate::command::{ @@ -7,7 +8,6 @@ use crate::command::{ }; use crate::entity::NBTStorage; use CommandError::InvalidConsumption; -use async_trait::async_trait; use pumpkin_nbt::compound::NbtCompound; use pumpkin_nbt::tag::NbtTag; use pumpkin_util::text::TextComponent; @@ -20,23 +20,24 @@ const ARG_ENTITY: &str = "entity"; struct GetEntityDataExecutor; -#[async_trait] impl CommandExecutor for GetEntityDataExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Entity(entity)) = args.get(&ARG_ENTITY) else { - return Err(InvalidConsumption(Some(ARG_ENTITY.into()))); - }; - let data_storage = entity.as_nbt_storage(); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Entity(entity)) = args.get(&ARG_ENTITY) else { + return Err(InvalidConsumption(Some(ARG_ENTITY.into()))); + }; + let data_storage = entity.as_nbt_storage(); - sender - .send_message(display_data(data_storage, entity.get_display_name().await).await?) - .await; - Ok(()) + sender + .send_message(display_data(data_storage, entity.get_display_name().await).await?) + .await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/defaultgamemode.rs b/pumpkin/src/command/commands/defaultgamemode.rs index ab6122172..10ccba844 100644 --- a/pumpkin/src/command/commands/defaultgamemode.rs +++ b/pumpkin/src/command/commands/defaultgamemode.rs @@ -1,11 +1,9 @@ +use crate::command::CommandResult; use crate::command::args::gamemode::GamemodeArgumentConsumer; use crate::command::args::{Arg, GetCloned}; use crate::command::dispatcher::CommandError::InvalidConsumption; use crate::command::tree::builder::argument; -use crate::command::{ - CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree, -}; -use async_trait::async_trait; +use crate::command::{CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree}; use pumpkin_config::BASIC_CONFIG; use pumpkin_util::GameMode; use pumpkin_util::text::TextComponent; @@ -22,38 +20,39 @@ pub struct DefaultGamemode { struct DefaultGamemodeExecutor; -#[async_trait] impl CommandExecutor for DefaultGamemodeExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else { - return Err(InvalidConsumption(Some(ARG_GAMEMODE.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else { + return Err(InvalidConsumption(Some(ARG_GAMEMODE.into()))); + }; - if BASIC_CONFIG.force_gamemode { - for player in server.get_all_players().await { - player.set_gamemode(gamemode).await; + if BASIC_CONFIG.force_gamemode { + for player in server.get_all_players().await { + player.set_gamemode(gamemode).await; + } } - } - let gamemode_string = format!("{gamemode:?}").to_lowercase(); - let gamemode_string = format!("gameMode.{gamemode_string}"); + let gamemode_string = format!("{gamemode:?}").to_lowercase(); + let gamemode_string = format!("gameMode.{gamemode_string}"); - sender - .send_message(TextComponent::translate( - "commands.defaultgamemode.success", - [TextComponent::translate(gamemode_string, [])], - )) - .await; + sender + .send_message(TextComponent::translate( + "commands.defaultgamemode.success", + [TextComponent::translate(gamemode_string, [])], + )) + .await; - //Change the default gamemode (not in configuration.toml) - server.defaultgamemode.lock().await.gamemode = gamemode; + //Change the default gamemode (not in configuration.toml) + server.defaultgamemode.lock().await.gamemode = gamemode; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/deop.rs b/pumpkin/src/command/commands/deop.rs index 4ce69afc1..6609cb2bd 100644 --- a/pumpkin/src/command/commands/deop.rs +++ b/pumpkin/src/command/commands/deop.rs @@ -1,3 +1,4 @@ +use crate::command::CommandResult; use crate::entity::EntityBase; use crate::{ command::{ @@ -9,7 +10,6 @@ use crate::{ data::{SaveJSONConfiguration, op_data::OPERATOR_CONFIG}, }; use CommandError::InvalidConsumption; -use async_trait::async_trait; use pumpkin_util::text::TextComponent; const NAMES: [&str; 1] = ["deop"]; @@ -18,44 +18,45 @@ const ARG_TARGETS: &str = "targets"; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let mut config = OPERATOR_CONFIG.write().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let mut config = OPERATOR_CONFIG.write().await; - let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { - return Err(InvalidConsumption(Some(ARG_TARGETS.into()))); - }; - - for player in targets { - if let Some(op_index) = config - .ops - .iter() - .position(|o| o.uuid == player.gameprofile.id) - { - config.ops.remove(op_index); - } - config.save(); - - { - let command_dispatcher = server.command_dispatcher.read().await; - player - .set_permission_lvl(pumpkin_util::PermissionLvl::Zero, &command_dispatcher) - .await; + let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { + return Err(InvalidConsumption(Some(ARG_TARGETS.into()))); }; - let msg = TextComponent::translate( - "commands.deop.success", - [player.get_display_name().await], - ); - sender.send_message(msg).await; - } - Ok(()) + for player in targets { + if let Some(op_index) = config + .ops + .iter() + .position(|o| o.uuid == player.gameprofile.id) + { + config.ops.remove(op_index); + } + config.save(); + + { + let command_dispatcher = server.command_dispatcher.read().await; + player + .set_permission_lvl(pumpkin_util::PermissionLvl::Zero, &command_dispatcher) + .await; + }; + + let msg = TextComponent::translate( + "commands.deop.success", + [player.get_display_name().await], + ); + sender.send_message(msg).await; + } + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/difficulty.rs b/pumpkin/src/command/commands/difficulty.rs index 29efc19f4..78c66b752 100644 --- a/pumpkin/src/command/commands/difficulty.rs +++ b/pumpkin/src/command/commands/difficulty.rs @@ -1,11 +1,9 @@ +use crate::command::CommandResult; use crate::command::args::difficulty::DifficultyArgumentConsumer; use crate::command::args::{Arg, GetCloned}; use crate::command::dispatcher::CommandError::InvalidConsumption; use crate::command::tree::builder::argument; -use crate::command::{ - CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree, -}; -use async_trait::async_trait; +use crate::command::{CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree}; use pumpkin_util::text::TextComponent; @@ -16,45 +14,46 @@ const DESCRIPTION: &str = "Change the difficulty of the world."; pub const ARG_DIFFICULTY: &str = "difficulty"; struct DifficultyExecutor; -#[async_trait] impl CommandExecutor for DifficultyExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Difficulty(difficulty)) = args.get_cloned(&ARG_DIFFICULTY) else { - return Err(InvalidConsumption(Some(ARG_DIFFICULTY.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Difficulty(difficulty)) = args.get_cloned(&ARG_DIFFICULTY) else { + return Err(InvalidConsumption(Some(ARG_DIFFICULTY.into()))); + }; - let difficulty_string = format!("{difficulty:?}").to_lowercase(); - let translation_key = format!("options.difficulty.{difficulty_string}"); + let difficulty_string = format!("{difficulty:?}").to_lowercase(); + let translation_key = format!("options.difficulty.{difficulty_string}"); - { - let level_info = server.level_info.read().await; + { + let level_info = server.level_info.read().await; - if level_info.difficulty == difficulty { - sender - .send_message(TextComponent::translate( - "commands.difficulty.failure", - [TextComponent::translate(translation_key, [])], - )) - .await; - return Ok(()); + if level_info.difficulty == difficulty { + sender + .send_message(TextComponent::translate( + "commands.difficulty.failure", + [TextComponent::translate(translation_key, [])], + )) + .await; + return Ok(()); + } } - } - server.set_difficulty(difficulty, Some(true)).await; + server.set_difficulty(difficulty, Some(true)).await; - sender - .send_message(TextComponent::translate( - "commands.difficulty.success", - [TextComponent::translate(translation_key, [])], - )) - .await; + sender + .send_message(TextComponent::translate( + "commands.difficulty.success", + [TextComponent::translate(translation_key, [])], + )) + .await; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/effect.rs b/pumpkin/src/command/commands/effect.rs index 8b8ff4298..b02ae2279 100644 --- a/pumpkin/src/command/commands/effect.rs +++ b/pumpkin/src/command/commands/effect.rs @@ -4,14 +4,11 @@ use crate::command::args::bounded_num::BoundedNumArgumentConsumer; use crate::command::args::players::PlayersArgumentConsumer; use crate::command::args::resource::effect::EffectTypeArgumentConsumer; use crate::command::args::{Arg, ConsumedArgs, FindArgDefaultName}; -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::command::{CommandExecutor, CommandResult, CommandSender}; use crate::entity::EntityBase; -use crate::server::Server; -use async_trait::async_trait; use pumpkin_data::potion::Effect; use pumpkin_util::text::color::{Color, NamedColor}; @@ -41,218 +38,223 @@ enum Amplifier { struct GiveExecutor(Time, Amplifier, bool); -#[async_trait] impl CommandExecutor for GiveExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _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()))); - }; - - //duration is in tick, so * 20 (not for the infinite because -1*20 cause visual glitch) - let second = match self.0 { - Time::Base => 30 * 20, - Time::Specified => { - BoundedNumArgumentConsumer::new() - .name("seconds") - .min(1) - .max(1_000_000) - .find_arg_default_name(args)?? - * 20 - } - Time::Infinite => -1, - }; - - let amplifier: i32 = match self.1 { - Amplifier::Base => 0, - Amplifier::Specified => BoundedNumArgumentConsumer::new() - .name("amplifier") - .min(0) - .max(255) - .find_arg_default_name(args)??, - }; - - let mut hide_particles = self.2; - //if false -> parameter is referred - if !hide_particles { - let Some(Arg::Bool(hide_particle)) = args.get(ARG_HIDE_PARTICLE) else { - return Err(InvalidConsumption(Some(ARG_HIDE_PARTICLE.into()))); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + 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()))); }; - hide_particles = *hide_particle; - } + //duration is in tick, so * 20 (not for the infinite because -1*20 cause visual glitch) + let second = match self.0 { + Time::Base => 30 * 20, + Time::Specified => { + BoundedNumArgumentConsumer::new() + .name("seconds") + .min(1) + .max(1_000_000) + .find_arg_default_name(args)?? + * 20 + } + Time::Infinite => -1, + }; - let mut failed = 0; + let amplifier: i32 = match self.1 { + Amplifier::Base => 0, + Amplifier::Specified => BoundedNumArgumentConsumer::new() + .name("amplifier") + .min(0) + .max(255) + .find_arg_default_name(args)??, + }; - for target in targets { - if target.living_entity.has_effect(effect).await - && target - .living_entity - .get_effect(effect) - .await - .unwrap() - .amplifier - > amplifier as u8 - { - failed += 1; + let mut hide_particles = self.2; + //if false -> parameter is referred + if !hide_particles { + let Some(Arg::Bool(hide_particle)) = args.get(ARG_HIDE_PARTICLE) else { + return Err(InvalidConsumption(Some(ARG_HIDE_PARTICLE.into()))); + }; + + hide_particles = *hide_particle; + } + + let mut failed = 0; + + for target in targets { + if target.living_entity.has_effect(effect).await + && target + .living_entity + .get_effect(effect) + .await + .unwrap() + .amplifier + > amplifier as u8 + { + failed += 1; + } else { + target + .add_effect(Effect { + effect_type: effect, + duration: second, + amplifier: amplifier as u8, + ambient: false, //this is not a beacon effect + show_particles: hide_particles, + show_icon: true, + blend: true, //Currently only used in the DARKNESS effect to apply extra void fog and adjust the gamma value for lighting. + }) + .await; + } + } + + let translation_name = TextComponent::translate(effect.translation_key.to_string(), []); + + if failed == targets.len() { + sender + .send_message( + TextComponent::translate("commands.effect.give.failed", []) + .color(Color::Named(NamedColor::Red)), + ) + .await; + } else if targets.len() == 1 { + sender + .send_message(TextComponent::translate( + "commands.effect.give.success.single", + [translation_name, targets[0].get_display_name().await], + )) + .await; } else { - target - .add_effect(Effect { - effect_type: effect, - duration: second, - amplifier: amplifier as u8, - ambient: false, //this is not a beacon effect - show_particles: hide_particles, - show_icon: true, - blend: true, //Currently only used in the DARKNESS effect to apply extra void fog and adjust the gamma value for lighting. - }) + sender + .send_message(TextComponent::translate( + "commands.effect.give.success.multiple", + [ + translation_name, + TextComponent::text(targets.len().to_string()), + ], + )) .await; } - } - let translation_name = TextComponent::translate(effect.translation_key.to_string(), []); - - if failed == targets.len() { - sender - .send_message( - TextComponent::translate("commands.effect.give.failed", []) - .color(Color::Named(NamedColor::Red)), - ) - .await; - } else if targets.len() == 1 { - sender - .send_message(TextComponent::translate( - "commands.effect.give.success.single", - [translation_name, targets[0].get_display_name().await], - )) - .await; - } else { - sender - .send_message(TextComponent::translate( - "commands.effect.give.success.multiple", - [ - translation_name, - TextComponent::text(targets.len().to_string()), - ], - )) - .await; - } - - Ok(()) + Ok(()) + }) } } struct ClearExecutor(bool); //the param -> true = delete every effect, false = only one -#[async_trait] impl CommandExecutor for ClearExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _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 effect; - //Only one effect - if self.0 { - let mut effect_number = 0; - for target in targets { - let effect_number_temp = target.remove_all_effect().await; - if effect_number_temp > effect_number { - effect_number = effect_number_temp; - } - } - - //if the player or everyplayer don't have any effect - if effect_number == 0 { - sender - .send_message( - TextComponent::translate("commands.effect.clear.everything.failed", []) - .color(Color::Named(NamedColor::Red)), - ) - .await; - } - //a player have at least 1 effect - else if targets.len() == 1 { - sender - .send_message(TextComponent::translate( - "commands.effect.clear.everything.success.single", - [targets[0].get_display_name().await], - )) - .await; - } else { - sender - .send_message(TextComponent::translate( - "commands.effect.clear.everything.success.multiple", - [TextComponent::text(targets.len().to_string())], - )) - .await; - } - } else { - let Some(Arg::Effect(effect_type)) = args.get(ARG_EFFECT) else { - return Err(InvalidConsumption(Some(ARG_EFFECT.into()))); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Players(targets)) = args.get(ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); }; - effect = *effect_type; - let mut has_effect = vec![]; - - for target in targets { - if !target.living_entity.has_effect(effect).await { - target.remove_effect(effect).await; + let effect; + //Only one effect + if self.0 { + let mut effect_number = 0; + for target in targets { + let effect_number_temp = target.remove_all_effect().await; + if effect_number_temp > effect_number { + effect_number = effect_number_temp; + } } - has_effect.push(target.living_entity.has_effect(effect).await); - } - if has_effect.contains(&false) { - //contain false for 1 player == don't have - if targets.len() == 1 || !has_effect.contains(&true) { + //if the player or everyplayer don't have any effect + if effect_number == 0 { sender .send_message( - TextComponent::translate("commands.effect.clear.specific.failed", []) + TextComponent::translate("commands.effect.clear.everything.failed", []) .color(Color::Named(NamedColor::Red)), ) .await; } - } else { - //true for 1 player = have the effect - if targets.len() == 1 { + //a player have at least 1 effect + else if targets.len() == 1 { sender .send_message(TextComponent::translate( - "commands.effect.clear.specific.success.single", - [ - TextComponent::translate(effect.translation_key, []), - targets[0].get_display_name().await, - ], + "commands.effect.clear.everything.success.single", + [targets[0].get_display_name().await], + )) + .await; + } else { + sender + .send_message(TextComponent::translate( + "commands.effect.clear.everything.success.multiple", + [TextComponent::text(targets.len().to_string())], )) .await; } - //contain true for everyplayer = at least 1 player have the effect - else { - sender - .send_message(TextComponent::translate( - "commands.effect.clear.specific.success.multiple", - [ - TextComponent::translate(effect.translation_key, []), - TextComponent::text(targets.len().to_string()), - ], - )) - .await; + } else { + let Some(Arg::Effect(effect_type)) = args.get(ARG_EFFECT) else { + return Err(InvalidConsumption(Some(ARG_EFFECT.into()))); + }; + + effect = *effect_type; + let mut has_effect = vec![]; + + for target in targets { + if !target.living_entity.has_effect(effect).await { + target.remove_effect(effect).await; + } + has_effect.push(target.living_entity.has_effect(effect).await); + } + + if has_effect.contains(&false) { + //contain false for 1 player == don't have + if targets.len() == 1 || !has_effect.contains(&true) { + sender + .send_message( + TextComponent::translate( + "commands.effect.clear.specific.failed", + [], + ) + .color(Color::Named(NamedColor::Red)), + ) + .await; + } + } else { + //true for 1 player = have the effect + if targets.len() == 1 { + sender + .send_message(TextComponent::translate( + "commands.effect.clear.specific.success.single", + [ + TextComponent::translate(effect.translation_key, []), + targets[0].get_display_name().await, + ], + )) + .await; + } + //contain true for everyplayer = at least 1 player have the effect + else { + sender + .send_message(TextComponent::translate( + "commands.effect.clear.specific.success.multiple", + [ + TextComponent::translate(effect.translation_key, []), + TextComponent::text(targets.len().to_string()), + ], + )) + .await; + } } } - } - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/enchant.rs b/pumpkin/src/command/commands/enchant.rs index 9c24a49f2..3651a74db 100644 --- a/pumpkin/src/command/commands/enchant.rs +++ b/pumpkin/src/command/commands/enchant.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_util::text::TextComponent; use crate::command::args::bounded_num::{BoundedNumArgumentConsumer, NotInBounds}; @@ -7,7 +6,7 @@ use crate::command::args::resource::enchantment::EnchantmentArgumentConsumer; use crate::command::args::{ConsumedArgs, FindArgDefaultName}; use crate::command::tree::CommandTree; use crate::command::tree::builder::argument_default_name; -use crate::command::{CommandError, CommandExecutor, CommandSender}; +use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender}; use pumpkin_data::data_component_impl::EnchantmentsImpl; const NAMES: [&str; 1] = ["enchant"]; @@ -15,134 +14,135 @@ const DESCRIPTION: &str = "Adds an enchantment to a player's selected item, subj struct Executor; -#[async_trait] impl CommandExecutor for Executor { #[allow(clippy::too_many_lines)] - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let targets = EntitiesArgumentConsumer.find_arg_default_name(args)?; - let enchantment = EnchantmentArgumentConsumer.find_arg_default_name(args)?; - let level = match enchantment_level_consumer().find_arg_default_name(args) { - Err(_) => 1, - Ok(Ok(level)) => level, - Ok(Err(err)) => { - let err_msg = match err { - NotInBounds::LowerBound(val, min) => TextComponent::translate( - "argument.integer.low", - &[ - TextComponent::text(min.to_string()), - TextComponent::text(val.to_string()), - ], - ), - NotInBounds::UpperBound(val, max) => TextComponent::translate( - "argument.integer.big", - &[ - TextComponent::text(max.to_string()), - TextComponent::text(val.to_string()), - ], - ), - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let targets = EntitiesArgumentConsumer.find_arg_default_name(args)?; + let enchantment = EnchantmentArgumentConsumer.find_arg_default_name(args)?; + let level = match enchantment_level_consumer().find_arg_default_name(args) { + Err(_) => 1, + Ok(Ok(level)) => level, + Ok(Err(err)) => { + let err_msg = match err { + NotInBounds::LowerBound(val, min) => TextComponent::translate( + "argument.integer.low", + &[ + TextComponent::text(min.to_string()), + TextComponent::text(val.to_string()), + ], + ), + NotInBounds::UpperBound(val, max) => TextComponent::translate( + "argument.integer.big", + &[ + TextComponent::text(max.to_string()), + TextComponent::text(val.to_string()), + ], + ), + }; - return Err(CommandError::CommandFailed(Box::new(err_msg))); - } - }; - - if level > enchantment.max_level { - let msg = TextComponent::translate( - "commands.enchant.failed.level", - [ - TextComponent::text(level.to_string()), - TextComponent::text(enchantment.max_level.to_string()), - ], - ); - return Err(CommandError::CommandFailed(Box::new(msg))); - } - - let only_one = targets.len() == 1; - let mut success = 0; - - for target in targets { - // let Some(target) = target.get_living_entity() else { - // if only_one { - // let msg = TextComponent::translate( - // "commands.enchant.failed.entity", - // [targets[0].get_display_name().await], - // ); - // sender.send_message(msg).await; - // return Ok(()); - // } - // continue; - // }; - // let lock = target.entity_equipment.lock().await.get(&EquipmentSlot::MAIN_HAND); TODO this dont work - let Some(player) = target.get_player() else { - todo!() + return Err(CommandError::CommandFailed(Box::new(err_msg))); + } }; - let lock = player.inventory.held_item(); - let mut item = lock.lock().await; - if item.is_empty() { - if only_one { - let msg = TextComponent::translate( - "commands.enchant.failed.itemless", - [targets[0].get_display_name().await], - ); - return Err(CommandError::CommandFailed(Box::new(msg))); - } - continue; + + if level > enchantment.max_level { + let msg = TextComponent::translate( + "commands.enchant.failed.level", + [ + TextComponent::text(level.to_string()), + TextComponent::text(enchantment.max_level.to_string()), + ], + ); + return Err(CommandError::CommandFailed(Box::new(msg))); } - if !enchantment.can_enchant(item.item) { - if only_one { - let msg = TextComponent::translate( - "commands.enchant.failed.incompatible", - [item.item.translated_name()], - ); - return Err(CommandError::CommandFailed(Box::new(msg))); + + let only_one = targets.len() == 1; + let mut success = 0; + + for target in targets { + // let Some(target) = target.get_living_entity() else { + // if only_one { + // let msg = TextComponent::translate( + // "commands.enchant.failed.entity", + // [targets[0].get_display_name().await], + // ); + // sender.send_message(msg).await; + // return Ok(()); + // } + // continue; + // }; + // let lock = target.entity_equipment.lock().await.get(&EquipmentSlot::MAIN_HAND); TODO this dont work + let Some(player) = target.get_player() else { + todo!() + }; + let lock = player.inventory.held_item(); + let mut item = lock.lock().await; + if item.is_empty() { + if only_one { + let msg = TextComponent::translate( + "commands.enchant.failed.itemless", + [targets[0].get_display_name().await], + ); + return Err(CommandError::CommandFailed(Box::new(msg))); + } + continue; } - continue; - } - if let Some(data) = item.get_data_component::() { - if enchantment.is_enchantment_compatible(data) { + if !enchantment.can_enchant(item.item) { + if only_one { + let msg = TextComponent::translate( + "commands.enchant.failed.incompatible", + [item.item.translated_name()], + ); + return Err(CommandError::CommandFailed(Box::new(msg))); + } + continue; + } + if let Some(data) = item.get_data_component::() { + if enchantment.is_enchantment_compatible(data) { + item.enchant(enchantment, level); + success += 1; + } else if only_one { + let msg = TextComponent::translate( + "commands.enchant.failed.incompatible", + [item.item.translated_name()], + ); + return Err(CommandError::CommandFailed(Box::new(msg))); + } + } else { item.enchant(enchantment, level); success += 1; - } else if only_one { - let msg = TextComponent::translate( - "commands.enchant.failed.incompatible", - [item.item.translated_name()], - ); - return Err(CommandError::CommandFailed(Box::new(msg))); } - } else { - item.enchant(enchantment, level); - success += 1; } - } - if success == 0 { - let msg = TextComponent::translate("commands.enchant.failed", []); - return Err(CommandError::CommandFailed(Box::new(msg))); - } - if only_one { - let msg = TextComponent::translate( - "commands.enchant.success.single", - [ - enchantment.get_fullname(level), - targets[0].get_display_name().await, - ], - ); - sender.send_message(msg).await; - } else { - let msg = TextComponent::translate( - "commands.enchant.success.multiple", - [ - enchantment.get_fullname(level), - TextComponent::text(targets.len().to_string()), - ], - ); - sender.send_message(msg).await; - } - Ok(()) + if success == 0 { + let msg = TextComponent::translate("commands.enchant.failed", []); + return Err(CommandError::CommandFailed(Box::new(msg))); + } + if only_one { + let msg = TextComponent::translate( + "commands.enchant.success.single", + [ + enchantment.get_fullname(level), + targets[0].get_display_name().await, + ], + ); + sender.send_message(msg).await; + } else { + let msg = TextComponent::translate( + "commands.enchant.success.multiple", + [ + enchantment.get_fullname(level), + TextComponent::text(targets.len().to_string()), + ], + ); + sender.send_message(msg).await; + } + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/experience.rs b/pumpkin/src/command/commands/experience.rs index 7f056f4ce..e7f15ad84 100644 --- a/pumpkin/src/command/commands/experience.rs +++ b/pumpkin/src/command/commands/experience.rs @@ -1,6 +1,5 @@ use std::sync::atomic::Ordering; -use async_trait::async_trait; use pumpkin_util::math::experience; use pumpkin_util::text::TextComponent; use pumpkin_util::text::color::{Color, NamedColor}; @@ -10,7 +9,7 @@ use crate::command::args::players::PlayersArgumentConsumer; use crate::command::args::{ConsumedArgs, FindArg}; use crate::command::tree::CommandTree; use crate::command::tree::builder::{argument, literal}; -use crate::command::{CommandError, CommandExecutor, CommandSender}; +use crate::command::{CommandExecutor, CommandResult, CommandSender}; use crate::entity::EntityBase; use crate::entity::player::Player; @@ -196,76 +195,77 @@ impl Executor { } } -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let targets = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS)?; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let targets = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS)?; - match self.mode { - Mode::Query => { - if targets.len() != 1 { - // TODO: Add proper error message for multiple players in query mode - return Ok(()); - } - self.handle_query(sender, &targets[0], self.exp_type.unwrap()) - .await; - } - Mode::Add | Mode::Set => { - let Ok(amount) = BoundedNumArgumentConsumer::::find_arg(args, ARG_AMOUNT)? - else { - sender - .send_message(TextComponent::translate( - "commands.experience.set.points.invalid", - [], - )) + match self.mode { + Mode::Query => { + if targets.len() != 1 { + // TODO: Add proper error message for multiple players in query mode + return Ok(()); + } + self.handle_query(sender, &targets[0], self.exp_type.unwrap()) .await; - return Ok(()); - }; - - if self.mode == Mode::Set && amount < 0 { - sender - .send_message(TextComponent::translate( - "commands.experience.set.points.invalid", - [], - )) - .await; - return Ok(()); } + Mode::Add | Mode::Set => { + let Ok(amount) = BoundedNumArgumentConsumer::::find_arg(args, ARG_AMOUNT)? + else { + sender + .send_message(TextComponent::translate( + "commands.experience.set.points.invalid", + [], + )) + .await; + return Ok(()); + }; - for target in targets { - match self - .handle_modify(target, amount, self.exp_type.unwrap(), self.mode) - .await - { - Ok(()) => { - let msg = Self::get_success_message( - self.mode, - self.exp_type.unwrap(), - amount, - targets.len(), - Some(target.get_display_name().await), - ); - sender.send_message(msg).await; - } - Err(error_msg) => { - sender - .send_message( - TextComponent::translate(error_msg, []) - .color(Color::Named(NamedColor::Red)), - ) - .await; + if self.mode == Mode::Set && amount < 0 { + sender + .send_message(TextComponent::translate( + "commands.experience.set.points.invalid", + [], + )) + .await; + return Ok(()); + } + + for target in targets { + match self + .handle_modify(target, amount, self.exp_type.unwrap(), self.mode) + .await + { + Ok(()) => { + let msg = Self::get_success_message( + self.mode, + self.exp_type.unwrap(), + amount, + targets.len(), + Some(target.get_display_name().await), + ); + sender.send_message(msg).await; + } + Err(error_msg) => { + sender + .send_message( + TextComponent::translate(error_msg, []) + .color(Color::Named(NamedColor::Red)), + ) + .await; + } } } } } - } - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/fill.rs b/pumpkin/src/command/commands/fill.rs index 578913149..5c6cacf57 100644 --- a/pumpkin/src/command/commands/fill.rs +++ b/pumpkin/src/command/commands/fill.rs @@ -5,9 +5,8 @@ use crate::command::args::position_block::BlockPosArgumentConsumer; use crate::command::args::{ConsumedArgs, FindArg}; use crate::command::tree::CommandTree; use crate::command::tree::builder::{argument, literal}; -use crate::command::{CommandError, CommandExecutor, CommandSender}; +use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender}; -use async_trait::async_trait; use pumpkin_data::Block; use pumpkin_util::math::position::BlockPos; use pumpkin_util::math::vector3::Vector3; @@ -50,94 +49,69 @@ fn not_in_filter(filter: &BlockPredicate, old_block: &Block) -> bool { } #[expect(clippy::too_many_lines)] -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?; - let block_state_id = block.default_state.id; - let from = BlockPosArgumentConsumer::find_arg(args, ARG_FROM)?; - let to = BlockPosArgumentConsumer::find_arg(args, ARG_TO)?; - let option_filter = BlockPredicateArgumentConsumer::find_arg(args, ARG_FILTER)?; - let mode = self.0; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?; + let block_state_id = block.default_state.id; + let from = BlockPosArgumentConsumer::find_arg(args, ARG_FROM)?; + let to = BlockPosArgumentConsumer::find_arg(args, ARG_TO)?; + let option_filter = BlockPredicateArgumentConsumer::find_arg(args, ARG_FILTER)?; + let mode = self.0; - let start_x = from.0.x.min(to.0.x); - let start_y = from.0.y.min(to.0.y); - let start_z = from.0.z.min(to.0.z); + let start_x = from.0.x.min(to.0.x); + let start_y = from.0.y.min(to.0.y); + let start_z = from.0.z.min(to.0.z); - let end_x = from.0.x.max(to.0.x); - let end_y = from.0.y.max(to.0.y); - let end_z = from.0.z.max(to.0.z); - // TODO: check isInWorldBounds and throw argument.pos.outofbounds + let end_x = from.0.x.max(to.0.x); + let end_y = from.0.y.max(to.0.y); + let end_z = from.0.z.max(to.0.z); + // TODO: check isInWorldBounds and throw argument.pos.outofbounds - let world = sender.world().ok_or(CommandError::InvalidRequirement)?; - let mut placed_blocks = 0; - let mut to_update = Vec::new(); - match mode { - Mode::Destroy => { - for x in start_x..=end_x { - for y in start_y..=end_y { - for z in start_z..=end_z { - let block_position = BlockPos(Vector3::new(x, y, z)); - if let Some(filter) = &option_filter - && not_in_filter(filter, world.get_block(&block_position).await) - { - continue; + let world = sender.world().ok_or(CommandError::InvalidRequirement)?; + let mut placed_blocks = 0; + let mut to_update = Vec::new(); + match mode { + Mode::Destroy => { + for x in start_x..=end_x { + for y in start_y..=end_y { + for z in start_z..=end_z { + let block_position = BlockPos(Vector3::new(x, y, z)); + if let Some(filter) = &option_filter + && not_in_filter(filter, world.get_block(&block_position).await) + { + continue; + } + world + .break_block( + &block_position, + None, + BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE, + ) + .await; + world + .set_block_state( + &block_position, + block_state_id, + BlockFlags::FORCE_STATE, + ) + .await; + placed_blocks += 1; + to_update.push(block_position); } - world - .break_block( - &block_position, - None, - BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE, - ) - .await; - world - .set_block_state( - &block_position, - block_state_id, - BlockFlags::FORCE_STATE, - ) - .await; - placed_blocks += 1; - to_update.push(block_position); } } } - } - Mode::Replace => { - for x in start_x..=end_x { - for y in start_y..=end_y { - for z in start_z..=end_z { - let block_position = BlockPos(Vector3::new(x, y, z)); - if let Some(filter) = &option_filter - && not_in_filter(filter, world.get_block(&block_position).await) - { - continue; - } - world - .set_block_state( - &block_position, - block_state_id, - BlockFlags::FORCE_STATE, - ) - .await; - placed_blocks += 1; - to_update.push(block_position); - } - } - } - } - Mode::Keep => { - for x in start_x..=end_x { - for y in start_y..=end_y { - for z in start_z..=end_z { - let block_position = BlockPos(Vector3::new(x, y, z)); - let old_state = world.get_block_state(&block_position).await; - if old_state.is_air() { + Mode::Replace => { + for x in start_x..=end_x { + for y in start_y..=end_y { + for z in start_z..=end_z { + let block_position = BlockPos(Vector3::new(x, y, z)); if let Some(filter) = &option_filter && not_in_filter(filter, world.get_block(&block_position).await) { @@ -156,24 +130,93 @@ impl CommandExecutor for Executor { } } } - } - Mode::Hollow => { - for x in start_x..=end_x { - for y in start_y..=end_y { - for z in start_z..=end_z { - let block_position = BlockPos(Vector3::new(x, y, z)); - let is_edge = x == start_x - || x == end_x - || y == start_y - || y == end_y - || z == start_z - || z == end_z; - if let Some(filter) = &option_filter - && not_in_filter(filter, world.get_block(&block_position).await) - { - continue; + Mode::Keep => { + for x in start_x..=end_x { + for y in start_y..=end_y { + for z in start_z..=end_z { + let block_position = BlockPos(Vector3::new(x, y, z)); + let old_state = world.get_block_state(&block_position).await; + if old_state.is_air() { + if let Some(filter) = &option_filter + && not_in_filter( + filter, + world.get_block(&block_position).await, + ) + { + continue; + } + world + .set_block_state( + &block_position, + block_state_id, + BlockFlags::FORCE_STATE, + ) + .await; + placed_blocks += 1; + to_update.push(block_position); + } } - if is_edge { + } + } + } + Mode::Hollow => { + for x in start_x..=end_x { + for y in start_y..=end_y { + for z in start_z..=end_z { + let block_position = BlockPos(Vector3::new(x, y, z)); + let is_edge = x == start_x + || x == end_x + || y == start_y + || y == end_y + || z == start_z + || z == end_z; + if let Some(filter) = &option_filter + && not_in_filter(filter, world.get_block(&block_position).await) + { + continue; + } + if is_edge { + world + .set_block_state( + &block_position, + block_state_id, + BlockFlags::FORCE_STATE, + ) + .await; + } else { + world + .set_block_state( + &block_position, + 0, + BlockFlags::FORCE_STATE, + ) + .await; + } + placed_blocks += 1; + to_update.push(block_position); + } + } + } + } + Mode::Outline => { + for x in start_x..=end_x { + for y in start_y..=end_y { + for z in start_z..=end_z { + let block_position = BlockPos(Vector3::new(x, y, z)); + let is_edge = x == start_x + || x == end_x + || y == start_y + || y == end_y + || z == start_z + || z == end_z; + if !is_edge { + continue; + } + if let Some(filter) = &option_filter + && not_in_filter(filter, world.get_block(&block_position).await) + { + continue; + } world .set_block_state( &block_position, @@ -181,85 +224,49 @@ impl CommandExecutor for Executor { BlockFlags::FORCE_STATE, ) .await; - } else { + placed_blocks += 1; + to_update.push(block_position); + } + } + } + } + Mode::Strict => { + for x in start_x..=end_x { + for y in start_y..=end_y { + for z in start_z..=end_z { + let block_position = BlockPos(Vector3::new(x, y, z)); + if let Some(filter) = &option_filter + && not_in_filter(filter, world.get_block(&block_position).await) + { + continue; + } world - .set_block_state(&block_position, 0, BlockFlags::FORCE_STATE) + .set_block_state( + &block_position, + block_state_id, + BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, + ) .await; + placed_blocks += 1; } - placed_blocks += 1; - to_update.push(block_position); } } } } - Mode::Outline => { - for x in start_x..=end_x { - for y in start_y..=end_y { - for z in start_z..=end_z { - let block_position = BlockPos(Vector3::new(x, y, z)); - let is_edge = x == start_x - || x == end_x - || y == start_y - || y == end_y - || z == start_z - || z == end_z; - if !is_edge { - continue; - } - if let Some(filter) = &option_filter - && not_in_filter(filter, world.get_block(&block_position).await) - { - continue; - } - world - .set_block_state( - &block_position, - block_state_id, - BlockFlags::FORCE_STATE, - ) - .await; - placed_blocks += 1; - to_update.push(block_position); - } - } - } + + for i in to_update { + world.update_neighbors(&i, None).await; } - Mode::Strict => { - for x in start_x..=end_x { - for y in start_y..=end_y { - for z in start_z..=end_z { - let block_position = BlockPos(Vector3::new(x, y, z)); - if let Some(filter) = &option_filter - && not_in_filter(filter, world.get_block(&block_position).await) - { - continue; - } - world - .set_block_state( - &block_position, - block_state_id, - BlockFlags::SKIP_BLOCK_ADDED_CALLBACK, - ) - .await; - placed_blocks += 1; - } - } - } - } - } - for i in to_update { - world.update_neighbors(&i, None).await; - } + sender + .send_message(TextComponent::translate( + "commands.fill.success", + [TextComponent::text(placed_blocks.to_string())], + )) + .await; - sender - .send_message(TextComponent::translate( - "commands.fill.success", - [TextComponent::text(placed_blocks.to_string())], - )) - .await; - - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/gamemode.rs b/pumpkin/src/command/commands/gamemode.rs index 4cefa077c..96fa07a0f 100644 --- a/pumpkin/src/command/commands/gamemode.rs +++ b/pumpkin/src/command/commands/gamemode.rs @@ -1,5 +1,3 @@ -use async_trait::async_trait; - use crate::command::args::GetCloned; use crate::command::args::gamemode::GamemodeArgumentConsumer; @@ -9,13 +7,11 @@ use crate::command::args::players::PlayersArgumentConsumer; use crate::command::CommandSender::Player; use crate::command::args::{Arg, ConsumedArgs}; -use crate::command::dispatcher::CommandError; use crate::command::dispatcher::CommandError::{InvalidConsumption, InvalidRequirement}; use crate::command::tree::CommandTree; use crate::command::tree::builder::{argument, require}; -use crate::command::{CommandExecutor, CommandSender}; +use crate::command::{CommandExecutor, CommandResult, CommandSender}; use crate::entity::EntityBase; -use crate::server::Server; const NAMES: [&str; 1] = ["gamemode"]; @@ -26,82 +22,84 @@ const ARG_TARGET: &str = "target"; struct TargetSelfExecutor; -#[async_trait] impl CommandExecutor for TargetSelfExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else { - return Err(InvalidConsumption(Some(ARG_GAMEMODE.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else { + return Err(InvalidConsumption(Some(ARG_GAMEMODE.into()))); + }; - if let Player(target) = sender { - if target.gamemode.load() != gamemode { - target.set_gamemode(gamemode).await; - let gamemode_string = format!("{gamemode:?}").to_lowercase(); - let gamemode_string = format!("gameMode.{gamemode_string}"); - target - .send_system_message(&TextComponent::translate( - "commands.gamemode.success.self", - [TextComponent::translate(gamemode_string, [])], - )) - .await; + if let Player(target) = sender { + if target.gamemode.load() != gamemode { + target.set_gamemode(gamemode).await; + let gamemode_string = format!("{gamemode:?}").to_lowercase(); + let gamemode_string = format!("gameMode.{gamemode_string}"); + target + .send_system_message(&TextComponent::translate( + "commands.gamemode.success.self", + [TextComponent::translate(gamemode_string, [])], + )) + .await; + } + Ok(()) + } else { + Err(InvalidRequirement) } - Ok(()) - } else { - Err(InvalidRequirement) - } + }) } } struct TargetPlayerExecutor; -#[async_trait] impl CommandExecutor for TargetPlayerExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else { - return Err(InvalidConsumption(Some(ARG_GAMEMODE.into()))); - }; - let Some(Arg::Players(targets)) = args.get(ARG_TARGET) else { - return Err(InvalidConsumption(Some(ARG_TARGET.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::GameMode(gamemode)) = args.get_cloned(&ARG_GAMEMODE) else { + return Err(InvalidConsumption(Some(ARG_GAMEMODE.into()))); + }; + let Some(Arg::Players(targets)) = args.get(ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); + }; - let target_count = targets.len(); + let target_count = targets.len(); - for target in targets { - if target.gamemode.load() != gamemode { - target.set_gamemode(gamemode).await; - let gamemode_string = format!("{gamemode:?}").to_lowercase(); - let gamemode_string = format!("gameMode.{gamemode_string}"); - target - .send_system_message(&TextComponent::translate( - "gameMode.changed", - [TextComponent::translate(gamemode_string.clone(), [])], - )) - .await; - if target_count == 1 { - sender - .send_message(TextComponent::translate( - "commands.gamemode.success.other", - [ - target.get_display_name().await, - TextComponent::translate(gamemode_string, []), - ], + for target in targets { + if target.gamemode.load() != gamemode { + target.set_gamemode(gamemode).await; + let gamemode_string = format!("{gamemode:?}").to_lowercase(); + let gamemode_string = format!("gameMode.{gamemode_string}"); + target + .send_system_message(&TextComponent::translate( + "gameMode.changed", + [TextComponent::translate(gamemode_string.clone(), [])], )) .await; + if target_count == 1 { + sender + .send_message(TextComponent::translate( + "commands.gamemode.success.other", + [ + target.get_display_name().await, + TextComponent::translate(gamemode_string, []), + ], + )) + .await; + } } } - } - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/gamerule.rs b/pumpkin/src/command/commands/gamerule.rs index ad4b68a42..26a89e1a8 100644 --- a/pumpkin/src/command/commands/gamerule.rs +++ b/pumpkin/src/command/commands/gamerule.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::game_rules::{GameRule, GameRuleRegistry, GameRuleValue}; use crate::command::args::FindArg; @@ -8,11 +7,9 @@ use crate::command::args::bounded_num::BoundedNumArgumentConsumer; use crate::TextComponent; use crate::command::args::ConsumedArgs; -use crate::command::dispatcher::CommandError; use crate::command::tree::CommandTree; use crate::command::tree::builder::{argument, literal}; -use crate::command::{CommandExecutor, CommandSender}; -use crate::server::Server; +use crate::command::{CommandExecutor, CommandResult, CommandSender}; const NAMES: [&str; 1] = ["gamerule"]; @@ -22,64 +19,66 @@ const ARG_NAME: &str = "value"; struct QueryExecutor(GameRule); -#[async_trait] impl CommandExecutor for QueryExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let key = TextComponent::text(self.0.to_string()); - let level_info = server.level_info.read().await; - let value = TextComponent::text(level_info.game_rules.get(&self.0).to_string()); - drop(level_info); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let key = TextComponent::text(self.0.to_string()); + let level_info = server.level_info.read().await; + let value = TextComponent::text(level_info.game_rules.get(&self.0).to_string()); + drop(level_info); - sender - .send_message(TextComponent::translate( - "commands.gamerule.query", - [key, value], - )) - .await; - Ok(()) + sender + .send_message(TextComponent::translate( + "commands.gamerule.query", + [key, value], + )) + .await; + Ok(()) + }) } } struct SetExecutor(GameRule); -#[async_trait] impl CommandExecutor for SetExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let key = TextComponent::text(self.0.to_string()); - let mut level_info = server.level_info.write().await; - let raw_value = level_info.game_rules.get_mut(&self.0); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let key = TextComponent::text(self.0.to_string()); + let mut level_info = server.level_info.write().await; + let raw_value = level_info.game_rules.get_mut(&self.0); - let value = TextComponent::text(match raw_value { - GameRuleValue::Int(value) => { - let arg_value = BoundedNumArgumentConsumer::::find_arg(args, ARG_NAME)??; - *value = arg_value; - arg_value.to_string() - } - GameRuleValue::Bool(value) => { - let arg_value = BoolArgConsumer::find_arg(args, ARG_NAME)?; - *value = arg_value; - arg_value.to_string() - } - }); - drop(level_info); + let value = TextComponent::text(match raw_value { + GameRuleValue::Int(value) => { + let arg_value = BoundedNumArgumentConsumer::::find_arg(args, ARG_NAME)??; + *value = arg_value; + arg_value.to_string() + } + GameRuleValue::Bool(value) => { + let arg_value = BoolArgConsumer::find_arg(args, ARG_NAME)?; + *value = arg_value; + arg_value.to_string() + } + }); + drop(level_info); - sender - .send_message(TextComponent::translate( - "commands.gamerule.set", - [key, value], - )) - .await; - Ok(()) + sender + .send_message(TextComponent::translate( + "commands.gamerule.set", + [key, value], + )) + .await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/give.rs b/pumpkin/src/command/commands/give.rs index adedb20bc..904e47523 100644 --- a/pumpkin/src/command/commands/give.rs +++ b/pumpkin/src/command/commands/give.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::data_component::DataComponent::MaxStackSize; use pumpkin_data::data_component_impl::{MaxStackSizeImpl, get}; use pumpkin_util::text::TextComponent; @@ -12,7 +11,7 @@ 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}; -use crate::command::{CommandError, CommandExecutor, CommandSender}; +use crate::command::{CommandExecutor, CommandResult, CommandSender}; use crate::entity::EntityBase; const NAMES: [&str; 1] = ["give"]; @@ -30,93 +29,96 @@ fn item_count_consumer() -> BoundedNumArgumentConsumer { struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let targets = PlayersArgumentConsumer.find_arg_default_name(args)?; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let targets = PlayersArgumentConsumer.find_arg_default_name(args)?; - let (item_name, item) = ItemArgumentConsumer::find_arg(args, ARG_ITEM)?; + let (item_name, item) = ItemArgumentConsumer::find_arg(args, ARG_ITEM)?; - let item_count = match item_count_consumer().find_arg_default_name(args) { - Err(_) => 1, - Ok(Ok(count)) => count, - Ok(Err(err)) => { - let err_msg = match err { - NotInBounds::LowerBound(_, min) => { - format!("Can't give less than {min} of {item_name}") - } - NotInBounds::UpperBound(_, max) => { - format!("Can't give more than {max} of {item_name}") - } - }; + let item_count = match item_count_consumer().find_arg_default_name(args) { + Err(_) => 1, + Ok(Ok(count)) => count, + Ok(Err(err)) => { + let err_msg = match err { + NotInBounds::LowerBound(_, min) => { + format!("Can't give less than {min} of {item_name}") + } + NotInBounds::UpperBound(_, max) => { + format!("Can't give more than {max} of {item_name}") + } + }; - sender - .send_message(TextComponent::text(err_msg).color(Color::Named(NamedColor::Red))) - .await; - return Ok(()); - } - }; - - for target in targets { - let max_stack = i32::from( - item.components - .iter() - .find_map(|(id, component)| { - (id == &MaxStackSize).then(|| get::(*component).size) - }) - .unwrap(), - ); - let mut remaining = item_count; - - while remaining > 0 { - let take = remaining.min(max_stack); - let mut stack = ItemStack::new(take as u8, item); - target.inventory().insert_stack_anywhere(&mut stack).await; - if !stack.is_empty() { - target.drop_item(stack).await; + sender + .send_message( + TextComponent::text(err_msg).color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); } - remaining -= take; - } - } - let msg = if targets.len() == 1 { - TextComponent::translate( - "commands.give.success.single", - [ - TextComponent::text(item_count.to_string()), - TextComponent::text("[") - .add_child(item.translated_name()) - .add_child(TextComponent::text("]")) - .hover_event(HoverEvent::ShowItem { - id: item_name.to_string().into(), - count: Some(item_count), - }), - targets[0].get_display_name().await, - ], - ) - } else { - TextComponent::translate( - "commands.give.success.multiple", - [ - TextComponent::text(item_count.to_string()), - TextComponent::text("[") - .add_child(item.translated_name()) - .add_child(TextComponent::text("]")) - .hover_event(HoverEvent::ShowItem { - id: item_name.to_string().into(), - count: Some(item_count), - }), - TextComponent::text(targets.len().to_string()), - ], - ) - }; - sender.send_message(msg).await; + }; - Ok(()) + for target in targets { + let max_stack = i32::from( + item.components + .iter() + .find_map(|(id, component)| { + (id == &MaxStackSize).then(|| get::(*component).size) + }) + .unwrap(), + ); + let mut remaining = item_count; + + while remaining > 0 { + let take = remaining.min(max_stack); + let mut stack = ItemStack::new(take as u8, item); + target.inventory().insert_stack_anywhere(&mut stack).await; + if !stack.is_empty() { + target.drop_item(stack).await; + } + remaining -= take; + } + } + let msg = if targets.len() == 1 { + TextComponent::translate( + "commands.give.success.single", + [ + TextComponent::text(item_count.to_string()), + TextComponent::text("[") + .add_child(item.translated_name()) + .add_child(TextComponent::text("]")) + .hover_event(HoverEvent::ShowItem { + id: item_name.to_string().into(), + count: Some(item_count), + }), + targets[0].get_display_name().await, + ], + ) + } else { + TextComponent::translate( + "commands.give.success.multiple", + [ + TextComponent::text(item_count.to_string()), + TextComponent::text("[") + .add_child(item.translated_name()) + .add_child(TextComponent::text("]")) + .hover_event(HoverEvent::ShowItem { + id: item_name.to_string().into(), + count: Some(item_count), + }), + TextComponent::text(targets.len().to_string()), + ], + ) + }; + sender.send_message(msg).await; + + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/help.rs b/pumpkin/src/command/commands/help.rs index 4bf06693d..fac4bb737 100644 --- a/pumpkin/src/command/commands/help.rs +++ b/pumpkin/src/command/commands/help.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use futures::StreamExt; use pumpkin_util::text::TextComponent; use pumpkin_util::text::click::ClickEvent; @@ -7,12 +6,10 @@ use pumpkin_util::text::color::{Color, NamedColor}; use crate::command::args::bounded_num::BoundedNumArgumentConsumer; use crate::command::args::command::CommandTreeArgumentConsumer; use crate::command::args::{Arg, ConsumedArgs, FindArgDefaultName}; -use crate::command::dispatcher::CommandError; use crate::command::dispatcher::CommandError::InvalidConsumption; use crate::command::tree::builder::{argument, argument_default_name}; use crate::command::tree::{Command, CommandTree}; -use crate::command::{CommandExecutor, CommandSender}; -use crate::server::Server; +use crate::command::{CommandExecutor, CommandResult, CommandSender}; const NAMES: [&str; 3] = ["help", "h", "?"]; @@ -28,207 +25,214 @@ fn page_number_consumer() -> BoundedNumArgumentConsumer { struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::CommandTree(tree)) = args.get(&ARG_COMMAND) else { - return Err(InvalidConsumption(Some(ARG_COMMAND.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::CommandTree(tree)) = args.get(&ARG_COMMAND) else { + return Err(InvalidConsumption(Some(ARG_COMMAND.into()))); + }; - let command_names = tree.names.join(", /"); - let usage = format!("{tree}"); - let description = &tree.description; + let command_names = tree.names.join(", /"); + let usage = format!("{tree}"); + let description = &tree.description; - let header_text = format!(" Help - /{} ", tree.names[0]); + let header_text = format!(" Help - /{} ", tree.names[0]); - let mut message = TextComponent::text("") - .add_child( - TextComponent::text("-".repeat((52 - header_text.len()) / 2) + " ") - .color_named(NamedColor::Yellow), - ) - .add_child(TextComponent::text(header_text.clone())) - .add_child( - TextComponent::text( - " ".to_owned() + &"-".repeat((52 - header_text.len()) / 2) + "\n", + let mut message = TextComponent::text("") + .add_child( + TextComponent::text("-".repeat((52 - header_text.len()) / 2) + " ") + .color_named(NamedColor::Yellow), ) - .color_named(NamedColor::Yellow), - ) - .add_child( - TextComponent::text("Command: ") - .color_named(NamedColor::Aqua) - .add_child( - TextComponent::text(format!("/{command_names}")) - .color_named(NamedColor::Gold) - .bold(), + .add_child(TextComponent::text(header_text.clone())) + .add_child( + TextComponent::text( + " ".to_owned() + &"-".repeat((52 - header_text.len()) / 2) + "\n", ) - .add_child(TextComponent::text("\n").color_named(NamedColor::White)) - .click_event(ClickEvent::SuggestCommand { - command: format!("/{}", tree.names[0]).into(), - }), - ) - .add_child( - TextComponent::text("Description: ") - .color_named(NamedColor::Aqua) - .add_child( - TextComponent::text(format!("{description}\n")) - .color_named(NamedColor::White), - ), - ) - .add_child( - TextComponent::text("Usage: ") - .color_named(NamedColor::Aqua) - .add_child( - TextComponent::text(format!("{usage}\n")).color_named(NamedColor::White), - ) - .click_event(ClickEvent::SuggestCommand { - command: format!("{tree}").into(), - }), - ); + .color_named(NamedColor::Yellow), + ) + .add_child( + TextComponent::text("Command: ") + .color_named(NamedColor::Aqua) + .add_child( + TextComponent::text(format!("/{command_names}")) + .color_named(NamedColor::Gold) + .bold(), + ) + .add_child(TextComponent::text("\n").color_named(NamedColor::White)) + .click_event(ClickEvent::SuggestCommand { + command: format!("/{}", tree.names[0]).into(), + }), + ) + .add_child( + TextComponent::text("Description: ") + .color_named(NamedColor::Aqua) + .add_child( + TextComponent::text(format!("{description}\n")) + .color_named(NamedColor::White), + ), + ) + .add_child( + TextComponent::text("Usage: ") + .color_named(NamedColor::Aqua) + .add_child( + TextComponent::text(format!("{usage}\n")) + .color_named(NamedColor::White), + ) + .click_event(ClickEvent::SuggestCommand { + command: format!("{tree}").into(), + }), + ); - message = - message.add_child(TextComponent::text("-".repeat(52)).color_named(NamedColor::Yellow)); + message = message + .add_child(TextComponent::text("-".repeat(52)).color_named(NamedColor::Yellow)); - sender.send_message(message).await; + sender.send_message(message).await; - Ok(()) + Ok(()) + }) } } struct BaseHelpExecutor; -#[async_trait] impl CommandExecutor for BaseHelpExecutor { #[expect(clippy::too_many_lines)] - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let page_number = match page_number_consumer().find_arg_default_name(args) { - Err(_) => 1, - Ok(Ok(number)) => number, - Ok(Err(_)) => { - sender - .send_message( - TextComponent::text("Invalid page number.") - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - } - }; - - let dispatcher = server.command_dispatcher.read().await; - let commands: Vec<&CommandTree> = dispatcher - .commands - .values() - .filter_map(|cmd| match cmd { - Command::Tree(tree) => Some(tree), - Command::Alias(_) => None, - }) - .collect(); - - let mut commands: Vec<&CommandTree> = futures::stream::iter(commands.iter()) - .filter(|tree| async { - if let Some(perm) = dispatcher.permissions.get(&tree.names[0]) { - return sender.has_permission(perm.as_str()).await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let page_number = match page_number_consumer().find_arg_default_name(args) { + Err(_) => 1, + Ok(Ok(number)) => number, + Ok(Err(_)) => { + sender + .send_message( + TextComponent::text("Invalid page number.") + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); } - false - }) - .collect() - .await; + }; - commands.sort_by(|a, b| a.names[0].cmp(&b.names[0])); - - let total_pages = (commands.len() as i32 + COMMANDS_PER_PAGE - 1) / COMMANDS_PER_PAGE; - let page = page_number.min(total_pages); - - let start = (page - 1) * COMMANDS_PER_PAGE; - let end = start + COMMANDS_PER_PAGE; - let page_commands = &commands[start as usize..end.min(commands.len() as i32) as usize]; - - let arrow_left = if page > 1 { - let cmd = format!("/help {}", page - 1); - TextComponent::text("<<<") - .color(Color::Named(NamedColor::Aqua)) - .click_event(ClickEvent::RunCommand { - command: cmd.into(), + let dispatcher = server.command_dispatcher.read().await; + let commands: Vec<&CommandTree> = dispatcher + .commands + .values() + .filter_map(|cmd| match cmd { + Command::Tree(tree) => Some(tree), + Command::Alias(_) => None, }) - } else { - TextComponent::text("<<<").color(Color::Named(NamedColor::Gray)) - }; + .collect(); - let arrow_right = if page < total_pages { - let cmd = format!("/help {}", page + 1); - TextComponent::text(">>>") - .color(Color::Named(NamedColor::Aqua)) - .click_event(ClickEvent::RunCommand { - command: cmd.into(), + let mut commands: Vec<&CommandTree> = futures::stream::iter(commands.iter()) + .filter(|tree| async { + if let Some(perm) = dispatcher.permissions.get(&tree.names[0]) { + return sender.has_permission(perm.as_str()).await; + } + false }) - } else { - TextComponent::text(">>>").color(Color::Named(NamedColor::Gray)) - }; + .collect() + .await; - let header_text = format!(" Help - Page {page}/{total_pages} "); + commands.sort_by(|a, b| a.names[0].cmp(&b.names[0])); - let mut message = TextComponent::text("") - .add_child( - TextComponent::text("-".repeat((52 - header_text.len() - 3) / 2) + " ") - .color_named(NamedColor::Yellow), - ) - .add_child(arrow_left.clone()) - .add_child(TextComponent::text(header_text.clone())) - .add_child(arrow_right.clone()) - .add_child( - TextComponent::text( - " ".to_owned() + &"-".repeat((52 - header_text.len() - 3) / 2) + "\n", + let total_pages = (commands.len() as i32 + COMMANDS_PER_PAGE - 1) / COMMANDS_PER_PAGE; + let page = page_number.min(total_pages); + + let start = (page - 1) * COMMANDS_PER_PAGE; + let end = start + COMMANDS_PER_PAGE; + let page_commands = &commands[start as usize..end.min(commands.len() as i32) as usize]; + + let arrow_left = if page > 1 { + let cmd = format!("/help {}", page - 1); + TextComponent::text("<<<") + .color(Color::Named(NamedColor::Aqua)) + .click_event(ClickEvent::RunCommand { + command: cmd.into(), + }) + } else { + TextComponent::text("<<<").color(Color::Named(NamedColor::Gray)) + }; + + let arrow_right = if page < total_pages { + let cmd = format!("/help {}", page + 1); + TextComponent::text(">>>") + .color(Color::Named(NamedColor::Aqua)) + .click_event(ClickEvent::RunCommand { + command: cmd.into(), + }) + } else { + TextComponent::text(">>>").color(Color::Named(NamedColor::Gray)) + }; + + let header_text = format!(" Help - Page {page}/{total_pages} "); + + let mut message = TextComponent::text("") + .add_child( + TextComponent::text("-".repeat((52 - header_text.len() - 3) / 2) + " ") + .color_named(NamedColor::Yellow), ) - .color_named(NamedColor::Yellow), - ); - - for tree in page_commands { - message = message.add_child( - TextComponent::text("/".to_owned() + &tree.names.join(", /")) - .color_named(NamedColor::Gold) - .add_child(TextComponent::text(" - ").color_named(NamedColor::Yellow)) - .add_child( - TextComponent::text(tree.description.clone() + "\n") - .color_named(NamedColor::White), + .add_child(arrow_left.clone()) + .add_child(TextComponent::text(header_text.clone())) + .add_child(arrow_right.clone()) + .add_child( + TextComponent::text( + " ".to_owned() + &"-".repeat((52 - header_text.len() - 3) / 2) + "\n", ) - .add_child(TextComponent::text(" Usage: ").color_named(NamedColor::Yellow)) - .add_child( - TextComponent::text(format!("{tree}")).color_named(NamedColor::White), + .color_named(NamedColor::Yellow), + ); + + for tree in page_commands { + message = message.add_child( + TextComponent::text("/".to_owned() + &tree.names.join(", /")) + .color_named(NamedColor::Gold) + .add_child(TextComponent::text(" - ").color_named(NamedColor::Yellow)) + .add_child( + TextComponent::text(tree.description.clone() + "\n") + .color_named(NamedColor::White), + ) + .add_child( + TextComponent::text(" Usage: ").color_named(NamedColor::Yellow), + ) + .add_child( + TextComponent::text(format!("{tree}")).color_named(NamedColor::White), + ) + .add_child(TextComponent::text("\n").color_named(NamedColor::White)) + .click_event(ClickEvent::SuggestCommand { + command: format!("/{}", tree.names[0]).into(), + }), + ); + } + + let footer_text = format!(" Page {page}/{total_pages} "); + message = message + .add_child( + TextComponent::text("-".repeat((52 - footer_text.len() - 3) / 2) + " ") + .color_named(NamedColor::Yellow), + ) + .add_child(arrow_left) + .add_child(TextComponent::text(footer_text.clone())) + .add_child(arrow_right) + .add_child( + TextComponent::text( + " ".to_owned() + &"-".repeat((52 - footer_text.len() - 3) / 2), ) - .add_child(TextComponent::text("\n").color_named(NamedColor::White)) - .click_event(ClickEvent::SuggestCommand { - command: format!("/{}", tree.names[0]).into(), - }), - ); - } - - let footer_text = format!(" Page {page}/{total_pages} "); - message = message - .add_child( - TextComponent::text("-".repeat((52 - footer_text.len() - 3) / 2) + " ") .color_named(NamedColor::Yellow), - ) - .add_child(arrow_left) - .add_child(TextComponent::text(footer_text.clone())) - .add_child(arrow_right) - .add_child( - TextComponent::text(" ".to_owned() + &"-".repeat((52 - footer_text.len() - 3) / 2)) - .color_named(NamedColor::Yellow), - ); + ); - sender.send_message(message).await; + sender.send_message(message).await; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/kick.rs b/pumpkin/src/command/commands/kick.rs index 510576725..b53253eea 100644 --- a/pumpkin/src/command/commands/kick.rs +++ b/pumpkin/src/command/commands/kick.rs @@ -1,13 +1,12 @@ -use async_trait::async_trait; use pumpkin_util::text::TextComponent; use pumpkin_util::text::color::NamedColor; -use crate::command::CommandError; use crate::command::args::message::MsgArgConsumer; use crate::command::args::players::PlayersArgumentConsumer; use crate::command::args::{Arg, ConsumedArgs}; use crate::command::tree::CommandTree; use crate::command::tree::builder::argument; +use crate::command::{CommandError, CommandResult}; use crate::command::{CommandExecutor, CommandSender}; use crate::entity::EntityBase; use crate::net::DisconnectReason; @@ -22,31 +21,32 @@ const ARG_REASON: &str = "reason"; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { - return Err(InvalidConsumption(Some(ARG_TARGETS.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { + return Err(InvalidConsumption(Some(ARG_TARGETS.into()))); + }; - let reason = match args.get(&ARG_REASON) { - Some(Arg::Msg(r)) => TextComponent::text(r.clone()), - _ => TextComponent::translate("multiplayer.disconnect.kicked", []), - }; + let reason = match args.get(&ARG_REASON) { + Some(Arg::Msg(r)) => TextComponent::text(r.clone()), + _ => TextComponent::translate("multiplayer.disconnect.kicked", []), + }; - for target in targets { - target.kick(DisconnectReason::Kicked, reason.clone()).await; - let mut msg = TextComponent::text("Kicked: "); - msg = msg.add_child(target.get_display_name().await); - sender.send_message(msg.color_named(NamedColor::Blue)).await; - } + for target in targets { + target.kick(DisconnectReason::Kicked, reason.clone()).await; + let mut msg = TextComponent::text("Kicked: "); + msg = msg.add_child(target.get_display_name().await); + sender.send_message(msg.color_named(NamedColor::Blue)).await; + } - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/kill.rs b/pumpkin/src/command/commands/kill.rs index 9a981e016..4375affd7 100644 --- a/pumpkin/src/command/commands/kill.rs +++ b/pumpkin/src/command/commands/kill.rs @@ -1,11 +1,10 @@ -use async_trait::async_trait; use pumpkin_util::text::TextComponent; use crate::command::args::entities::EntitiesArgumentConsumer; use crate::command::args::{Arg, ConsumedArgs}; use crate::command::tree::CommandTree; use crate::command::tree::builder::{argument, require}; -use crate::command::{CommandError, CommandExecutor, CommandSender}; +use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender}; use crate::entity::EntityBase; use CommandError::InvalidConsumption; @@ -16,62 +15,64 @@ const ARG_TARGET: &str = "target"; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Entities(targets)) = args.get(&ARG_TARGET) else { - return Err(InvalidConsumption(Some(ARG_TARGET.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Entities(targets)) = args.get(&ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); + }; - let target_count = targets.len(); - for target in targets { - target.kill(target.clone()).await; - } + let target_count = targets.len(); + for target in targets { + target.kill(target.clone()).await; + } - let msg = if target_count == 1 { - TextComponent::translate( - "commands.kill.success.single", - [targets[0].get_display_name().await], - ) - } else { - TextComponent::translate( - "commands.kill.success.multiple", - [TextComponent::text(target_count.to_string())], - ) - }; + let msg = if target_count == 1 { + TextComponent::translate( + "commands.kill.success.single", + [targets[0].get_display_name().await], + ) + } else { + TextComponent::translate( + "commands.kill.success.multiple", + [TextComponent::text(target_count.to_string())], + ) + }; - sender.send_message(msg).await; + sender.send_message(msg).await; - Ok(()) + Ok(()) + }) } } struct SelfExecutor; -#[async_trait] impl CommandExecutor for SelfExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?; - target.kill(target.clone()).await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let target = sender.as_player().ok_or(CommandError::InvalidRequirement)?; + target.kill(target.clone()).await; - sender - .send_message(TextComponent::translate( - "commands.kill.success.single", - [target.get_display_name().await], - )) - .await; + sender + .send_message(TextComponent::translate( + "commands.kill.success.single", + [target.get_display_name().await], + )) + .await; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/list.rs b/pumpkin/src/command/commands/list.rs index 6e8e8d0a1..c1e4705f3 100644 --- a/pumpkin/src/command/commands/list.rs +++ b/pumpkin/src/command/commands/list.rs @@ -1,12 +1,11 @@ use std::sync::Arc; -use async_trait::async_trait; use pumpkin_config::BASIC_CONFIG; use pumpkin_util::text::TextComponent; use crate::{ command::{ - CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree, + CommandExecutor, CommandResult, CommandSender, args::ConsumedArgs, tree::CommandTree, }, entity::player::Player, }; @@ -17,28 +16,29 @@ const DESCRIPTION: &str = "Print the list of online players."; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let players: Vec> = server.get_all_players().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let players: Vec> = server.get_all_players().await; - sender - .send_message(TextComponent::translate( - "commands.list.players", - [ - TextComponent::text(players.len().to_string()), - TextComponent::text(BASIC_CONFIG.max_players.to_string()), - TextComponent::text(get_player_names(players)), - ], - )) - .await; + sender + .send_message(TextComponent::translate( + "commands.list.players", + [ + TextComponent::text(players.len().to_string()), + TextComponent::text(BASIC_CONFIG.max_players.to_string()), + TextComponent::text(get_player_names(players)), + ], + )) + .await; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/me.rs b/pumpkin/src/command/commands/me.rs index ff2f1ffe6..899c2dbf9 100644 --- a/pumpkin/src/command/commands/me.rs +++ b/pumpkin/src/command/commands/me.rs @@ -1,12 +1,10 @@ -use async_trait::async_trait; use pumpkin_data::world::EMOTE_COMMAND; use pumpkin_util::text::TextComponent; use crate::command::{ - CommandError, CommandExecutor, CommandSender, + CommandError, CommandExecutor, CommandResult, CommandSender, args::{Arg, ConsumedArgs, message::MsgArgConsumer}, - tree::CommandTree, - tree::builder::argument, + tree::{CommandTree, builder::argument}, }; use CommandError::InvalidConsumption; @@ -18,27 +16,28 @@ const ARG_MESSAGE: &str = "action"; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else { - return Err(InvalidConsumption(Some(ARG_MESSAGE.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else { + return Err(InvalidConsumption(Some(ARG_MESSAGE.into()))); + }; - server - .broadcast_message( - &TextComponent::text(msg.clone()), - &TextComponent::text(format!("{sender}")), - EMOTE_COMMAND, - None, - ) - .await; - Ok(()) + server + .broadcast_message( + &TextComponent::text(msg.clone()), + &TextComponent::text(format!("{sender}")), + EMOTE_COMMAND, + None, + ) + .await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/msg.rs b/pumpkin/src/command/commands/msg.rs index efc5775d2..c25895fbf 100644 --- a/pumpkin/src/command/commands/msg.rs +++ b/pumpkin/src/command/commands/msg.rs @@ -1,15 +1,16 @@ -use async_trait::async_trait; use pumpkin_data::world::{MSG_COMMAND_INCOMING, MSG_COMMAND_OUTGOING}; use pumpkin_util::text::TextComponent; use crate::command::{ - CommandError, CommandExecutor, CommandSender, + CommandError, CommandExecutor, CommandResult, CommandSender, args::{ Arg, ConsumedArgs, FindArgDefaultName, message::MsgArgConsumer, players::PlayersArgumentConsumer, }, - tree::CommandTree, - tree::builder::{argument, argument_default_name}, + tree::{ + CommandTree, + builder::{argument, argument_default_name}, + }, }; use crate::entity::EntityBase; use CommandError::InvalidConsumption; @@ -22,42 +23,43 @@ const ARG_MESSAGE: &str = "message"; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else { - return Err(InvalidConsumption(Some(ARG_MESSAGE.into()))); - }; - let targets = PlayersArgumentConsumer.find_arg_default_name(args)?; - let player = sender.as_player().ok_or(CommandError::InvalidRequirement)?; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else { + return Err(InvalidConsumption(Some(ARG_MESSAGE.into()))); + }; + let targets = PlayersArgumentConsumer.find_arg_default_name(args)?; + let player = sender.as_player().ok_or(CommandError::InvalidRequirement)?; - for target in targets { - player - .send_message( - &TextComponent::text(msg.clone()), - MSG_COMMAND_OUTGOING, - &player.get_display_name().await, - Some(&target.get_display_name().await), - ) - .await; - } - for target in targets { - target - .send_message( - &TextComponent::text(msg.clone()), - MSG_COMMAND_INCOMING, - &player.get_display_name().await, - Some(&target.get_display_name().await), - ) - .await; - } + for target in targets { + player + .send_message( + &TextComponent::text(msg.clone()), + MSG_COMMAND_OUTGOING, + &player.get_display_name().await, + Some(&target.get_display_name().await), + ) + .await; + } + for target in targets { + target + .send_message( + &TextComponent::text(msg.clone()), + MSG_COMMAND_INCOMING, + &player.get_display_name().await, + Some(&target.get_display_name().await), + ) + .await; + } - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/op.rs b/pumpkin/src/command/commands/op.rs index e45464c29..b08de9cf4 100644 --- a/pumpkin/src/command/commands/op.rs +++ b/pumpkin/src/command/commands/op.rs @@ -1,3 +1,4 @@ +use crate::command::CommandResult; use crate::entity::EntityBase; use crate::{ command::{ @@ -9,7 +10,6 @@ use crate::{ data::{SaveJSONConfiguration, op_data::OPERATOR_CONFIG}, }; use CommandError::InvalidConsumption; -use async_trait::async_trait; use pumpkin_config::{BASIC_CONFIG, op::Op}; use pumpkin_util::text::TextComponent; @@ -19,66 +19,67 @@ const ARG_TARGETS: &str = "targets"; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let mut config = OPERATOR_CONFIG.write().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let mut config = OPERATOR_CONFIG.write().await; - let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { - return Err(InvalidConsumption(Some(ARG_TARGETS.into()))); - }; - - for player in targets { - let new_level = BASIC_CONFIG - .op_permission_level - .min(sender.permission_lvl()); - - if player.permission_lvl.load() == new_level { - sender - .send_message(TextComponent::translate("commands.op.failed", [])) - .await; - continue; - } - - if let Some(op) = config - .ops - .iter_mut() - .find(|o| o.uuid == player.gameprofile.id) - { - op.level = new_level; - } else { - let op_entry = Op::new( - player.gameprofile.id, - player.gameprofile.name.clone(), - new_level, - false, - ); - config.ops.push(op_entry); - } - - config.save(); - - { - let command_dispatcher = server.command_dispatcher.read().await; - player - .set_permission_lvl(new_level, &command_dispatcher) - .await; + let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { + return Err(InvalidConsumption(Some(ARG_TARGETS.into()))); }; - sender - .send_message(TextComponent::translate( - "commands.op.success", - [player.get_display_name().await], - )) - .await; - } + for player in targets { + let new_level = BASIC_CONFIG + .op_permission_level + .min(sender.permission_lvl()); - Ok(()) + if player.permission_lvl.load() == new_level { + sender + .send_message(TextComponent::translate("commands.op.failed", [])) + .await; + continue; + } + + if let Some(op) = config + .ops + .iter_mut() + .find(|o| o.uuid == player.gameprofile.id) + { + op.level = new_level; + } else { + let op_entry = Op::new( + player.gameprofile.id, + player.gameprofile.name.clone(), + new_level, + false, + ); + config.ops.push(op_entry); + } + + config.save(); + + { + let command_dispatcher = server.command_dispatcher.read().await; + player + .set_permission_lvl(new_level, &command_dispatcher) + .await; + }; + + sender + .send_message(TextComponent::translate( + "commands.op.success", + [player.get_display_name().await], + )) + .await; + } + + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/pardon.rs b/pumpkin/src/command/commands/pardon.rs index e12941af1..601b56996 100644 --- a/pumpkin/src/command/commands/pardon.rs +++ b/pumpkin/src/command/commands/pardon.rs @@ -1,14 +1,12 @@ use crate::{ command::{ - CommandError, CommandExecutor, CommandSender, + CommandError, CommandExecutor, CommandResult, CommandSender, args::{Arg, ConsumedArgs, simple::SimpleArgConsumer}, - tree::CommandTree, - tree::builder::argument, + tree::{CommandTree, builder::argument}, }, data::{SaveJSONConfiguration, banned_player_data::BANNED_PLAYER_LIST}, }; use CommandError::InvalidConsumption; -use async_trait::async_trait; use pumpkin_util::text::TextComponent; const NAMES: [&str; 1] = ["pardon"]; @@ -18,43 +16,44 @@ const ARG_TARGET: &str = "player"; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else { - return Err(InvalidConsumption(Some(ARG_TARGET.into()))); - }; - let target = (*target).to_string(); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); + }; + let target = (*target).to_string(); - let mut lock = BANNED_PLAYER_LIST.write().await; + let mut lock = BANNED_PLAYER_LIST.write().await; + + if let Some(idx) = lock + .banned_players + .iter() + .position(|entry| entry.name == target) + { + lock.banned_players.remove(idx); + } else { + sender + .send_message(TextComponent::translate("commands.pardon.failed", [])) + .await; + return Ok(()); + } + + lock.save(); - if let Some(idx) = lock - .banned_players - .iter() - .position(|entry| entry.name == target) - { - lock.banned_players.remove(idx); - } else { sender - .send_message(TextComponent::translate("commands.pardon.failed", [])) + .send_message(TextComponent::translate( + "commands.pardon.success", + [TextComponent::text(target)], + )) .await; - return Ok(()); - } - - lock.save(); - - sender - .send_message(TextComponent::translate( - "commands.pardon.success", - [TextComponent::text(target)], - )) - .await; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/pardonip.rs b/pumpkin/src/command/commands/pardonip.rs index 906788230..bf70f7dac 100644 --- a/pumpkin/src/command/commands/pardonip.rs +++ b/pumpkin/src/command/commands/pardonip.rs @@ -2,15 +2,13 @@ use std::{net::IpAddr, str::FromStr}; use crate::{ command::{ - CommandError, CommandExecutor, CommandSender, + CommandError, CommandExecutor, CommandResult, CommandSender, args::{Arg, ConsumedArgs, simple::SimpleArgConsumer}, - tree::CommandTree, - tree::builder::argument, + tree::{CommandTree, builder::argument}, }, data::{SaveJSONConfiguration, banned_ip_data::BANNED_IP_LIST}, }; use CommandError::InvalidConsumption; -use async_trait::async_trait; use pumpkin_util::text::TextComponent; const NAMES: [&str; 1] = ["pardon-ip"]; @@ -20,45 +18,46 @@ const ARG_TARGET: &str = "ip"; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else { - return Err(InvalidConsumption(Some(ARG_TARGET.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Simple(target)) = args.get(&ARG_TARGET) else { + return Err(InvalidConsumption(Some(ARG_TARGET.into()))); + }; + + let Ok(ip) = IpAddr::from_str(target) else { + sender + .send_message(TextComponent::translate("commands.pardonip.invalid", [])) + .await; + return Ok(()); + }; + + let mut lock = BANNED_IP_LIST.write().await; + + if let Some(idx) = lock.banned_ips.iter().position(|entry| entry.ip == ip) { + lock.banned_ips.remove(idx); + } else { + sender + .send_message(TextComponent::translate("commands.pardonip.failed", [])) + .await; + return Ok(()); + } + + lock.save(); - let Ok(ip) = IpAddr::from_str(target) else { sender - .send_message(TextComponent::translate("commands.pardonip.invalid", [])) + .send_message(TextComponent::translate( + "commands.pardonip.success", + [TextComponent::text(ip.to_string())], + )) .await; - return Ok(()); - }; - - let mut lock = BANNED_IP_LIST.write().await; - - if let Some(idx) = lock.banned_ips.iter().position(|entry| entry.ip == ip) { - lock.banned_ips.remove(idx); - } else { - sender - .send_message(TextComponent::translate("commands.pardonip.failed", [])) - .await; - return Ok(()); - } - - lock.save(); - - sender - .send_message(TextComponent::translate( - "commands.pardonip.success", - [TextComponent::text(ip.to_string())], - )) - .await; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/particle.rs b/pumpkin/src/command/commands/particle.rs index 01ef985a3..669be7d70 100644 --- a/pumpkin/src/command/commands/particle.rs +++ b/pumpkin/src/command/commands/particle.rs @@ -1,8 +1,7 @@ -use async_trait::async_trait; use pumpkin_util::{math::vector3::Vector3, text::TextComponent}; use crate::command::{ - CommandError, CommandExecutor, CommandSender, + CommandError, CommandExecutor, CommandResult, CommandSender, args::{ ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer, position_3d::Position3DArgumentConsumer, resource::particle::ParticleArgumentConsumer, @@ -22,63 +21,64 @@ const ARG_COUNT: &str = "count"; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let particle = ParticleArgumentConsumer::find_arg(args, ARG_NAME)?; - let pos = Position3DArgumentConsumer::find_arg(args, ARG_POS); - let delta = Position3DArgumentConsumer::find_arg(args, ARG_DELTA); - let speed = BoundedNumArgumentConsumer::::find_arg(args, ARG_SPEED); - let count = BoundedNumArgumentConsumer::::find_arg(args, ARG_COUNT); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let particle = ParticleArgumentConsumer::find_arg(args, ARG_NAME)?; + let pos = Position3DArgumentConsumer::find_arg(args, ARG_POS); + let delta = Position3DArgumentConsumer::find_arg(args, ARG_DELTA); + let speed = BoundedNumArgumentConsumer::::find_arg(args, ARG_SPEED); + let count = BoundedNumArgumentConsumer::::find_arg(args, ARG_COUNT); - let delta = delta.unwrap_or(Vector3::new(0.0, 0.0, 0.0)); - let delta: Vector3 = Vector3::new(delta.x as f32, delta.y as f32, delta.z as f32); - let speed = speed.unwrap_or(Ok(0.0))?; - let count = count.unwrap_or(Ok(0))?; - let (world, pos) = match sender { - CommandSender::Console | CommandSender::Rcon(_) => { - let guard = server.worlds.read().await; - let world = guard - .first() - .cloned() - .ok_or(CommandError::InvalidRequirement)?; - // default position for spawning a player, in this case for particle - let pos = { - let info = &world.level_info.read().await; - // default position for spawning a player, in this case for mob - pos.unwrap_or(Vector3::new( - f64::from(info.spawn_x) + 0.5, - f64::from(info.spawn_y) + 1.0, - f64::from(info.spawn_z) + 0.5, - )) - }; + let delta = delta.unwrap_or(Vector3::new(0.0, 0.0, 0.0)); + let delta: Vector3 = Vector3::new(delta.x as f32, delta.y as f32, delta.z as f32); + let speed = speed.unwrap_or(Ok(0.0))?; + let count = count.unwrap_or(Ok(0))?; + let (world, pos) = match sender { + CommandSender::Console | CommandSender::Rcon(_) => { + let guard = server.worlds.read().await; + let world = guard + .first() + .cloned() + .ok_or(CommandError::InvalidRequirement)?; + // default position for spawning a player, in this case for particle + let pos = { + let info = &world.level_info.read().await; + // default position for spawning a player, in this case for mob + pos.unwrap_or(Vector3::new( + f64::from(info.spawn_x) + 0.5, + f64::from(info.spawn_y) + 1.0, + f64::from(info.spawn_z) + 0.5, + )) + }; - (world, pos) - } - CommandSender::Player(player) => { - let pos = pos.unwrap_or(player.living_entity.entity.pos.load()); + (world, pos) + } + CommandSender::Player(player) => { + let pos = pos.unwrap_or(player.living_entity.entity.pos.load()); - (player.world().clone(), pos) - } - }; + (player.world().clone(), pos) + } + }; - world - .spawn_particle(pos, delta, speed, count, *particle) - .await; + world + .spawn_particle(pos, delta, speed, count, *particle) + .await; - sender - .send_message(TextComponent::translate( - "commands.particle.success", - [TextComponent::text(format!("{particle:?}"))], - )) - .await; + sender + .send_message(TextComponent::translate( + "commands.particle.success", + [TextComponent::text(format!("{particle:?}"))], + )) + .await; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/playsound.rs b/pumpkin/src/command/commands/playsound.rs index e8a026663..03cb2dbf0 100644 --- a/pumpkin/src/command/commands/playsound.rs +++ b/pumpkin/src/command/commands/playsound.rs @@ -1,17 +1,15 @@ -use async_trait::async_trait; use pumpkin_data::sound::SoundCategory; use pumpkin_util::text::TextComponent; use rand::{Rng, rng}; use crate::command::{ - CommandError, CommandExecutor, CommandSender, + CommandExecutor, CommandResult, CommandSender, args::{ Arg, ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer, players::PlayersArgumentConsumer, position_3d::Position3DArgumentConsumer, sound::SoundArgumentConsumer, sound_category::SoundCategoryArgumentConsumer, }, - tree::CommandTree, - tree::builder::argument, + tree::{CommandTree, builder::argument}, }; use crate::entity::EntityBase; @@ -60,109 +58,112 @@ fn min_volume_consumer() -> BoundedNumArgumentConsumer { struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - // Get required sound argument - let sound = SoundArgumentConsumer::find_arg(args, ARG_SOUND)?; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + // Get required sound argument + let sound = SoundArgumentConsumer::find_arg(args, ARG_SOUND)?; - // Get optional sound category, defaults to Master - let source = args - .get(ARG_SOURCE) - .map_or(SoundCategory::Master, |arg| match arg { - Arg::SoundCategory(category) => *category, - _ => SoundCategory::Master, - }); + // Get optional sound category, defaults to Master + let source = args + .get(ARG_SOURCE) + .map_or(SoundCategory::Master, |arg| match arg { + Arg::SoundCategory(category) => *category, + _ => SoundCategory::Master, + }); - // Get target players, defaults to sender if not specified - let targets = if let Ok(players) = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS) { - players - } else if let Some(player) = sender.as_player() { - &[player] - } else { - return Ok(()); - }; + // Get target players, defaults to sender if not specified + let targets = if let Ok(players) = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS) + { + players + } else if let Some(player) = sender.as_player() { + &[player] + } else { + return Ok(()); + }; - // Get optional position, defaults to target's position - let position = Position3DArgumentConsumer::find_arg(args, ARG_POS).ok(); + // Get optional position, defaults to target's position + let position = Position3DArgumentConsumer::find_arg(args, ARG_POS).ok(); - // Get optional volume parameter - let volume = match BoundedNumArgumentConsumer::::find_arg(args, ARG_VOLUME) { - Ok(Ok(v)) => v, - _ => 1.0, // Default volume - }; + // Get optional volume parameter + let volume = match BoundedNumArgumentConsumer::::find_arg(args, ARG_VOLUME) { + Ok(Ok(v)) => v, + _ => 1.0, // Default volume + }; - // Get optional pitch parameter - let pitch = match BoundedNumArgumentConsumer::::find_arg(args, ARG_PITCH) { - Ok(Ok(p)) => p.max(0.5), // Values below 0.5 are clamped - _ => 1.0, // Default pitch - }; + // Get optional pitch parameter + let pitch = match BoundedNumArgumentConsumer::::find_arg(args, ARG_PITCH) { + Ok(Ok(p)) => p.max(0.5), // Values below 0.5 are clamped + _ => 1.0, // Default pitch + }; - // Get optional minimum volume (currently unused in implementation) - let min_volume = match BoundedNumArgumentConsumer::::find_arg(args, ARG_MIN_VOLUME) { - Ok(Ok(v)) => v, - _ => 0.0, // Default minimum volume - }; + // Get optional minimum volume (currently unused in implementation) + let min_volume = match BoundedNumArgumentConsumer::::find_arg(args, ARG_MIN_VOLUME) + { + Ok(Ok(v)) => v, + _ => 0.0, // Default minimum volume + }; - // Use same random seed for all targets to ensure sound synchronization - let seed = rng().random::(); + // Use same random seed for all targets to ensure sound synchronization + let seed = rng().random::(); - // Track how many players actually received the sound - let mut players_who_heard = 0; + // Track how many players actually received the sound + let mut players_who_heard = 0; - // Play sound for each target player - for target in targets { - let pos = position.unwrap_or(target.living_entity.entity.pos.load()); + // Play sound for each target player + for target in targets { + let pos = position.unwrap_or(target.living_entity.entity.pos.load()); - // Check if player can hear the sound based on volume and distance - let player_pos = target.living_entity.entity.pos.load(); - let distance = player_pos.squared_distance_to_vec(pos); - let max_distance: f64 = (16.0 * volume).into(); // 16 blocks is base distance at volume 1.0 + // Check if player can hear the sound based on volume and distance + let player_pos = target.living_entity.entity.pos.load(); + let distance = player_pos.squared_distance_to_vec(pos); + let max_distance: f64 = (16.0 * volume).into(); // 16 blocks is base distance at volume 1.0 - if distance <= max_distance || min_volume > 0.0 { - target - .play_sound(sound as u16, source, &pos, volume, pitch, seed) - .await; - players_who_heard += 1; + if distance <= max_distance || min_volume > 0.0 { + target + .play_sound(sound as u16, source, &pos, volume, pitch, seed) + .await; + players_who_heard += 1; + } } - } - // Send appropriate message based on results - if players_who_heard == 0 { - sender - .send_message(TextComponent::translate("commands.playsound.failed", [])) - .await; - } else { - let sound_name = sound.to_name(); - if players_who_heard == 1 { + // Send appropriate message based on results + if players_who_heard == 0 { sender - .send_message(TextComponent::translate( - "commands.playsound.success.single", - [ - TextComponent::text(sound_name), - targets[0].get_display_name().await, - ], - )) + .send_message(TextComponent::translate("commands.playsound.failed", [])) .await; } else { - sender - .send_message(TextComponent::translate( - "commands.playsound.success.multiple", - [ - TextComponent::text(sound_name), - TextComponent::text(players_who_heard.to_string()), - ], - )) - .await; + let sound_name = sound.to_name(); + if players_who_heard == 1 { + sender + .send_message(TextComponent::translate( + "commands.playsound.success.single", + [ + TextComponent::text(sound_name), + targets[0].get_display_name().await, + ], + )) + .await; + } else { + sender + .send_message(TextComponent::translate( + "commands.playsound.success.multiple", + [ + TextComponent::text(sound_name), + TextComponent::text(players_who_heard.to_string()), + ], + )) + .await; + } } - } - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/plugin.rs b/pumpkin/src/command/commands/plugin.rs index c741793b9..5ae1f5f65 100644 --- a/pumpkin/src/command/commands/plugin.rs +++ b/pumpkin/src/command/commands/plugin.rs @@ -1,6 +1,5 @@ use std::path::Path; -use async_trait::async_trait; use pumpkin_util::{ PermissionLvl, text::{TextComponent, color::NamedColor, hover::HoverEvent}, @@ -9,7 +8,7 @@ use pumpkin_util::{ use crate::{ PLUGIN_MANAGER, command::{ - CommandError, CommandExecutor, CommandSender, + CommandExecutor, CommandResult, CommandSender, args::{Arg, ConsumedArgs, simple::SimpleArgConsumer}, tree::{ CommandTree, @@ -28,143 +27,154 @@ const PLUGIN_NAME: &str = "plugin_name"; struct ListExecutor; -#[async_trait] impl CommandExecutor for ListExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let plugins = PLUGIN_MANAGER.active_plugins().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let plugins = PLUGIN_MANAGER.active_plugins().await; - let message_text = if plugins.is_empty() { - "There are no loaded plugins.".to_string() - } else if plugins.len() == 1 { - "There is 1 plugin loaded:\n".to_string() - } else { - format!("There are {} plugins loaded:\n", plugins.len()) - }; - let mut message = TextComponent::text(message_text); - - for (i, metadata) in plugins.clone().into_iter().enumerate() { - let fmt = if i == plugins.len() - 1 { - metadata.name.to_string() + let message_text = if plugins.is_empty() { + "There are no loaded plugins.".to_string() + } else if plugins.len() == 1 { + "There is 1 plugin loaded:\n".to_string() } else { - format!("{}, ", metadata.name) + format!("There are {} plugins loaded:\n", plugins.len()) }; - let hover_text = format!( - "Version: {}\nAuthors: {}\nDescription: {}", - metadata.version, metadata.authors, metadata.description - ); - let component = TextComponent::text(fmt) - .color_named(NamedColor::Green) - .hover_event(HoverEvent::show_text(TextComponent::text(hover_text))); + let mut message = TextComponent::text(message_text); - message = message.add_child(component); - } + for (i, metadata) in plugins.clone().into_iter().enumerate() { + let fmt = if i == plugins.len() - 1 { + metadata.name.to_string() + } else { + format!("{}, ", metadata.name) + }; + let hover_text = format!( + "Version: {}\nAuthors: {}\nDescription: {}", + metadata.version, metadata.authors, metadata.description + ); + let component = TextComponent::text(fmt) + .color_named(NamedColor::Green) + .hover_event(HoverEvent::show_text(TextComponent::text(hover_text))); - sender.send_message(message).await; + message = message.add_child(component); + } - Ok(()) + sender.send_message(message).await; + + Ok(()) + }) } } struct LoadExecutor; -#[async_trait] impl CommandExecutor for LoadExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Simple(plugin_name)) = args.get(PLUGIN_NAME) else { - return Err(InvalidConsumption(Some(PLUGIN_NAME.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Simple(plugin_name)) = args.get(PLUGIN_NAME) else { + return Err(InvalidConsumption(Some(PLUGIN_NAME.into()))); + }; - if PLUGIN_MANAGER.is_plugin_active(plugin_name).await { - sender - .send_message( - TextComponent::text(format!("Plugin {plugin_name} is already loaded")) - .color_named(NamedColor::Red), - ) - .await; - return Ok(()); - } - - let result = PLUGIN_MANAGER.try_load_plugin(Path::new(plugin_name)).await; - - match result { - Ok(()) => { + if PLUGIN_MANAGER.is_plugin_active(plugin_name).await { sender .send_message( - TextComponent::text(format!("Plugin {plugin_name} loaded successfully")) - .color_named(NamedColor::Green), - ) - .await; - } - Err(e) => { - sender - .send_message( - TextComponent::text(format!("Failed to load plugin {plugin_name}: {e}")) + TextComponent::text(format!("Plugin {plugin_name} is already loaded")) .color_named(NamedColor::Red), ) .await; + return Ok(()); } - } - Ok(()) + let result = PLUGIN_MANAGER.try_load_plugin(Path::new(plugin_name)).await; + + match result { + Ok(()) => { + sender + .send_message( + TextComponent::text(format!( + "Plugin {plugin_name} loaded successfully" + )) + .color_named(NamedColor::Green), + ) + .await; + } + Err(e) => { + sender + .send_message( + TextComponent::text(format!( + "Failed to load plugin {plugin_name}: {e}" + )) + .color_named(NamedColor::Red), + ) + .await; + } + } + + Ok(()) + }) } } struct UnloadExecutor; -#[async_trait] impl CommandExecutor for UnloadExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Simple(plugin_name)) = args.get(PLUGIN_NAME) else { - return Err(InvalidConsumption(Some(PLUGIN_NAME.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Simple(plugin_name)) = args.get(PLUGIN_NAME) else { + return Err(InvalidConsumption(Some(PLUGIN_NAME.into()))); + }; - if !PLUGIN_MANAGER.is_plugin_active(plugin_name).await { - sender - .send_message( - TextComponent::text(format!("Plugin {plugin_name} is not loaded")) - .color_named(NamedColor::Red), - ) - .await; - return Ok(()); - } - - let result = PLUGIN_MANAGER.unload_plugin(plugin_name).await; - - match result { - Ok(()) => { + if !PLUGIN_MANAGER.is_plugin_active(plugin_name).await { sender .send_message( - TextComponent::text(format!("Plugin {plugin_name} unloaded successfully",)) - .color_named(NamedColor::Green), - ) - .await; - } - Err(e) => { - sender - .send_message( - TextComponent::text(format!("Failed to unload plugin {plugin_name}: {e}")) + TextComponent::text(format!("Plugin {plugin_name} is not loaded")) .color_named(NamedColor::Red), ) .await; + return Ok(()); } - } - Ok(()) + let result = PLUGIN_MANAGER.unload_plugin(plugin_name).await; + + match result { + Ok(()) => { + sender + .send_message( + TextComponent::text(format!( + "Plugin {plugin_name} unloaded successfully", + )) + .color_named(NamedColor::Green), + ) + .await; + } + Err(e) => { + sender + .send_message( + TextComponent::text(format!( + "Failed to unload plugin {plugin_name}: {e}" + )) + .color_named(NamedColor::Red), + ) + .await; + } + } + + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/plugins.rs b/pumpkin/src/command/commands/plugins.rs index a96c5f74a..f50a1bd33 100644 --- a/pumpkin/src/command/commands/plugins.rs +++ b/pumpkin/src/command/commands/plugins.rs @@ -1,10 +1,9 @@ -use async_trait::async_trait; use pumpkin_util::text::{TextComponent, color::NamedColor, hover::HoverEvent}; use crate::{ PLUGIN_MANAGER, command::{ - CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree, + CommandExecutor, CommandResult, CommandSender, args::ConsumedArgs, tree::CommandTree, }, }; @@ -14,44 +13,45 @@ const DESCRIPTION: &str = "List all available plugins."; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let plugins = PLUGIN_MANAGER.active_plugins().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let plugins = PLUGIN_MANAGER.active_plugins().await; - let message_text = if plugins.is_empty() { - "There are no loaded plugins.".to_string() - } else if plugins.len() == 1 { - "There is 1 plugin loaded:\n".to_string() - } else { - format!("There are {} plugins loaded:\n", plugins.len()) - }; - let mut message = TextComponent::text(message_text); - - for (i, metadata) in plugins.clone().into_iter().enumerate() { - let fmt = if i == plugins.len() - 1 { - metadata.name.to_string() + let message_text = if plugins.is_empty() { + "There are no loaded plugins.".to_string() + } else if plugins.len() == 1 { + "There is 1 plugin loaded:\n".to_string() } else { - format!("{}, ", metadata.name) + format!("There are {} plugins loaded:\n", plugins.len()) }; - let hover_text = format!( - "Version: {}\nAuthors: {}\nDescription: {}", - metadata.version, metadata.authors, metadata.description - ); - let component = TextComponent::text(fmt) - .color_named(NamedColor::Green) - .hover_event(HoverEvent::show_text(TextComponent::text(hover_text))); - message = message.add_child(component); - } + let mut message = TextComponent::text(message_text); - sender.send_message(message).await; + for (i, metadata) in plugins.clone().into_iter().enumerate() { + let fmt = if i == plugins.len() - 1 { + metadata.name.to_string() + } else { + format!("{}, ", metadata.name) + }; + let hover_text = format!( + "Version: {}\nAuthors: {}\nDescription: {}", + metadata.version, metadata.authors, metadata.description + ); + let component = TextComponent::text(fmt) + .color_named(NamedColor::Green) + .hover_event(HoverEvent::show_text(TextComponent::text(hover_text))); + message = message.add_child(component); + } - Ok(()) + sender.send_message(message).await; + + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/profile.rs b/pumpkin/src/command/commands/profile.rs index b0c7c30c4..e76726f4c 100644 --- a/pumpkin/src/command/commands/profile.rs +++ b/pumpkin/src/command/commands/profile.rs @@ -1,10 +1,11 @@ -use async_trait::async_trait; use pumpkin_util::text::TextComponent; use pumpkin_util::text::color::NamedColor; +use crate::command::CommandResult; + use crate::command::args::ConsumedArgs; use crate::command::tree::CommandTree; -use crate::command::{CommandError, CommandExecutor, CommandSender}; +use crate::command::{CommandExecutor, CommandSender}; use crate::{HEAP_PROFILER, stop_server}; const NAMES: [&str; 1] = ["mem_profile"]; @@ -13,27 +14,29 @@ const DESCRIPTION: &str = "Stop the server and dump a memory profile."; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - sender - .send_message( - TextComponent::translate("commands.stop.stopping", []).color_named(NamedColor::Red), - ) - .await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + sender + .send_message( + TextComponent::translate("commands.stop.stopping", []) + .color_named(NamedColor::Red), + ) + .await; - let mut profiler = HEAP_PROFILER.lock().await; - let p = profiler.take().unwrap(); - // Do the actual profiling - drop(p); + let mut profiler = HEAP_PROFILER.lock().await; + let p = profiler.take().unwrap(); + drop(p); - stop_server(); - Ok(()) + stop_server(); + + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/pumpkin.rs b/pumpkin/src/command/commands/pumpkin.rs index 0a1a86f3c..a443fd34e 100644 --- a/pumpkin/src/command/commands/pumpkin.rs +++ b/pumpkin/src/command/commands/pumpkin.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_data::packet::CURRENT_MC_PROTOCOL; use pumpkin_util::text::click::ClickEvent; use pumpkin_util::text::hover::HoverEvent; @@ -7,9 +6,8 @@ use pumpkin_util::translation::get_translation_text; use pumpkin_world::CURRENT_MC_VERSION; use std::borrow::Cow; -use crate::command::{ - CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree, -}; +use crate::command::CommandResult; +use crate::command::{CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree}; const NAMES: [&str; 2] = ["pumpkin", "version"]; @@ -19,117 +17,122 @@ struct Executor; const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); -#[async_trait] #[allow(clippy::too_many_lines)] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let locale = sender.get_locale().await; - sender - .send_message( - TextComponent::custom( - "pumpkin", - "commands.pumpkin.version", - locale, - vec![TextComponent::text(CARGO_PKG_VERSION)], - ) - .hover_event(HoverEvent::show_text(TextComponent::custom( - "pumpkin", - "commands.pumpkin.version.hover", - locale, - vec![], - ))) - .click_event(ClickEvent::CopyToClipboard { - value: Cow::from( - get_translation_text( - "pumpkin:commands.pumpkin.version", + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let locale = sender.get_locale().await; + sender + .send_message( + TextComponent::custom( + "pumpkin", + "commands.pumpkin.version", + locale, + vec![TextComponent::text(CARGO_PKG_VERSION)], + ) + .hover_event(HoverEvent::show_text(TextComponent::custom( + "pumpkin", + "commands.pumpkin.version.hover", + locale, + vec![], + ))) + .click_event(ClickEvent::CopyToClipboard { + value: Cow::from( + get_translation_text( + "pumpkin:commands.pumpkin.version", + locale, + vec![TextComponent::text(CARGO_PKG_VERSION).0], + ) + .replace('\n', ""), + ), + }) + .color_named(NamedColor::Green) + .add_child( + TextComponent::custom( + "pumpkin", + "commands.pumpkin.description", locale, - vec![TextComponent::text(CARGO_PKG_VERSION).0], + vec![], ) - .replace('\n', ""), - ), - }) - .color_named(NamedColor::Green) - .add_child( - TextComponent::custom( - "pumpkin", - "commands.pumpkin.description", - locale, - vec![], - ) - .click_event(ClickEvent::CopyToClipboard { - value: Cow::from( - get_translation_text( - "pumpkin:commands.pumpkin.description", - locale, - vec![], - ) - .replace('\n', ""), - ), - }) - .hover_event(HoverEvent::show_text(TextComponent::custom( - "pumpkin", - "commands.pumpkin.description.hover", - locale, - vec![], - ))) - .color_named(NamedColor::White), - ) - .add_child( - TextComponent::custom( - "pumpkin", - "commands.pumpkin.minecraft_version", - locale, - vec![ - TextComponent::text(CURRENT_MC_VERSION), - TextComponent::text(format!("{CURRENT_MC_PROTOCOL}")), - ], - ) - .click_event(ClickEvent::CopyToClipboard { - value: Cow::from( - get_translation_text( - "pumpkin:commands.pumpkin.minecraft_version", - locale, - vec![ - TextComponent::text(CURRENT_MC_VERSION).0, - TextComponent::text(format!("{CURRENT_MC_PROTOCOL}")).0, - ], - ) - .replace('\n', ""), - ), - }) - .hover_event(HoverEvent::show_text(TextComponent::custom( - "pumpkin", - "commands.pumpkin.minecraft_version.hover", - locale, - vec![], - ))) - .color_named(NamedColor::Gold), - ) - // https://pumpkinmc.org/ - .add_child( - TextComponent::custom("pumpkin", "commands.pumpkin.github", locale, vec![]) - .click_event(ClickEvent::OpenUrl { - url: Cow::from("https://github.com/Pumpkin-MC/Pumpkin"), + .click_event(ClickEvent::CopyToClipboard { + value: Cow::from( + get_translation_text( + "pumpkin:commands.pumpkin.description", + locale, + vec![], + ) + .replace('\n', ""), + ), }) .hover_event(HoverEvent::show_text(TextComponent::custom( "pumpkin", - "commands.pumpkin.github.hover", + "commands.pumpkin.description.hover", locale, vec![], ))) - .color_named(NamedColor::Blue) - .bold() - .underlined(), - ) - // Added docs. and a space for spacing - .add_child(TextComponent::text(" ")) - .add_child( - TextComponent::custom("pumpkin", "commands.pumpkin.website", locale, vec![]) + .color_named(NamedColor::White), + ) + .add_child( + TextComponent::custom( + "pumpkin", + "commands.pumpkin.minecraft_version", + locale, + vec![ + TextComponent::text(CURRENT_MC_VERSION), + TextComponent::text(format!("{CURRENT_MC_PROTOCOL}")), + ], + ) + .click_event(ClickEvent::CopyToClipboard { + value: Cow::from( + get_translation_text( + "pumpkin:commands.pumpkin.minecraft_version", + locale, + vec![ + TextComponent::text(CURRENT_MC_VERSION).0, + TextComponent::text(format!("{CURRENT_MC_PROTOCOL}")).0, + ], + ) + .replace('\n', ""), + ), + }) + .hover_event(HoverEvent::show_text(TextComponent::custom( + "pumpkin", + "commands.pumpkin.minecraft_version.hover", + locale, + vec![], + ))) + .color_named(NamedColor::Gold), + ) + // https://pumpkinmc.org/ + .add_child( + TextComponent::custom("pumpkin", "commands.pumpkin.github", locale, vec![]) + .click_event(ClickEvent::OpenUrl { + url: Cow::from("https://github.com/Pumpkin-MC/Pumpkin"), + }) + .hover_event(HoverEvent::show_text(TextComponent::custom( + "pumpkin", + "commands.pumpkin.github.hover", + locale, + vec![], + ))) + .color_named(NamedColor::Blue) + .bold() + .underlined(), + ) + // Added docs. and a space for spacing + .add_child(TextComponent::text(" ")) + .add_child( + TextComponent::custom( + "pumpkin", + "commands.pumpkin.website", + locale, + vec![], + ) .click_event(ClickEvent::OpenUrl { url: Cow::from("https://pumpkinmc.org/"), }) @@ -142,10 +145,11 @@ impl CommandExecutor for Executor { .color_named(NamedColor::Blue) .bold() .underlined(), - ), - ) - .await; - Ok(()) + ), + ) + .await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/say.rs b/pumpkin/src/command/commands/say.rs index 32f147864..8dded2bc8 100644 --- a/pumpkin/src/command/commands/say.rs +++ b/pumpkin/src/command/commands/say.rs @@ -1,12 +1,10 @@ -use async_trait::async_trait; use pumpkin_data::world::SAY_COMMAND; use pumpkin_util::text::TextComponent; use crate::command::{ - CommandError, CommandExecutor, CommandSender, + CommandError, CommandExecutor, CommandResult, CommandSender, args::{Arg, ConsumedArgs, message::MsgArgConsumer}, - tree::CommandTree, - tree::builder::argument, + tree::{CommandTree, builder::argument}, }; use CommandError::InvalidConsumption; @@ -18,27 +16,28 @@ const ARG_MESSAGE: &str = "message"; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else { - return Err(InvalidConsumption(Some(ARG_MESSAGE.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Msg(msg)) = args.get(ARG_MESSAGE) else { + return Err(InvalidConsumption(Some(ARG_MESSAGE.into()))); + }; - server - .broadcast_message( - &TextComponent::text(msg.clone()), - &TextComponent::text(format!("{sender}")), - SAY_COMMAND, - None, - ) - .await; - Ok(()) + server + .broadcast_message( + &TextComponent::text(msg.clone()), + &TextComponent::text(format!("{sender}")), + SAY_COMMAND, + None, + ) + .await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/seed.rs b/pumpkin/src/command/commands/seed.rs index 54e827ede..b05e1ba63 100644 --- a/pumpkin/src/command/commands/seed.rs +++ b/pumpkin/src/command/commands/seed.rs @@ -1,7 +1,7 @@ +use crate::command::CommandResult; use crate::command::{ CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree, }; -use async_trait::async_trait; use pumpkin_util::text::click::ClickEvent; use pumpkin_util::text::hover::HoverEvent; use pumpkin_util::text::{TextComponent, color::NamedColor}; @@ -13,43 +13,44 @@ const DESCRIPTION: &str = "Displays the world seed."; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let seed = match sender { - CommandSender::Player(player) => player.living_entity.entity.world.level.seed.0, - // TODO: Maybe ask player for world, or get the current world - _ => match server.worlds.read().await.first() { - Some(world) => world.level.seed.0, - None => { - return Err(CommandError::CommandFailed(Box::new(TextComponent::text( - "Unable to get Seed", - )))); - } - }, - }; - let seed = (seed as i64).to_string(); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let seed = match sender { + CommandSender::Player(player) => player.living_entity.entity.world.level.seed.0, + // TODO: Maybe ask player for world, or get the current world + _ => match server.worlds.read().await.first() { + Some(world) => world.level.seed.0, + None => { + return Err(CommandError::CommandFailed(Box::new(TextComponent::text( + "Unable to get Seed", + )))); + } + }, + }; + let seed = (seed as i64).to_string(); - sender - .send_message(TextComponent::translate( - "commands.seed.success", - [TextComponent::text(seed.clone()) - .hover_event(HoverEvent::show_text(TextComponent::translate( - Cow::from("chat.copy.click"), - [], - ))) - .click_event(ClickEvent::CopyToClipboard { - value: Cow::from(seed), - }) - .color_named(NamedColor::Green)], - )) - .await; - Ok(()) + sender + .send_message(TextComponent::translate( + "commands.seed.success", + [TextComponent::text(seed.clone()) + .hover_event(HoverEvent::show_text(TextComponent::translate( + Cow::from("chat.copy.click"), + [], + ))) + .click_event(ClickEvent::CopyToClipboard { + value: Cow::from(seed), + }) + .color_named(NamedColor::Green)], + )) + .await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/setblock.rs b/pumpkin/src/command/commands/setblock.rs index 976bbd315..bb5d3d867 100644 --- a/pumpkin/src/command/commands/setblock.rs +++ b/pumpkin/src/command/commands/setblock.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_util::text::TextComponent; use pumpkin_world::world::BlockFlags; @@ -7,7 +6,7 @@ use crate::command::args::position_block::BlockPosArgumentConsumer; use crate::command::args::{ConsumedArgs, FindArg}; use crate::command::tree::CommandTree; use crate::command::tree::builder::{argument, literal}; -use crate::command::{CommandError, CommandExecutor, CommandSender}; +use crate::command::{CommandError, CommandExecutor, CommandResult, CommandSender}; const NAMES: [&str; 1] = ["setblock"]; @@ -30,57 +29,35 @@ enum Mode { struct Executor(Mode); -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?; - let block_state_id = block.default_state.id; - let pos = BlockPosArgumentConsumer::find_arg(args, ARG_BLOCK_POS)?; - let mode = self.0; - let world = match sender { - CommandSender::Console | CommandSender::Rcon(_) => { - let guard = server.worlds.read().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let block = BlockArgumentConsumer::find_arg(args, ARG_BLOCK)?; + let block_state_id = block.default_state.id; + let pos = BlockPosArgumentConsumer::find_arg(args, ARG_BLOCK_POS)?; + let mode = self.0; + let world = match sender { + CommandSender::Console | CommandSender::Rcon(_) => { + let guard = server.worlds.read().await; - guard - .first() - .cloned() - .ok_or(CommandError::InvalidRequirement)? - } - CommandSender::Player(player) => player.world().clone(), - }; - let success = match mode { - Mode::Destroy => { - world - .clone() - .break_block(&pos, None, BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE) - .await; - world - .set_block_state( - &pos, - block_state_id, - BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - true - } - Mode::Replace => { - world - .set_block_state( - &pos, - block_state_id, - BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS, - ) - .await; - true - } - Mode::Keep => { - let old_state = world.get_block_state(&pos).await; - if old_state.is_air() { + guard + .first() + .cloned() + .ok_or(CommandError::InvalidRequirement)? + } + CommandSender::Player(player) => player.world().clone(), + }; + let success = match mode { + Mode::Destroy => { + world + .clone() + .break_block(&pos, None, BlockFlags::SKIP_DROPS | BlockFlags::FORCE_STATE) + .await; world .set_block_state( &pos, @@ -89,28 +66,51 @@ impl CommandExecutor for Executor { ) .await; true - } else { - false } - } - }; + Mode::Replace => { + world + .set_block_state( + &pos, + block_state_id, + BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS, + ) + .await; + true + } + Mode::Keep => { + let old_state = world.get_block_state(&pos).await; + if old_state.is_air() { + world + .set_block_state( + &pos, + block_state_id, + BlockFlags::FORCE_STATE | BlockFlags::NOTIFY_NEIGHBORS, + ) + .await; + true + } else { + false + } + } + }; - sender - .send_message(if success { - TextComponent::translate( - "commands.setblock.success", - [ - TextComponent::text(pos.0.x.to_string()), - TextComponent::text(pos.0.y.to_string()), - TextComponent::text(pos.0.z.to_string()), - ], - ) - } else { - TextComponent::translate("commands.setblock.failed", []) - }) - .await; + sender + .send_message(if success { + TextComponent::translate( + "commands.setblock.success", + [ + TextComponent::text(pos.0.x.to_string()), + TextComponent::text(pos.0.y.to_string()), + TextComponent::text(pos.0.z.to_string()), + ], + ) + } else { + TextComponent::translate("commands.setblock.failed", []) + }) + .await; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/setworldspawn.rs b/pumpkin/src/command/commands/setworldspawn.rs index fd0e029ff..976e3624b 100644 --- a/pumpkin/src/command/commands/setworldspawn.rs +++ b/pumpkin/src/command/commands/setworldspawn.rs @@ -1,3 +1,4 @@ +use crate::command::CommandResult; use crate::command::dispatcher::CommandError::InvalidConsumption; use crate::command::{ CommandExecutor, CommandSender, @@ -9,7 +10,6 @@ use crate::command::{ tree::{CommandTree, builder::argument}, }; use crate::server::Server; -use async_trait::async_trait; use pumpkin_registry::VanillaDimensionType; use pumpkin_util::{math::position::BlockPos, text::TextComponent}; @@ -23,72 +23,75 @@ const ARG_ANGLE: &str = "angle"; struct NoArgsWorldSpawnExecutor; -#[async_trait] impl CommandExecutor for NoArgsWorldSpawnExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(block_pos) = sender.position() else { - let level_info_guard = server.level_info.read().await; - sender - .send_message(TextComponent::translate( - "commands.setworldspawn.success", - [ - TextComponent::text(level_info_guard.spawn_x.to_string()), - TextComponent::text(level_info_guard.spawn_y.to_string()), - TextComponent::text(level_info_guard.spawn_z.to_string()), - TextComponent::text(level_info_guard.spawn_angle.to_string()), - ], - )) - .await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(block_pos) = sender.position() else { + let level_info_guard = server.level_info.read().await; + sender + .send_message(TextComponent::translate( + "commands.setworldspawn.success", + [ + TextComponent::text(level_info_guard.spawn_x.to_string()), + TextComponent::text(level_info_guard.spawn_y.to_string()), + TextComponent::text(level_info_guard.spawn_z.to_string()), + TextComponent::text(level_info_guard.spawn_angle.to_string()), + ], + )) + .await; - return Ok(()); - }; + return Ok(()); + }; - setworldspawn(sender, server, block_pos.to_block_pos(), 0.0).await + setworldspawn(sender, server, block_pos.to_block_pos(), 0.0).await + }) } } struct DefaultWorldSpawnExecutor; -#[async_trait] impl CommandExecutor for DefaultWorldSpawnExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::BlockPos(block_pos)) = args.get(ARG_BLOCK_POS) else { - return Err(InvalidConsumption(Some(ARG_BLOCK_POS.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::BlockPos(block_pos)) = args.get(ARG_BLOCK_POS) else { + return Err(InvalidConsumption(Some(ARG_BLOCK_POS.into()))); + }; - setworldspawn(sender, server, *block_pos, 0.0).await + setworldspawn(sender, server, *block_pos, 0.0).await + }) } } struct AngleWorldSpawnExecutor; -#[async_trait] impl CommandExecutor for AngleWorldSpawnExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::BlockPos(block_pos)) = args.get(ARG_BLOCK_POS) else { - return Err(InvalidConsumption(Some(ARG_BLOCK_POS.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::BlockPos(block_pos)) = args.get(ARG_BLOCK_POS) else { + return Err(InvalidConsumption(Some(ARG_BLOCK_POS.into()))); + }; - let Some(Arg::Rotation(_, yaw)) = args.get(ARG_ANGLE) else { - return Err(InvalidConsumption(Some(ARG_ANGLE.into()))); - }; + let Some(Arg::Rotation(_, yaw)) = args.get(ARG_ANGLE) else { + return Err(InvalidConsumption(Some(ARG_ANGLE.into()))); + }; - setworldspawn(sender, server, *block_pos, *yaw).await + setworldspawn(sender, server, *block_pos, *yaw).await + }) } } diff --git a/pumpkin/src/command/commands/stop.rs b/pumpkin/src/command/commands/stop.rs index 47c8776f2..5733b897a 100644 --- a/pumpkin/src/command/commands/stop.rs +++ b/pumpkin/src/command/commands/stop.rs @@ -1,10 +1,9 @@ -use async_trait::async_trait; use pumpkin_util::text::TextComponent; use pumpkin_util::text::color::NamedColor; use crate::command::args::ConsumedArgs; use crate::command::tree::CommandTree; -use crate::command::{CommandError, CommandExecutor, CommandSender}; +use crate::command::{CommandExecutor, CommandResult, CommandSender}; use crate::stop_server; const NAMES: [&str; 1] = ["stop"]; @@ -13,21 +12,23 @@ const DESCRIPTION: &str = "Stop the server."; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - sender - .send_message( - TextComponent::translate("commands.stop.stopping", []).color_named(NamedColor::Red), - ) - .await; - stop_server(); - Ok(()) + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + sender + .send_message( + TextComponent::translate("commands.stop.stopping", []) + .color_named(NamedColor::Red), + ) + .await; + stop_server(); + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/stopsound.rs b/pumpkin/src/command/commands/stopsound.rs index 2ff1068f6..8b517b1d4 100644 --- a/pumpkin/src/command/commands/stopsound.rs +++ b/pumpkin/src/command/commands/stopsound.rs @@ -1,13 +1,11 @@ use crate::command::{ - CommandExecutor, CommandSender, + CommandExecutor, CommandResult, CommandSender, args::{ ConsumedArgs, FindArg, players::PlayersArgumentConsumer, sound::SoundArgumentConsumer, sound_category::SoundCategoryArgumentConsumer, }, - dispatcher::CommandError, tree::{CommandTree, builder::argument}, }; -use async_trait::async_trait; use pumpkin_util::resource_location::ResourceLocation; use pumpkin_util::text::TextComponent; @@ -20,54 +18,55 @@ const ARG_SOUND: &str = "sound"; pub struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let targets = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS)?; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let targets = PlayersArgumentConsumer::find_arg(args, ARG_TARGETS)?; - let category = SoundCategoryArgumentConsumer::find_arg(args, ARG_SOURCE); - let sound = SoundArgumentConsumer::find_arg(args, ARG_SOUND); + let category = SoundCategoryArgumentConsumer::find_arg(args, ARG_SOURCE); + let sound = SoundArgumentConsumer::find_arg(args, ARG_SOUND); - for target in targets { - target - .stop_sound( - sound - .as_ref() - .cloned() - .map(|s| ResourceLocation::vanilla(s.to_name())) - .ok(), - category.as_ref().map(|s| **s).ok(), - ) - .await; - } - let text = match (category, sound) { - (Ok(c), Ok(s)) => TextComponent::translate( - "commands.stopsound.success.source.sound", - [ - TextComponent::text(s.to_name()), - TextComponent::text(c.to_name()), - ], - ), - (Ok(c), Err(_)) => TextComponent::translate( - "commands.stopsound.success.source.any", - [TextComponent::text(c.to_name())], - ), - (Err(_), Ok(s)) => TextComponent::translate( - "commands.stopsound.success.sourceless.sound", - [TextComponent::text(s.to_name())], - ), - (Err(_), Err(_)) => { - TextComponent::translate("commands.stopsound.success.sourceless.any", []) + for target in targets { + target + .stop_sound( + sound + .as_ref() + .cloned() + .map(|s| ResourceLocation::vanilla(s.to_name())) + .ok(), + category.as_ref().map(|s| **s).ok(), + ) + .await; } - }; - sender.send_message(text).await; + let text = match (category, sound) { + (Ok(c), Ok(s)) => TextComponent::translate( + "commands.stopsound.success.source.sound", + [ + TextComponent::text(s.to_name()), + TextComponent::text(c.to_name()), + ], + ), + (Ok(c), Err(_)) => TextComponent::translate( + "commands.stopsound.success.source.any", + [TextComponent::text(c.to_name())], + ), + (Err(_), Ok(s)) => TextComponent::translate( + "commands.stopsound.success.sourceless.sound", + [TextComponent::text(s.to_name())], + ), + (Err(_), Err(_)) => { + TextComponent::translate("commands.stopsound.success.sourceless.any", []) + } + }; + sender.send_message(text).await; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/summon.rs b/pumpkin/src/command/commands/summon.rs index 55b05fb11..547927db9 100644 --- a/pumpkin/src/command/commands/summon.rs +++ b/pumpkin/src/command/commands/summon.rs @@ -1,10 +1,9 @@ -use async_trait::async_trait; use pumpkin_util::{math::vector3::Vector3, text::TextComponent}; use uuid::Uuid; use crate::{ command::{ - CommandError, CommandExecutor, CommandSender, + CommandError, CommandExecutor, CommandResult, CommandSender, args::{ ConsumedArgs, FindArg, position_3d::Position3DArgumentConsumer, summonable_entities::SummonableEntitiesArgumentConsumer, @@ -23,52 +22,53 @@ const ARG_POS: &str = "pos"; struct Executor; -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let entity = SummonableEntitiesArgumentConsumer::find_arg(args, ARG_ENTITY)?; - let pos = Position3DArgumentConsumer::find_arg(args, ARG_POS); - let (world, pos) = match sender { - CommandSender::Console | CommandSender::Rcon(_) => { - let guard = server.worlds.read().await; - let world = guard - .first() - .cloned() - .ok_or(CommandError::InvalidRequirement)?; - let pos = { - let info = &world.level_info.read().await; - // default position for spawning a player, in this case for mob - pos.unwrap_or(Vector3::new( - f64::from(info.spawn_x) + 0.5, - f64::from(info.spawn_y) + 1.0, - f64::from(info.spawn_z) + 0.5, - )) - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let entity = SummonableEntitiesArgumentConsumer::find_arg(args, ARG_ENTITY)?; + let pos = Position3DArgumentConsumer::find_arg(args, ARG_POS); + let (world, pos) = match sender { + CommandSender::Console | CommandSender::Rcon(_) => { + let guard = server.worlds.read().await; + let world = guard + .first() + .cloned() + .ok_or(CommandError::InvalidRequirement)?; + let pos = { + let info = &world.level_info.read().await; + // default position for spawning a player, in this case for mob + pos.unwrap_or(Vector3::new( + f64::from(info.spawn_x) + 0.5, + f64::from(info.spawn_y) + 1.0, + f64::from(info.spawn_z) + 0.5, + )) + }; - (world, pos) - } - CommandSender::Player(player) => { - let pos = pos.unwrap_or(player.living_entity.entity.pos.load()); + (world, pos) + } + CommandSender::Player(player) => { + let pos = pos.unwrap_or(player.living_entity.entity.pos.load()); - (player.world().clone(), pos) - } - }; - let mob = from_type(entity, pos, &world, Uuid::new_v4()).await; - world.spawn_entity(mob).await; + (player.world().clone(), pos) + } + }; + let mob = from_type(entity, pos, &world, Uuid::new_v4()).await; + world.spawn_entity(mob).await; - sender - .send_message(TextComponent::translate( - "commands.summon.success", - [TextComponent::text(format!("{entity:?}"))], - )) - .await; + sender + .send_message(TextComponent::translate( + "commands.summon.success", + [TextComponent::text(format!("{entity:?}"))], + )) + .await; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/teleport.rs b/pumpkin/src/command/commands/teleport.rs index 634a2e2e2..ac6aae1a6 100644 --- a/pumpkin/src/command/commands/teleport.rs +++ b/pumpkin/src/command/commands/teleport.rs @@ -1,8 +1,8 @@ -use async_trait::async_trait; use pumpkin_util::math::vector3::Vector3; use pumpkin_util::text::TextComponent; use crate::command::CommandError; +use crate::command::CommandResult; use crate::command::args::ConsumedArgs; use crate::command::args::FindArg; use crate::command::args::entities::EntitiesArgumentConsumer; @@ -53,256 +53,264 @@ fn yaw_pitch_facing_position( struct EntitiesToEntityExecutor; -#[async_trait] impl CommandExecutor for EntitiesToEntityExecutor { - async fn execute<'a>( - &self, - _sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?; + fn execute<'a>( + &'a self, + _sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?; - let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?; - let pos = destination.get_entity().pos.load(); - if !World::is_valid(pos) { - return Err(CommandError::CommandFailed(Box::new( - TextComponent::translate("argument.pos.outofbounds", []), - ))); - } - for target in targets { - let base_entity = target.get_entity(); - let yaw = base_entity.yaw.load(); - let pitch = base_entity.pitch.load(); - let world = base_entity.world.clone(); - target - .clone() - .teleport(pos, yaw.into(), pitch.into(), world) - .await; - } + let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?; + let pos = destination.get_entity().pos.load(); + if !World::is_valid(pos) { + return Err(CommandError::CommandFailed(Box::new( + TextComponent::translate("argument.pos.outofbounds", []), + ))); + } + for target in targets { + let base_entity = target.get_entity(); + let yaw = base_entity.yaw.load(); + let pitch = base_entity.pitch.load(); + let world = base_entity.world.clone(); + target + .clone() + .teleport(pos, yaw.into(), pitch.into(), world) + .await; + } - Ok(()) + Ok(()) + }) } } struct EntitiesToPosFacingPosExecutor; -#[async_trait] impl CommandExecutor for EntitiesToPosFacingPosExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?; - let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; - if !World::is_valid(pos) { - return Err(CommandError::CommandFailed(Box::new( - TextComponent::translate("argument.pos.outofbounds", []), - ))); - } - let facing_pos = Position3DArgumentConsumer::find_arg(args, ARG_FACING_LOCATION)?; - let (yaw, pitch) = yaw_pitch_facing_position(&pos, &facing_pos); - //todo - let world = match sender { - CommandSender::Rcon(_) | CommandSender::Console => { - server.worlds.read().await.first().unwrap().clone() + let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; + if !World::is_valid(pos) { + return Err(CommandError::CommandFailed(Box::new( + TextComponent::translate("argument.pos.outofbounds", []), + ))); } - CommandSender::Player(player) => player.world().clone(), - }; + let facing_pos = Position3DArgumentConsumer::find_arg(args, ARG_FACING_LOCATION)?; + let (yaw, pitch) = yaw_pitch_facing_position(&pos, &facing_pos); + //todo + let world = match sender { + CommandSender::Rcon(_) | CommandSender::Console => { + server.worlds.read().await.first().unwrap().clone() + } + CommandSender::Player(player) => player.world().clone(), + }; - for target in targets { - target - .clone() - .teleport(pos, Some(yaw), Some(pitch), world.clone()) - .await; - } + for target in targets { + target + .clone() + .teleport(pos, Some(yaw), Some(pitch), world.clone()) + .await; + } - Ok(()) + Ok(()) + }) } } struct EntitiesToPosFacingEntityExecutor; -#[async_trait] impl CommandExecutor for EntitiesToPosFacingEntityExecutor { - async fn execute<'a>( - &self, - _sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?; + fn execute<'a>( + &'a self, + _sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?; - let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; - if !World::is_valid(pos) { - return Err(CommandError::CommandFailed(Box::new( - TextComponent::translate("argument.pos.outofbounds", []), - ))); - } - let facing_entity = EntityArgumentConsumer::find_arg(args, ARG_FACING_ENTITY)?; - let (yaw, pitch) = yaw_pitch_facing_position(&pos, &facing_entity.get_entity().pos.load()); + let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; + if !World::is_valid(pos) { + return Err(CommandError::CommandFailed(Box::new( + TextComponent::translate("argument.pos.outofbounds", []), + ))); + } + let facing_entity = EntityArgumentConsumer::find_arg(args, ARG_FACING_ENTITY)?; + let (yaw, pitch) = + yaw_pitch_facing_position(&pos, &facing_entity.get_entity().pos.load()); - for target in targets { - target - .clone() - .teleport( - pos, - Some(yaw), - Some(pitch), - facing_entity.get_entity().world.clone(), - ) - .await; - } + for target in targets { + target + .clone() + .teleport( + pos, + Some(yaw), + Some(pitch), + facing_entity.get_entity().world.clone(), + ) + .await; + } - Ok(()) + Ok(()) + }) } } struct EntitiesToPosWithRotationExecutor; -#[async_trait] impl CommandExecutor for EntitiesToPosWithRotationExecutor { - async fn execute<'a>( - &self, - _sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?; + fn execute<'a>( + &'a self, + _sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?; - let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; - if !World::is_valid(pos) { - return Err(CommandError::CommandFailed(Box::new( - TextComponent::translate("argument.pos.outofbounds", []), - ))); - } - let (yaw, pitch) = RotationArgumentConsumer::find_arg(args, ARG_ROTATION)?; + let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; + if !World::is_valid(pos) { + return Err(CommandError::CommandFailed(Box::new( + TextComponent::translate("argument.pos.outofbounds", []), + ))); + } + let (yaw, pitch) = RotationArgumentConsumer::find_arg(args, ARG_ROTATION)?; - // todo command context - let world = server.worlds.read().await.first().unwrap().clone(); - for target in targets { - target - .clone() - .teleport(pos, Some(yaw), Some(pitch), world.clone()) - .await; - } + // todo command context + let world = server.worlds.read().await.first().unwrap().clone(); + for target in targets { + target + .clone() + .teleport(pos, Some(yaw), Some(pitch), world.clone()) + .await; + } - Ok(()) + Ok(()) + }) } } struct EntitiesToPosExecutor; -#[async_trait] impl CommandExecutor for EntitiesToPosExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let targets = EntitiesArgumentConsumer::find_arg(args, ARG_TARGETS)?; - let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; - if !World::is_valid(pos) { - return Err(CommandError::CommandFailed(Box::new( - TextComponent::translate("argument.pos.outofbounds", []), - ))); - } - // todo command context - let world = match sender { - CommandSender::Rcon(_) | CommandSender::Console => { - server.worlds.read().await.first().unwrap().clone() + let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; + if !World::is_valid(pos) { + return Err(CommandError::CommandFailed(Box::new( + TextComponent::translate("argument.pos.outofbounds", []), + ))); + } + // todo command context + let world = match sender { + CommandSender::Rcon(_) | CommandSender::Console => { + server.worlds.read().await.first().unwrap().clone() + } + CommandSender::Player(player) => player.world().clone(), + }; + for target in targets { + let yaw = target.get_entity().yaw.load(); + let pitch = target.get_entity().pitch.load(); + target + .clone() + .teleport(pos, Some(yaw), Some(pitch), world.clone()) + .await; } - CommandSender::Player(player) => player.world().clone(), - }; - for target in targets { - let yaw = target.get_entity().yaw.load(); - let pitch = target.get_entity().pitch.load(); - target - .clone() - .teleport(pos, Some(yaw), Some(pitch), world.clone()) - .await; - } - Ok(()) + Ok(()) + }) } } struct SelfToEntityExecutor; -#[async_trait] impl CommandExecutor for SelfToEntityExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?; - let pos = destination.get_entity().pos.load(); - let world = destination.get_entity().world.clone(); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let destination = EntityArgumentConsumer::find_arg(args, ARG_DESTINATION)?; + let pos = destination.get_entity().pos.load(); + let world = destination.get_entity().world.clone(); - match sender { - CommandSender::Player(player) => { - let yaw = player.living_entity.entity.yaw.load(); - let pitch = player.living_entity.entity.pitch.load(); - if !World::is_valid(pos) { - return Err(CommandError::CommandFailed(Box::new( - TextComponent::translate("argument.pos.outofbounds", []), - ))); + match sender { + CommandSender::Player(player) => { + let yaw = player.living_entity.entity.yaw.load(); + let pitch = player.living_entity.entity.pitch.load(); + if !World::is_valid(pos) { + return Err(CommandError::CommandFailed(Box::new( + TextComponent::translate("argument.pos.outofbounds", []), + ))); + } + player + .clone() + .teleport(pos, Some(yaw), Some(pitch), world) + .await; + } + _ => { + sender + .send_message(TextComponent::translate("permissions.requires.player", [])) + .await; } - player - .clone() - .teleport(pos, Some(yaw), Some(pitch), world) - .await; } - _ => { - sender - .send_message(TextComponent::translate("permissions.requires.player", [])) - .await; - } - } - Ok(()) + Ok(()) + }) } } struct SelfToPosExecutor; -#[async_trait] impl CommandExecutor for SelfToPosExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - match sender { - CommandSender::Player(player) => { - let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; - let yaw = player.living_entity.entity.yaw.load(); - let pitch = player.living_entity.entity.pitch.load(); - if !World::is_valid(pos) { - return Err(CommandError::CommandFailed(Box::new( - TextComponent::translate("argument.pos.outofbounds", []), - ))); + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + match sender { + CommandSender::Player(player) => { + let pos = Position3DArgumentConsumer::find_arg(args, ARG_LOCATION)?; + let yaw = player.living_entity.entity.yaw.load(); + let pitch = player.living_entity.entity.pitch.load(); + if !World::is_valid(pos) { + return Err(CommandError::CommandFailed(Box::new( + TextComponent::translate("argument.pos.outofbounds", []), + ))); + } + player + .clone() + .teleport(pos, Some(yaw), Some(pitch), player.world().clone()) + .await; + } + _ => { + sender + .send_message(TextComponent::translate("permissions.requires.player", [])) + .await; } - player - .clone() - .teleport(pos, Some(yaw), Some(pitch), player.world().clone()) - .await; } - _ => { - sender - .send_message(TextComponent::translate("permissions.requires.player", [])) - .await; - } - } - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/tellraw.rs b/pumpkin/src/command/commands/tellraw.rs index 538965c04..b28fe0d80 100644 --- a/pumpkin/src/command/commands/tellraw.rs +++ b/pumpkin/src/command/commands/tellraw.rs @@ -1,13 +1,11 @@ use crate::command::{ - CommandError, CommandExecutor, CommandSender, + CommandError, CommandExecutor, CommandResult, CommandSender, args::{ Arg, ConsumedArgs, FindArg, players::PlayersArgumentConsumer, textcomponent::TextComponentArgConsumer, }, - tree::CommandTree, - tree::builder::argument, + tree::{CommandTree, builder::argument}, }; -use async_trait::async_trait; const NAMES: [&str; 1] = ["tellraw"]; @@ -19,23 +17,24 @@ const ARG_MESSAGE: &str = "message"; struct TellRawExecutor; -#[async_trait] impl CommandExecutor for TellRawExecutor { - async fn execute<'a>( - &self, - _sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { - return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into()))); - }; + fn execute<'a>( + &'a self, + _sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { + return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into()))); + }; - let text = TextComponentArgConsumer::find_arg(args, ARG_MESSAGE)?; - for target in targets { - target.send_system_message(&text).await; - } - Ok(()) + let text = TextComponentArgConsumer::find_arg(args, ARG_MESSAGE)?; + for target in targets { + target.send_system_message(&text).await; + } + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/tick.rs b/pumpkin/src/command/commands/tick.rs index cbcd47975..88d1b6ca4 100644 --- a/pumpkin/src/command/commands/tick.rs +++ b/pumpkin/src/command/commands/tick.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_config::BASIC_CONFIG; use pumpkin_util::text::{ TextComponent, @@ -7,7 +6,7 @@ use pumpkin_util::text::{ use std::sync::atomic::Ordering; use crate::command::{ - CommandExecutor, CommandSender, + CommandExecutor, CommandResult, CommandSender, args::{ ConsumedArgs, FindArg, bounded_num::BoundedNumArgumentConsumer, time::TimeArgumentConsumer, }, @@ -199,104 +198,110 @@ impl TickExecutor { } } -#[async_trait] impl CommandExecutor for TickExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let manager = &server.tick_rate_manager; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let manager = &server.tick_rate_manager; - match self.0 { - SubCommand::Query => self.handle_query(sender, server, manager).await, - SubCommand::Rate => { - let rate = BoundedNumArgumentConsumer::::find_arg(args, "rate")??; - manager.set_tick_rate(server, rate).await; - sender - .send_message(TextComponent::translate( - "commands.tick.rate.success", - [TextComponent::text(format!("{rate:.1}"))], - )) - .await; - Ok(()) - } - SubCommand::RateLiteral(rate) => { - manager.set_tick_rate(server, rate).await; - sender - .send_message(TextComponent::translate( - "commands.tick.rate.success", - [TextComponent::text(format!("{rate:.1}"))], - )) - .await; - Ok(()) - } - SubCommand::Freeze(freeze) => { - manager.set_frozen(server, freeze).await; - let message_key = if freeze { - "commands.tick.status.frozen" - } else { - "commands.tick.status.running" - }; - sender - .send_message(TextComponent::translate(message_key, [])) - .await; - Ok(()) - } - SubCommand::StepDefault => self.handle_step_command(sender, server, manager, 1).await, - SubCommand::StepTimed => { - let ticks = TimeArgumentConsumer::find_arg(args, "time")?; - self.handle_step_command(sender, server, manager, ticks) - .await - } - SubCommand::StepLiteral(ticks) => { - self.handle_step_command(sender, server, manager, ticks) - .await - } - SubCommand::StepStop => { - if manager.stop_stepping(server).await { + match self.0 { + SubCommand::Query => self.handle_query(sender, server, manager).await, + SubCommand::Rate => { + let rate = BoundedNumArgumentConsumer::::find_arg(args, "rate")??; + manager.set_tick_rate(server, rate).await; sender .send_message(TextComponent::translate( - "commands.tick.step.stop.success", - [], + "commands.tick.rate.success", + [TextComponent::text(format!("{rate:.1}"))], )) .await; - } else { - sender - .send_message(TextComponent::translate("commands.tick.step.stop.fail", [])) - .await; + Ok(()) } - Ok(()) - } - SubCommand::SprintTimed => { - let ticks = TimeArgumentConsumer::find_arg(args, "time")?; - self.handle_sprint_command(sender, server, manager, ticks) - .await - } - SubCommand::SprintLiteral(ticks) => { - self.handle_sprint_command(sender, server, manager, ticks) - .await - } - SubCommand::SprintStop => { - if manager.stop_sprinting(server).await { + SubCommand::RateLiteral(rate) => { + manager.set_tick_rate(server, rate).await; sender .send_message(TextComponent::translate( - "commands.tick.sprint.stop.success", - [], + "commands.tick.rate.success", + [TextComponent::text(format!("{rate:.1}"))], )) .await; - } else { + Ok(()) + } + SubCommand::Freeze(freeze) => { + manager.set_frozen(server, freeze).await; + let message_key = if freeze { + "commands.tick.status.frozen" + } else { + "commands.tick.status.running" + }; sender - .send_message( - TextComponent::translate("commands.tick.sprint.stop.fail", []) - .color(Color::Named(NamedColor::Red)), - ) + .send_message(TextComponent::translate(message_key, [])) .await; + Ok(()) + } + SubCommand::StepDefault => { + self.handle_step_command(sender, server, manager, 1).await + } + SubCommand::StepTimed => { + let ticks = TimeArgumentConsumer::find_arg(args, "time")?; + self.handle_step_command(sender, server, manager, ticks) + .await + } + SubCommand::StepLiteral(ticks) => { + self.handle_step_command(sender, server, manager, ticks) + .await + } + SubCommand::StepStop => { + if manager.stop_stepping(server).await { + sender + .send_message(TextComponent::translate( + "commands.tick.step.stop.success", + [], + )) + .await; + } else { + sender + .send_message(TextComponent::translate( + "commands.tick.step.stop.fail", + [], + )) + .await; + } + Ok(()) + } + SubCommand::SprintTimed => { + let ticks = TimeArgumentConsumer::find_arg(args, "time")?; + self.handle_sprint_command(sender, server, manager, ticks) + .await + } + SubCommand::SprintLiteral(ticks) => { + self.handle_sprint_command(sender, server, manager, ticks) + .await + } + SubCommand::SprintStop => { + if manager.stop_sprinting(server).await { + sender + .send_message(TextComponent::translate( + "commands.tick.sprint.stop.success", + [], + )) + .await; + } else { + sender + .send_message( + TextComponent::translate("commands.tick.sprint.stop.fail", []) + .color(Color::Named(NamedColor::Red)), + ) + .await; + } + Ok(()) } - Ok(()) } - } + }) } } diff --git a/pumpkin/src/command/commands/time.rs b/pumpkin/src/command/commands/time.rs index 9032f490f..68f1c7543 100644 --- a/pumpkin/src/command/commands/time.rs +++ b/pumpkin/src/command/commands/time.rs @@ -1,12 +1,10 @@ -use async_trait::async_trait; use pumpkin_util::text::TextComponent; use pumpkin_util::text::color::{Color, NamedColor}; +use crate::command::CommandResult; use crate::command::args::{FindArg, time::TimeArgumentConsumer}; use crate::command::tree::builder::{argument, literal}; -use crate::command::{ - CommandError, CommandExecutor, CommandSender, ConsumedArgs, tree::CommandTree, -}; +use crate::command::{CommandExecutor, CommandSender, ConsumedArgs, tree::CommandTree}; const NAMES: [&str; 1] = ["time"]; const DESCRIPTION: &str = "Query the world time."; @@ -46,107 +44,109 @@ enum QueryMode { struct QueryExecutor(QueryMode); -#[async_trait] impl CommandExecutor for QueryExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let mode = self.0; - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let level_time = world.level_time.lock().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let mode = self.0; + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let level_time = world.level_time.lock().await; - let msg = match mode { - QueryMode::DayTime => { - let curr_time = level_time.query_daytime(); - TextComponent::translate( - "commands.time.query", - [TextComponent::text(curr_time.to_string())], - ) - } - QueryMode::GameTime => { - let curr_time = level_time.query_gametime(); - TextComponent::translate( - "commands.time.query", - [TextComponent::text(curr_time.to_string())], - ) - } - QueryMode::Day => { - let curr_time = level_time.query_day(); - TextComponent::translate( - "commands.time.query", - [TextComponent::text(curr_time.to_string())], - ) - } - }; + let msg = match mode { + QueryMode::DayTime => { + let curr_time = level_time.query_daytime(); + TextComponent::translate( + "commands.time.query", + [TextComponent::text(curr_time.to_string())], + ) + } + QueryMode::GameTime => { + let curr_time = level_time.query_gametime(); + TextComponent::translate( + "commands.time.query", + [TextComponent::text(curr_time.to_string())], + ) + } + QueryMode::Day => { + let curr_time = level_time.query_day(); + TextComponent::translate( + "commands.time.query", + [TextComponent::text(curr_time.to_string())], + ) + } + }; - sender.send_message(msg).await; - Ok(()) + sender.send_message(msg).await; + Ok(()) + }) } } struct ChangeExecutor(Mode); -#[async_trait] impl CommandExecutor for ChangeExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let time_count = if let Mode::Set(Some(preset)) = &self.0 { - preset.to_ticks() - } else if let Ok(ticks) = TimeArgumentConsumer::find_arg(args, ARG_TIME) { - ticks - } else { - sender - .send_message( - TextComponent::text("Invalid time specified.") - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let time_count = if let Mode::Set(Some(preset)) = &self.0 { + preset.to_ticks() + } else if let Ok(ticks) = TimeArgumentConsumer::find_arg(args, ARG_TIME) { + ticks + } else { + sender + .send_message( + TextComponent::text("Invalid time specified.") + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + }; - let mode = self.0; - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let mut level_time = world.level_time.lock().await; + let mode = self.0; + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let mut level_time = world.level_time.lock().await; - let msg = match mode { - Mode::Add => { - // add - level_time.add_time(time_count.into()); - level_time.send_time(world).await; - let curr_time = level_time.query_daytime(); - TextComponent::translate( - "commands.time.set", - [TextComponent::text(curr_time.to_string())], - ) - } - Mode::Set(_) => { - // set - level_time.set_time(time_count.into()); - level_time.send_time(world).await; - TextComponent::translate( - "commands.time.set", - [TextComponent::text(time_count.to_string())], - ) - } - }; + let msg = match mode { + Mode::Add => { + // add + level_time.add_time(time_count.into()); + level_time.send_time(world).await; + let curr_time = level_time.query_daytime(); + TextComponent::translate( + "commands.time.set", + [TextComponent::text(curr_time.to_string())], + ) + } + Mode::Set(_) => { + // set + level_time.set_time(time_count.into()); + level_time.send_time(world).await; + TextComponent::translate( + "commands.time.set", + [TextComponent::text(time_count.to_string())], + ) + } + }; - sender.send_message(msg).await; - Ok(()) + sender.send_message(msg).await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/title.rs b/pumpkin/src/command/commands/title.rs index 928a65afb..a152dba3e 100644 --- a/pumpkin/src/command/commands/title.rs +++ b/pumpkin/src/command/commands/title.rs @@ -1,7 +1,7 @@ -use async_trait::async_trait; use pumpkin_protocol::java::client::play::CClearTitle; use pumpkin_util::text::TextComponent; +use crate::command::CommandResult; use crate::entity::EntityBase; use crate::{ command::{ @@ -27,82 +27,84 @@ const ARG_TITLE: &str = "title"; /// bool: Whether to reset or not struct ClearOrResetExecutor(bool); -#[async_trait] impl CommandExecutor for ClearOrResetExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { - return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into()))); - }; - let reset = self.0; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { + return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into()))); + }; + let reset = self.0; - for target in targets { - target.client.enqueue_packet(&CClearTitle::new(reset)).await; - } - sender - .send_message(if targets.len() == 1 { - let text = if reset { - "commands.title.reset.single" + for target in targets { + target.client.enqueue_packet(&CClearTitle::new(reset)).await; + } + sender + .send_message(if targets.len() == 1 { + let text = if reset { + "commands.title.reset.single" + } else { + "commands.title.cleared.single" + }; + TextComponent::translate(text, [targets[0].get_display_name().await]) } else { - "commands.title.cleared.single" - }; - TextComponent::translate(text, [targets[0].get_display_name().await]) - } else { - let text = if reset { - "commands.title.reset.multiple" - } else { - "commands.title.cleared.multiple" - }; - TextComponent::translate(text, [TextComponent::text(targets.len().to_string())]) - }) - .await; + let text = if reset { + "commands.title.reset.multiple" + } else { + "commands.title.cleared.multiple" + }; + TextComponent::translate(text, [TextComponent::text(targets.len().to_string())]) + }) + .await; - Ok(()) + Ok(()) + }) } } struct TitleExecutor(TitleMode); -#[async_trait] impl CommandExecutor for TitleExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { - return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { + return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into()))); + }; - let text = TextComponentArgConsumer::find_arg(args, ARG_TITLE)?; + let text = TextComponentArgConsumer::find_arg(args, ARG_TITLE)?; - let mode = &self.0; + let mode = &self.0; - for target in targets { - target.show_title(&text, mode).await; - } + for target in targets { + target.show_title(&text, mode).await; + } - let mode_name = format!("{mode:?}").to_lowercase(); - sender - .send_message(if targets.len() == 1 { - TextComponent::translate( - format!("commands.title.show.{mode_name}.single"), - [targets[0].get_display_name().await], - ) - } else { - TextComponent::translate( - format!("commands.title.show.{mode_name}.multiple"), - [TextComponent::text(targets.len().to_string())], - ) - }) - .await; + let mode_name = format!("{mode:?}").to_lowercase(); + sender + .send_message(if targets.len() == 1 { + TextComponent::translate( + format!("commands.title.show.{mode_name}.single"), + [targets[0].get_display_name().await], + ) + } else { + TextComponent::translate( + format!("commands.title.show.{mode_name}.multiple"), + [TextComponent::text(targets.len().to_string())], + ) + }) + .await; - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/transfer.rs b/pumpkin/src/command/commands/transfer.rs index 1c2526a82..6878c885b 100644 --- a/pumpkin/src/command/commands/transfer.rs +++ b/pumpkin/src/command/commands/transfer.rs @@ -1,18 +1,16 @@ -use async_trait::async_trait; use pumpkin_protocol::codec::var_int::VarInt; use pumpkin_protocol::java::client::play::CTransfer; use pumpkin_util::text::TextComponent; use pumpkin_util::text::color::{Color, NamedColor}; +use crate::command::CommandResult; use crate::command::args::bounded_num::BoundedNumArgumentConsumer; use crate::command::args::players::PlayersArgumentConsumer; use crate::command::args::simple::SimpleArgConsumer; use crate::command::args::{Arg, FindArgDefaultName}; use crate::command::dispatcher::CommandError::{InvalidConsumption, InvalidRequirement}; use crate::command::tree::builder::{argument, argument_default_name, require}; -use crate::command::{ - CommandError, CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree, -}; +use crate::command::{CommandExecutor, CommandSender, args::ConsumedArgs, tree::CommandTree}; const NAMES: [&str; 1] = ["transfer"]; @@ -31,89 +29,91 @@ fn port_consumer() -> BoundedNumArgumentConsumer { struct TargetSelfExecutor; -#[async_trait] impl CommandExecutor for TargetSelfExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Simple(hostname)) = args.get(ARG_HOSTNAME) else { - return Err(InvalidConsumption(Some(ARG_HOSTNAME.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Simple(hostname)) = args.get(ARG_HOSTNAME) else { + return Err(InvalidConsumption(Some(ARG_HOSTNAME.into()))); + }; - let port = match port_consumer().find_arg_default_name(args) { - Err(_) => 25565, - Ok(Ok(count)) => count, - Ok(Err(_)) => { - sender - .send_message( - TextComponent::text("Port must be between 1 and 65535.") - .color(Color::Named(NamedColor::Red)), - ) + let port = match port_consumer().find_arg_default_name(args) { + Err(_) => 25565, + Ok(Ok(count)) => count, + Ok(Err(_)) => { + sender + .send_message( + TextComponent::text("Port must be between 1 and 65535.") + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + } + }; + + if let CommandSender::Player(player) = sender { + let name = &player.gameprofile.name; + log::info!("[{name}: Transferring {name} to {hostname}:{port}]"); + player + .client + .enqueue_packet(&CTransfer::new(hostname, VarInt(port))) .await; - return Ok(()); + Ok(()) + } else { + Err(InvalidRequirement) } - }; - - if let CommandSender::Player(player) = sender { - let name = &player.gameprofile.name; - log::info!("[{name}: Transferring {name} to {hostname}:{port}]"); - player - .client - .enqueue_packet(&CTransfer::new(hostname, VarInt(port))) - .await; - Ok(()) - } else { - Err(InvalidRequirement) - } + }) } } struct TargetPlayerExecutor; -#[async_trait] impl CommandExecutor for TargetPlayerExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Simple(hostname)) = args.get(ARG_HOSTNAME) else { - return Err(InvalidConsumption(Some(ARG_HOSTNAME.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Simple(hostname)) = args.get(ARG_HOSTNAME) else { + return Err(InvalidConsumption(Some(ARG_HOSTNAME.into()))); + }; - let port = match port_consumer().find_arg_default_name(args) { - Err(_) => 25565, - Ok(Ok(count)) => count, - Ok(Err(_)) => { - sender - .send_message( - TextComponent::text("Port must be between 1 and 65535.") - .color(Color::Named(NamedColor::Red)), - ) + let port = match port_consumer().find_arg_default_name(args) { + Err(_) => 25565, + Ok(Ok(count)) => count, + Ok(Err(_)) => { + sender + .send_message( + TextComponent::text("Port must be between 1 and 65535.") + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + } + }; + + let Some(Arg::Players(players)) = args.get(ARG_PLAYERS) else { + return Err(InvalidConsumption(Some(ARG_PLAYERS.into()))); + }; + + for p in players { + p.client + .enqueue_packet(&CTransfer::new(hostname, VarInt(port))) .await; - return Ok(()); + log::info!( + "[{sender}: Transferring {} to {hostname}:{port}]", + p.gameprofile.name + ); } - }; - let Some(Arg::Players(players)) = args.get(ARG_PLAYERS) else { - return Err(InvalidConsumption(Some(ARG_PLAYERS.into()))); - }; - - for p in players { - p.client - .enqueue_packet(&CTransfer::new(hostname, VarInt(port))) - .await; - log::info!( - "[{sender}: Transferring {} to {hostname}:{port}]", - p.gameprofile.name - ); - } - - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/weather.rs b/pumpkin/src/command/commands/weather.rs index 37e2e6244..41f383be7 100644 --- a/pumpkin/src/command/commands/weather.rs +++ b/pumpkin/src/command/commands/weather.rs @@ -1,11 +1,12 @@ -use async_trait::async_trait; use pumpkin_util::text::TextComponent; use crate::command::{ - CommandError, CommandExecutor, CommandSender, + CommandError, CommandExecutor, CommandResult, CommandSender, args::{ConsumedArgs, FindArg, time::TimeArgumentConsumer}, - tree::CommandTree, - tree::builder::{argument, literal}, + tree::{ + CommandTree, + builder::{argument, literal}, + }, }; const NAMES: [&str; 1] = ["weather"]; @@ -22,53 +23,54 @@ enum WeatherMode { Thunder, } -#[async_trait] impl CommandExecutor for Executor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &crate::server::Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let duration = TimeArgumentConsumer::find_arg(args, ARG_DURATION).unwrap_or(6000); - let world = { - let guard = server.worlds.read().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let duration = TimeArgumentConsumer::find_arg(args, ARG_DURATION).unwrap_or(6000); + let world = { + let guard = server.worlds.read().await; - guard - .first() - .cloned() - .ok_or(CommandError::InvalidRequirement)? - }; - let mut weather = world.weather.lock().await; + guard + .first() + .cloned() + .ok_or(CommandError::InvalidRequirement)? + }; + let mut weather = world.weather.lock().await; - match self.mode { - WeatherMode::Clear => { - weather - .set_weather_parameters(&world, duration, 0, false, false) - .await; - sender - .send_message(TextComponent::translate("commands.weather.set.clear", [])) - .await; + match self.mode { + WeatherMode::Clear => { + weather + .set_weather_parameters(&world, duration, 0, false, false) + .await; + sender + .send_message(TextComponent::translate("commands.weather.set.clear", [])) + .await; + } + WeatherMode::Rain => { + weather + .set_weather_parameters(&world, 0, duration, true, false) + .await; + sender + .send_message(TextComponent::translate("commands.weather.set.rain", [])) + .await; + } + WeatherMode::Thunder => { + weather + .set_weather_parameters(&world, 0, duration, true, true) + .await; + sender + .send_message(TextComponent::translate("commands.weather.set.thunder", [])) + .await; + } } - WeatherMode::Rain => { - weather - .set_weather_parameters(&world, 0, duration, true, false) - .await; - sender - .send_message(TextComponent::translate("commands.weather.set.rain", [])) - .await; - } - WeatherMode::Thunder => { - weather - .set_weather_parameters(&world, 0, duration, true, true) - .await; - sender - .send_message(TextComponent::translate("commands.weather.set.thunder", [])) - .await; - } - } - Ok(()) + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/whitelist.rs b/pumpkin/src/command/commands/whitelist.rs index a810e7336..bfbd07057 100644 --- a/pumpkin/src/command/commands/whitelist.rs +++ b/pumpkin/src/command/commands/whitelist.rs @@ -1,9 +1,9 @@ use std::sync::atomic::Ordering; -use async_trait::async_trait; use pumpkin_config::{BASIC_CONFIG, whitelist::WhitelistEntry}; use pumpkin_util::text::TextComponent; +use crate::command::CommandResult; use crate::entity::EntityBase; use crate::{ command::{ @@ -46,205 +46,211 @@ async fn kick_non_whitelisted_players(server: &Server) { struct OnExecutor; -#[async_trait] impl CommandExecutor for OnExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let previous = server.white_list.swap(true, Ordering::Relaxed); - if previous { - sender - .send_message(TextComponent::translate( - "commands.whitelist.alreadyOn", - &[], - )) - .await; - } else { - kick_non_whitelisted_players(server).await; - sender - .send_message(TextComponent::translate("commands.whitelist.enabled", &[])) - .await; - } - Ok(()) + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let previous = server.white_list.swap(true, Ordering::Relaxed); + if previous { + sender + .send_message(TextComponent::translate( + "commands.whitelist.alreadyOn", + &[], + )) + .await; + } else { + kick_non_whitelisted_players(server).await; + sender + .send_message(TextComponent::translate("commands.whitelist.enabled", &[])) + .await; + } + Ok(()) + }) } } struct OffExecutor; -#[async_trait] impl CommandExecutor for OffExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let previous = server.white_list.swap(false, Ordering::Relaxed); - if previous { - sender - .send_message(TextComponent::translate("commands.whitelist.disabled", &[])) - .await; - } else { - sender - .send_message(TextComponent::translate( - "commands.whitelist.alreadyOff", - &[], - )) - .await; - } - Ok(()) + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let previous = server.white_list.swap(false, Ordering::Relaxed); + if previous { + sender + .send_message(TextComponent::translate("commands.whitelist.disabled", &[])) + .await; + } else { + sender + .send_message(TextComponent::translate( + "commands.whitelist.alreadyOff", + &[], + )) + .await; + } + Ok(()) + }) } } struct ListExecutor; -#[async_trait] impl CommandExecutor for ListExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let whitelist = &WHITELIST_CONFIG.read().await.whitelist; - if whitelist.is_empty() { + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let whitelist = &WHITELIST_CONFIG.read().await.whitelist; + if whitelist.is_empty() { + sender + .send_message(TextComponent::translate("commands.whitelist.none", [])) + .await; + return Ok(()); + } + + let names = whitelist + .iter() + .map(|entry| entry.name.as_str()) + .collect::>() + .join(", "); + sender - .send_message(TextComponent::translate("commands.whitelist.none", [])) + .send_message(TextComponent::translate( + "commands.whitelist.list", + [ + TextComponent::text(whitelist.len().to_string()), + TextComponent::text(names), + ], + )) .await; - return Ok(()); - } - let names = whitelist - .iter() - .map(|entry| entry.name.as_str()) - .collect::>() - .join(", "); - - sender - .send_message(TextComponent::translate( - "commands.whitelist.list", - [ - TextComponent::text(whitelist.len().to_string()), - TextComponent::text(names), - ], - )) - .await; - - Ok(()) + Ok(()) + }) } } struct ReloadExecutor; -#[async_trait] impl CommandExecutor for ReloadExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - *WHITELIST_CONFIG.write().await = WhitelistConfig::load(); - kick_non_whitelisted_players(server).await; - sender - .send_message(TextComponent::translate("commands.whitelist.reloaded", &[])) - .await; - Ok(()) + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + *WHITELIST_CONFIG.write().await = WhitelistConfig::load(); + kick_non_whitelisted_players(server).await; + sender + .send_message(TextComponent::translate("commands.whitelist.reloaded", &[])) + .await; + Ok(()) + }) } } pub struct AddExecutor; -#[async_trait] impl CommandExecutor for AddExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - _server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { - return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + _server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { + return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into()))); + }; - let mut whitelist = WHITELIST_CONFIG.write().await; - for player in targets { - let profile = &player.gameprofile; - if whitelist.is_whitelisted(profile) { + let mut whitelist = WHITELIST_CONFIG.write().await; + for player in targets { + let profile = &player.gameprofile; + if whitelist.is_whitelisted(profile) { + sender + .send_message(TextComponent::translate( + "commands.whitelist.add.failed", + &[], + )) + .await; + continue; + } + whitelist + .whitelist + .push(WhitelistEntry::new(profile.id, profile.name.clone())); sender .send_message(TextComponent::translate( - "commands.whitelist.add.failed", - &[], + "commands.whitelist.add.success", + [TextComponent::text(profile.name.clone())], )) .await; - continue; } - whitelist - .whitelist - .push(WhitelistEntry::new(profile.id, profile.name.clone())); - sender - .send_message(TextComponent::translate( - "commands.whitelist.add.success", - [TextComponent::text(profile.name.clone())], - )) - .await; - } - whitelist.save(); - Ok(()) + whitelist.save(); + Ok(()) + }) } } pub struct RemoveExecutor; -#[async_trait] impl CommandExecutor for RemoveExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { - return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into()))); - }; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + let Some(Arg::Players(targets)) = args.get(&ARG_TARGETS) else { + return Err(CommandError::InvalidConsumption(Some(ARG_TARGETS.into()))); + }; - let mut whitelist = WHITELIST_CONFIG.write().await; - for player in targets { - let i = whitelist - .whitelist - .iter() - .position(|entry| entry.uuid == player.gameprofile.id); + let mut whitelist = WHITELIST_CONFIG.write().await; + for player in targets { + let i = whitelist + .whitelist + .iter() + .position(|entry| entry.uuid == player.gameprofile.id); - match i { - Some(i) => { - whitelist.whitelist.remove(i); - sender - .send_message(TextComponent::translate( - "commands.whitelist.remove.success", - [player.get_display_name().await], - )) - .await; - } - None => { - sender - .send_message(TextComponent::translate( - "commands.whitelist.remove.failed", - [], - )) - .await; + match i { + Some(i) => { + whitelist.whitelist.remove(i); + sender + .send_message(TextComponent::translate( + "commands.whitelist.remove.success", + [player.get_display_name().await], + )) + .await; + } + None => { + sender + .send_message(TextComponent::translate( + "commands.whitelist.remove.failed", + [], + )) + .await; + } } } - } - whitelist.save(); - drop(whitelist); + whitelist.save(); + drop(whitelist); - kick_non_whitelisted_players(server).await; - Ok(()) + kick_non_whitelisted_players(server).await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/commands/worldborder.rs b/pumpkin/src/command/commands/worldborder.rs index e316f0fa5..8d52f9695 100644 --- a/pumpkin/src/command/commands/worldborder.rs +++ b/pumpkin/src/command/commands/worldborder.rs @@ -1,4 +1,3 @@ -use async_trait::async_trait; use pumpkin_util::{ math::vector2::Vector2, text::{ @@ -7,17 +6,16 @@ use pumpkin_util::{ }, }; -use crate::{ - command::{ - CommandError, CommandExecutor, CommandSender, - args::{ - ConsumedArgs, DefaultNameArgConsumer, FindArgDefaultName, - bounded_num::BoundedNumArgumentConsumer, position_2d::Position2DArgumentConsumer, - }, - tree::CommandTree, - tree::builder::{argument_default_name, literal}, +use crate::command::{ + CommandExecutor, CommandResult, CommandSender, + args::{ + ConsumedArgs, DefaultNameArgConsumer, FindArgDefaultName, + bounded_num::BoundedNumArgumentConsumer, position_2d::Position2DArgumentConsumer, + }, + tree::{ + CommandTree, + builder::{argument_default_name, literal}, }, - server::Server, }; const NAMES: [&str; 1] = ["worldborder"]; @@ -50,269 +48,203 @@ fn warning_distance_consumer() -> BoundedNumArgumentConsumer { struct GetExecutor; -#[async_trait] impl CommandExecutor for GetExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - _args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let border = world.worldborder.lock().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + _args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let border = world.worldborder.lock().await; - let diameter = border.new_diameter.round() as i32; - sender - .send_message(TextComponent::translate( - "commands.worldborder.get", - [TextComponent::text(diameter.to_string())], - )) - .await; - Ok(()) + let diameter = border.new_diameter.round() as i32; + sender + .send_message(TextComponent::translate( + "commands.worldborder.get", + [TextComponent::text(diameter.to_string())], + )) + .await; + Ok(()) + }) } } struct SetExecutor; -#[async_trait] impl CommandExecutor for SetExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let mut border = world.worldborder.lock().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let mut border = world.worldborder.lock().await; - let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { - sender - .send_message( - TextComponent::text(format!( - "{} is out of bounds.", - distance_consumer().default_name() - )) - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; - - if (distance - border.new_diameter).abs() < f64::EPSILON { - sender - .send_message( - TextComponent::translate(NOTHING_CHANGED_EXCEPTION, []) + let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { + sender + .send_message( + TextComponent::text(format!( + "{} is out of bounds.", + distance_consumer().default_name() + )) .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - } + ) + .await; + return Ok(()); + }; - let dist = format!("{distance:.1}"); - sender - .send_message(TextComponent::translate( - "commands.worldborder.set.immediate", - [TextComponent::text(dist)], - )) - .await; - border.set_diameter(world, distance, None).await; - Ok(()) + if (distance - border.new_diameter).abs() < f64::EPSILON { + sender + .send_message( + TextComponent::translate(NOTHING_CHANGED_EXCEPTION, []) + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + } + + let dist = format!("{distance:.1}"); + sender + .send_message(TextComponent::translate( + "commands.worldborder.set.immediate", + [TextComponent::text(dist)], + )) + .await; + border.set_diameter(world, distance, None).await; + Ok(()) + }) } } struct SetTimeExecutor; -#[async_trait] impl CommandExecutor for SetTimeExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let mut border = world.worldborder.lock().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let mut border = world.worldborder.lock().await; - let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { - sender - .send_message( - TextComponent::text(format!( - "{} is out of bounds.", - distance_consumer().default_name() - )) - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; - let Ok(time) = time_consumer().find_arg_default_name(args)? else { - sender - .send_message( - TextComponent::text(format!( - "{} is out of bounds.", - time_consumer().default_name() - )) - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; - - match distance.total_cmp(&border.new_diameter) { - std::cmp::Ordering::Equal => { + let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { sender .send_message( - TextComponent::translate(NOTHING_CHANGED_EXCEPTION, []) - .color(Color::Named(NamedColor::Red)), + TextComponent::text(format!( + "{} is out of bounds.", + distance_consumer().default_name() + )) + .color(Color::Named(NamedColor::Red)), ) .await; return Ok(()); - } - std::cmp::Ordering::Less => { - let dist = format!("{distance:.1}"); + }; + let Ok(time) = time_consumer().find_arg_default_name(args)? else { sender - .send_message(TextComponent::translate( - "commands.worldborder.set.shrink", - [ - TextComponent::text(dist), - TextComponent::text(time.to_string()), - ], - )) + .send_message( + TextComponent::text(format!( + "{} is out of bounds.", + time_consumer().default_name() + )) + .color(Color::Named(NamedColor::Red)), + ) .await; - } - std::cmp::Ordering::Greater => { - let dist = format!("{distance:.1}"); - sender - .send_message(TextComponent::translate( - "commands.worldborder.set.grow", - [ - TextComponent::text(dist), - TextComponent::text(time.to_string()), - ], - )) - .await; - } - } + return Ok(()); + }; - border - .set_diameter(world, distance, Some(i64::from(time) * 1000)) - .await; - Ok(()) + match distance.total_cmp(&border.new_diameter) { + std::cmp::Ordering::Equal => { + sender + .send_message( + TextComponent::translate(NOTHING_CHANGED_EXCEPTION, []) + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + } + std::cmp::Ordering::Less => { + let dist = format!("{distance:.1}"); + sender + .send_message(TextComponent::translate( + "commands.worldborder.set.shrink", + [ + TextComponent::text(dist), + TextComponent::text(time.to_string()), + ], + )) + .await; + } + std::cmp::Ordering::Greater => { + let dist = format!("{distance:.1}"); + sender + .send_message(TextComponent::translate( + "commands.worldborder.set.grow", + [ + TextComponent::text(dist), + TextComponent::text(time.to_string()), + ], + )) + .await; + } + } + + border + .set_diameter(world, distance, Some(i64::from(time) * 1000)) + .await; + Ok(()) + }) } } struct AddExecutor; -#[async_trait] impl CommandExecutor for AddExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let mut border = world.worldborder.lock().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let mut border = world.worldborder.lock().await; - let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { - sender - .send_message( - TextComponent::text(format!( - "{} is out of bounds.", - distance_consumer().default_name() - )) - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; - - if distance == 0.0 { - sender - .send_message( - TextComponent::translate(NOTHING_CHANGED_EXCEPTION, []) + let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { + sender + .send_message( + TextComponent::text(format!( + "{} is out of bounds.", + distance_consumer().default_name() + )) .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - } + ) + .await; + return Ok(()); + }; - let distance = border.new_diameter + distance; - - let dist = format!("{distance:.1}"); - sender - .send_message(TextComponent::translate( - "commands.worldborder.set.immediate", - [TextComponent::text(dist)], - )) - .await; - border.set_diameter(world, distance, None).await; - Ok(()) - } -} - -struct AddTimeExecutor; - -#[async_trait] -impl CommandExecutor for AddTimeExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let mut border = world.worldborder.lock().await; - - let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { - sender - .send_message( - TextComponent::text(format!( - "{} is out of bounds.", - distance_consumer().default_name() - )) - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; - let Ok(time) = time_consumer().find_arg_default_name(args)? else { - sender - .send_message( - TextComponent::text(format!( - "{} is out of bounds.", - time_consumer().default_name() - )) - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; - - let distance = distance + border.new_diameter; - - match distance.total_cmp(&border.new_diameter) { - std::cmp::Ordering::Equal => { + if distance == 0.0 { sender .send_message( TextComponent::translate(NOTHING_CHANGED_EXCEPTION, []) @@ -321,275 +253,355 @@ impl CommandExecutor for AddTimeExecutor { .await; return Ok(()); } - std::cmp::Ordering::Less => { - let dist = format!("{distance:.1}"); - sender - .send_message(TextComponent::translate( - "commands.worldborder.set.shrink", - [ - TextComponent::text(dist), - TextComponent::text(time.to_string()), - ], - )) - .await; - } - std::cmp::Ordering::Greater => { - let dist = format!("{distance:.1}"); - sender - .send_message(TextComponent::translate( - "commands.worldborder.set.grow", - [ - TextComponent::text(dist), - TextComponent::text(time.to_string()), - ], - )) - .await; - } - } - border - .set_diameter(world, distance, Some(i64::from(time) * 1000)) - .await; - Ok(()) + let distance = border.new_diameter + distance; + + let dist = format!("{distance:.1}"); + sender + .send_message(TextComponent::translate( + "commands.worldborder.set.immediate", + [TextComponent::text(dist)], + )) + .await; + border.set_diameter(world, distance, None).await; + Ok(()) + }) + } +} + +struct AddTimeExecutor; + +impl CommandExecutor for AddTimeExecutor { + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let mut border = world.worldborder.lock().await; + + let Ok(distance) = distance_consumer().find_arg_default_name(args)? else { + sender + .send_message( + TextComponent::text(format!( + "{} is out of bounds.", + distance_consumer().default_name() + )) + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + }; + let Ok(time) = time_consumer().find_arg_default_name(args)? else { + sender + .send_message( + TextComponent::text(format!( + "{} is out of bounds.", + time_consumer().default_name() + )) + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + }; + + let distance = distance + border.new_diameter; + + match distance.total_cmp(&border.new_diameter) { + std::cmp::Ordering::Equal => { + sender + .send_message( + TextComponent::translate(NOTHING_CHANGED_EXCEPTION, []) + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + } + std::cmp::Ordering::Less => { + let dist = format!("{distance:.1}"); + sender + .send_message(TextComponent::translate( + "commands.worldborder.set.shrink", + [ + TextComponent::text(dist), + TextComponent::text(time.to_string()), + ], + )) + .await; + } + std::cmp::Ordering::Greater => { + let dist = format!("{distance:.1}"); + sender + .send_message(TextComponent::translate( + "commands.worldborder.set.grow", + [ + TextComponent::text(dist), + TextComponent::text(time.to_string()), + ], + )) + .await; + } + } + + border + .set_diameter(world, distance, Some(i64::from(time) * 1000)) + .await; + Ok(()) + }) } } struct CenterExecutor; -#[async_trait] impl CommandExecutor for CenterExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let mut border = world.worldborder.lock().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let mut border = world.worldborder.lock().await; - let Vector2 { x, y } = Position2DArgumentConsumer.find_arg_default_name(args)?; + let Vector2 { x, y } = Position2DArgumentConsumer.find_arg_default_name(args)?; - sender - .send_message(TextComponent::translate( - "commands.worldborder.center.success", - [ - TextComponent::text(format!("{x:.2}")), - TextComponent::text(format!("{y:.2}")), - ], - )) - .await; - border.set_center(world, x, y).await; - Ok(()) + sender + .send_message(TextComponent::translate( + "commands.worldborder.center.success", + [ + TextComponent::text(format!("{x:.2}")), + TextComponent::text(format!("{y:.2}")), + ], + )) + .await; + border.set_center(world, x, y).await; + Ok(()) + }) } } struct DamageAmountExecutor; -#[async_trait] impl CommandExecutor for DamageAmountExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let mut border = world.worldborder.lock().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let mut border = world.worldborder.lock().await; - let Ok(damage_per_block) = damage_per_block_consumer().find_arg_default_name(args)? else { - sender - .send_message( - TextComponent::text(format!( - "{} is out of bounds.", - damage_per_block_consumer().default_name() - )) - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; - - if (damage_per_block - border.damage_per_block).abs() < f32::EPSILON { - sender - .send_message( - TextComponent::translate("commands.worldborder.damage.amount.failed", []) + let Ok(damage_per_block) = damage_per_block_consumer().find_arg_default_name(args)? + else { + sender + .send_message( + TextComponent::text(format!( + "{} is out of bounds.", + damage_per_block_consumer().default_name() + )) .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - } + ) + .await; + return Ok(()); + }; - let damage = format!("{damage_per_block:.2}"); - sender - .send_message(TextComponent::translate( - "commands.worldborder.damage.amount.success", - [TextComponent::text(damage)], - )) - .await; - border.damage_per_block = damage_per_block; - Ok(()) + if (damage_per_block - border.damage_per_block).abs() < f32::EPSILON { + sender + .send_message( + TextComponent::translate("commands.worldborder.damage.amount.failed", []) + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + } + + let damage = format!("{damage_per_block:.2}"); + sender + .send_message(TextComponent::translate( + "commands.worldborder.damage.amount.success", + [TextComponent::text(damage)], + )) + .await; + border.damage_per_block = damage_per_block; + Ok(()) + }) } } struct DamageBufferExecutor; -#[async_trait] impl CommandExecutor for DamageBufferExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let mut border = world.worldborder.lock().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let mut border = world.worldborder.lock().await; - let Ok(buffer) = damage_buffer_consumer().find_arg_default_name(args)? else { - sender - .send_message( - TextComponent::text(format!( - "{} is out of bounds.", - damage_buffer_consumer().default_name() - )) - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; - - if (buffer - border.buffer).abs() < f32::EPSILON { - sender - .send_message( - TextComponent::translate("commands.worldborder.damage.buffer.failed", []) + let Ok(buffer) = damage_buffer_consumer().find_arg_default_name(args)? else { + sender + .send_message( + TextComponent::text(format!( + "{} is out of bounds.", + damage_buffer_consumer().default_name() + )) .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - } + ) + .await; + return Ok(()); + }; - let buf = format!("{buffer:.2}"); - sender - .send_message(TextComponent::translate( - "commands.worldborder.damage.buffer.success", - [TextComponent::text(buf)], - )) - .await; - border.buffer = buffer; - Ok(()) + if (buffer - border.buffer).abs() < f32::EPSILON { + sender + .send_message( + TextComponent::translate("commands.worldborder.damage.buffer.failed", []) + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + } + + let buf = format!("{buffer:.2}"); + sender + .send_message(TextComponent::translate( + "commands.worldborder.damage.buffer.success", + [TextComponent::text(buf)], + )) + .await; + border.buffer = buffer; + Ok(()) + }) } } struct WarningDistanceExecutor; -#[async_trait] impl CommandExecutor for WarningDistanceExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let mut border = world.worldborder.lock().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let mut border = world.worldborder.lock().await; - let Ok(distance) = warning_distance_consumer().find_arg_default_name(args)? else { - sender - .send_message( - TextComponent::text(format!( - "{} is out of bounds.", - warning_distance_consumer().default_name() - )) - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; - - if distance == border.warning_blocks { - sender - .send_message( - TextComponent::translate("commands.worldborder.warning.distance.failed", []) + let Ok(distance) = warning_distance_consumer().find_arg_default_name(args)? else { + sender + .send_message( + TextComponent::text(format!( + "{} is out of bounds.", + warning_distance_consumer().default_name() + )) .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - } + ) + .await; + return Ok(()); + }; - sender - .send_message(TextComponent::translate( - "commands.worldborder.warning.distance.success", - [TextComponent::text(distance.to_string())], - )) - .await; - border.set_warning_distance(world, distance).await; - Ok(()) + if distance == border.warning_blocks { + sender + .send_message( + TextComponent::translate( + "commands.worldborder.warning.distance.failed", + [], + ) + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + } + + sender + .send_message(TextComponent::translate( + "commands.worldborder.warning.distance.success", + [TextComponent::text(distance.to_string())], + )) + .await; + border.set_warning_distance(world, distance).await; + Ok(()) + }) } } struct WarningTimeExecutor; -#[async_trait] impl CommandExecutor for WarningTimeExecutor { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError> { - // TODO: Maybe ask player for world, or get the current world - let worlds = server.worlds.read().await; - let world = worlds - .first() - .expect("There should always be at least one world"); - let mut border = world.worldborder.lock().await; + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a crate::server::Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a> { + Box::pin(async move { + // TODO: Maybe ask player for world, or get the current world + let worlds = server.worlds.read().await; + let world = worlds + .first() + .expect("There should always be at least one world"); + let mut border = world.worldborder.lock().await; - let Ok(time) = time_consumer().find_arg_default_name(args)? else { - sender - .send_message( - TextComponent::text(format!( - "{} is out of bounds.", - time_consumer().default_name() - )) - .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - }; - - if time == border.warning_time { - sender - .send_message( - TextComponent::translate("commands.worldborder.warning.time.failed", []) + let Ok(time) = time_consumer().find_arg_default_name(args)? else { + sender + .send_message( + TextComponent::text(format!( + "{} is out of bounds.", + time_consumer().default_name() + )) .color(Color::Named(NamedColor::Red)), - ) - .await; - return Ok(()); - } + ) + .await; + return Ok(()); + }; - sender - .send_message(TextComponent::translate( - "commands.worldborder.warning.time.success", - [TextComponent::text(time.to_string())], - )) - .await; - border.set_warning_delay(world, time).await; - Ok(()) + if time == border.warning_time { + sender + .send_message( + TextComponent::translate("commands.worldborder.warning.time.failed", []) + .color(Color::Named(NamedColor::Red)), + ) + .await; + return Ok(()); + } + + sender + .send_message(TextComponent::translate( + "commands.worldborder.warning.time.success", + [TextComponent::text(time.to_string())], + )) + .await; + border.set_warning_delay(world, time).await; + Ok(()) + }) } } diff --git a/pumpkin/src/command/dispatcher.rs b/pumpkin/src/command/dispatcher.rs index 52dac1065..d8a34ff8d 100644 --- a/pumpkin/src/command/dispatcher.rs +++ b/pumpkin/src/command/dispatcher.rs @@ -64,7 +64,7 @@ pub struct CommandDispatcher { impl CommandDispatcher { pub async fn handle_command<'a>( &'a self, - sender: &mut CommandSender, + sender: &CommandSender, server: &'a Server, cmd: &'a str, ) { @@ -242,7 +242,7 @@ impl CommandDispatcher { /// Execute a command using its corresponding [`CommandTree`]. pub(crate) async fn dispatch<'a>( &'a self, - src: &mut CommandSender, + src: &CommandSender, server: &'a Server, cmd: &'a str, ) -> Result<(), CommandError> { @@ -302,7 +302,7 @@ impl CommandDispatcher { } async fn try_is_fitting_path<'a>( - src: &mut CommandSender, + src: &'a CommandSender, server: &'a Server, path: &[usize], tree: &'a CommandTree, @@ -357,7 +357,7 @@ impl CommandDispatcher { } async fn try_find_suggestions_on_path<'a>( - src: &CommandSender, + src: &'a CommandSender, server: &'a Server, path: &[usize], tree: &'a CommandTree, diff --git a/pumpkin/src/command/mod.rs b/pumpkin/src/command/mod.rs index 93e2ae505..5162fa442 100644 --- a/pumpkin/src/command/mod.rs +++ b/pumpkin/src/command/mod.rs @@ -1,4 +1,5 @@ use std::fmt; +use std::pin::Pin; use std::str::FromStr; use std::sync::Arc; @@ -6,7 +7,6 @@ use crate::entity::player::Player; use crate::server::Server; use crate::world::World; use args::ConsumedArgs; -use async_trait::async_trait; use dispatcher::CommandError; use pumpkin_util::math::vector3::Vector3; @@ -118,12 +118,13 @@ impl CommandSender { } } -#[async_trait] -pub trait CommandExecutor: Sync { - async fn execute<'a>( - &self, - sender: &mut CommandSender, - server: &Server, - args: &ConsumedArgs<'a>, - ) -> Result<(), CommandError>; +pub type CommandResult<'a> = Pin> + Send + 'a>>; + +pub trait CommandExecutor: Sync + Send { + fn execute<'a>( + &'a self, + sender: &'a CommandSender, + server: &'a Server, + args: &'a ConsumedArgs<'a>, + ) -> CommandResult<'a>; } diff --git a/pumpkin/src/command/tree/builder.rs b/pumpkin/src/command/tree/builder.rs index 35bfc6b51..1f6375943 100644 --- a/pumpkin/src/command/tree/builder.rs +++ b/pumpkin/src/command/tree/builder.rs @@ -39,7 +39,7 @@ impl CommandTree { /// /// Also see [`NonLeafNodeBuilder::execute`]. #[must_use] - pub fn execute(mut self, executor: impl CommandExecutor + 'static + Send) -> Self { + pub fn execute(mut self, executor: impl CommandExecutor + 'static) -> Self { let node = Node { node_type: NodeType::ExecuteLeaf { executor: Arc::new(executor), @@ -116,7 +116,7 @@ impl NonLeafNodeBuilder { /// /// Also see [`CommandTree::execute`]. #[must_use] - pub fn execute(mut self, executor: impl CommandExecutor + 'static + Send) -> Self { + pub fn execute(mut self, executor: impl CommandExecutor + 'static) -> Self { self.leaf_nodes.push(LeafNodeBuilder { node_type: NodeType::ExecuteLeaf { executor: Arc::new(executor), @@ -149,7 +149,7 @@ pub fn literal(string: impl Into) -> NonLeafNodeBuilder { /// reversed, so [`Vec::pop`] can be used to obtain args in ltr order. pub fn argument( name: impl Into, - consumer: impl ArgumentConsumer + 'static + Send, + consumer: impl ArgumentConsumer + 'static, ) -> NonLeafNodeBuilder { NonLeafNodeBuilder { node_type: NodeType::Argument { diff --git a/pumpkin/src/lib.rs b/pumpkin/src/lib.rs index 36305f1fb..f0d3cc816 100644 --- a/pumpkin/src/lib.rs +++ b/pumpkin/src/lib.rs @@ -489,7 +489,7 @@ async fn setup_stdin_console(server: Arc) { 'after: { let dispatcher = &server.command_dispatcher.read().await; dispatcher - .handle_command(&mut command::CommandSender::Console, &server, command.as_str()) + .handle_command(&command::CommandSender::Console, &server, command.as_str()) .await; }; }} @@ -522,7 +522,7 @@ fn setup_console(rl: Readline, server: Arc) { let dispatcher = server.command_dispatcher.read().await; dispatcher - .handle_command(&mut command::CommandSender::Console, &server, &line) + .handle_command(&command::CommandSender::Console, &server, &line) .await; rl.add_history_entry(line).unwrap(); } diff --git a/pumpkin/src/net/bedrock/play.rs b/pumpkin/src/net/bedrock/play.rs index 04dd09740..0df176288 100644 --- a/pumpkin/src/net/bedrock/play.rs +++ b/pumpkin/src/net/bedrock/play.rs @@ -225,7 +225,7 @@ impl BedrockClient { let dispatcher = server_clone.command_dispatcher.read().await; dispatcher .handle_command( - &mut CommandSender::Player(player_clone), + &CommandSender::Player(player_clone), &server_clone, &command_clone, ) diff --git a/pumpkin/src/net/java/play.rs b/pumpkin/src/net/java/play.rs index e5fe6977b..5fca5bccb 100644 --- a/pumpkin/src/net/java/play.rs +++ b/pumpkin/src/net/java/play.rs @@ -544,7 +544,7 @@ impl JavaClient { server.spawn_task(async move { server_clone.command_dispatcher.read().await .handle_command( - &mut CommandSender::Player(player_clone), + &CommandSender::Player(player_clone), &server_clone, &command_clone, ) diff --git a/pumpkin/src/net/rcon/mod.rs b/pumpkin/src/net/rcon/mod.rs index 18d17403f..76c104080 100644 --- a/pumpkin/src/net/rcon/mod.rs +++ b/pumpkin/src/net/rcon/mod.rs @@ -131,7 +131,7 @@ impl RCONClient { .read() .await .handle_command( - &mut crate::command::CommandSender::Rcon(output_clone), + &crate::command::CommandSender::Rcon(output_clone), &server_clone, &packet_body, )