diff --git a/pumpkin/src/command/argument_types/coordinates/vec3.rs b/pumpkin/src/command/argument_types/coordinates/vec3.rs index e25aa84b7..e29f571bc 100644 --- a/pumpkin/src/command/argument_types/coordinates/vec3.rs +++ b/pumpkin/src/command/argument_types/coordinates/vec3.rs @@ -1,9 +1,11 @@ use crate::command::argument_types::argument_type::{ArgumentType, JavaClientArgumentType}; use crate::command::argument_types::coordinates::Coordinates; +use crate::command::context::command_context::CommandContext; use crate::command::errors::command_syntax_error::CommandSyntaxError; use crate::command::errors::error_types::CommandErrorType; use crate::command::string_reader::StringReader; use pumpkin_data::translation; +use pumpkin_util::math::vector3::Vector3; pub const INCOMPLETE_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(translation::ARGUMENT_POS3D_INCOMPLETE); @@ -52,6 +54,24 @@ impl ArgumentType for Vec3ArgumentType { } } +impl Vec3ArgumentType { + /// Returns a [`CommandContext`]'s parsed three-dimensional vector as a set of [`Coordinates`]. + pub fn get_coordinates( + context: &CommandContext, + name: &str, + ) -> Result { + Ok(*context.get_argument(name)?) + } + + /// Returns a [`CommandContext`]'s parsed three-dimensional vector and resolves it to a [`Vector3`]. + pub fn get_vector3( + context: &CommandContext, + name: &str, + ) -> Result, CommandSyntaxError> { + Ok(Self::get_coordinates(context, name)?.resolve(context.source.as_ref())) + } +} + #[cfg(test)] mod test { use crate::command::argument_types::argument_type::ArgumentType; diff --git a/pumpkin/src/command/argument_types/core/bool.rs b/pumpkin/src/command/argument_types/core/bool.rs index ddbf30640..38731b948 100644 --- a/pumpkin/src/command/argument_types/core/bool.rs +++ b/pumpkin/src/command/argument_types/core/bool.rs @@ -24,6 +24,8 @@ impl ArgumentType for BoolArgumentType { } } +impl_copy_get!(BoolArgumentType, bool); + #[cfg(test)] mod test { use crate::command::{ diff --git a/pumpkin/src/command/argument_types/core/double.rs b/pumpkin/src/command/argument_types/core/double.rs index cb0b68055..cb03ee786 100644 --- a/pumpkin/src/command/argument_types/core/double.rs +++ b/pumpkin/src/command/argument_types/core/double.rs @@ -43,6 +43,8 @@ impl ArgumentType for DoubleArgumentType { } } +impl_copy_get!(DoubleArgumentType, f64); + impl DoubleArgumentType { /// Constructs a new [`DoubleArgumentType`] with no minimum or maximum bounds. #[must_use] diff --git a/pumpkin/src/command/argument_types/core/float.rs b/pumpkin/src/command/argument_types/core/float.rs index 5468c073d..ecf581487 100644 --- a/pumpkin/src/command/argument_types/core/float.rs +++ b/pumpkin/src/command/argument_types/core/float.rs @@ -43,6 +43,8 @@ impl ArgumentType for FloatArgumentType { } } +impl_copy_get!(FloatArgumentType, f32); + impl FloatArgumentType { /// Constructs a new [`FloatArgumentType`] with no minimum or maximum bounds. #[must_use] diff --git a/pumpkin/src/command/argument_types/core/integer.rs b/pumpkin/src/command/argument_types/core/integer.rs index c940f44d7..5b576a000 100644 --- a/pumpkin/src/command/argument_types/core/integer.rs +++ b/pumpkin/src/command/argument_types/core/integer.rs @@ -43,6 +43,8 @@ impl ArgumentType for IntegerArgumentType { } } +impl_copy_get!(IntegerArgumentType, i32); + impl IntegerArgumentType { /// Constructs a new [`IntegerArgumentType`] with no minimum or maximum bounds. #[must_use] diff --git a/pumpkin/src/command/argument_types/core/long.rs b/pumpkin/src/command/argument_types/core/long.rs index e040b9751..018fd6aee 100644 --- a/pumpkin/src/command/argument_types/core/long.rs +++ b/pumpkin/src/command/argument_types/core/long.rs @@ -43,6 +43,8 @@ impl ArgumentType for LongArgumentType { } } +impl_copy_get!(LongArgumentType, i64); + impl LongArgumentType { /// Constructs a new [`LongArgumentType`] with no minimum or maximum bounds. #[must_use] diff --git a/pumpkin/src/command/argument_types/core/string.rs b/pumpkin/src/command/argument_types/core/string.rs index 4e8b9e1f7..06946e531 100644 --- a/pumpkin/src/command/argument_types/core/string.rs +++ b/pumpkin/src/command/argument_types/core/string.rs @@ -1,5 +1,6 @@ use pumpkin_protocol::java::client::play::StringProtoArgBehavior; +use crate::command::context::command_context::CommandContext; use crate::command::{ argument_types::argument_type::{ArgumentType, JavaClientArgumentType}, errors::command_syntax_error::CommandSyntaxError, @@ -49,6 +50,13 @@ impl ArgumentType for StringArgumentType { } } +impl StringArgumentType { + /// Returns a [`CommandContext`]'s parsed `String` argument as a string slice. + pub fn get<'a>(context: &'a CommandContext, name: &str) -> Result<&'a str, CommandSyntaxError> { + Ok(context.get_argument::(name)?.as_str()) + } +} + #[cfg(test)] mod test { use crate::command::{ diff --git a/pumpkin/src/command/argument_types/entity.rs b/pumpkin/src/command/argument_types/entity.rs index 48c9078cd..ae4a669fd 100644 --- a/pumpkin/src/command/argument_types/entity.rs +++ b/pumpkin/src/command/argument_types/entity.rs @@ -1,10 +1,14 @@ use crate::command::argument_types::argument_type::{ArgumentType, JavaClientArgumentType}; use crate::command::argument_types::entity_selector::EntitySelector; use crate::command::argument_types::entity_selector::parser::EntitySelectorParser; +use crate::command::context::command_context::CommandContext; use crate::command::errors::command_syntax_error::CommandSyntaxError; use crate::command::errors::error_types::CommandErrorType; use crate::command::string_reader::StringReader; +use crate::entity::EntityBase; +use crate::entity::player::Player; use pumpkin_data::translation; +use std::sync::Arc; /// A [`CommandErrorType`] to tell that no entities could be found. pub const NO_ENTITIES_ERROR_TYPE: CommandErrorType<0> = @@ -101,4 +105,74 @@ impl EntityArgumentType { Ok(selector) } } + + /// Tries to get a single entity from a parsed argument of the provided [`CommandContext`]. + pub async fn get_entity( + context: &CommandContext<'_>, + name: &str, + ) -> Result, CommandSyntaxError> { + context + .get_argument::(name)? + .find_single_entity(context.source.as_ref()) + .await + } + + /// Tries to get at least 1 entity from a parsed argument of the provided [`CommandContext`]. + pub async fn get_entities( + context: &CommandContext<'_>, + name: &str, + ) -> Result>, CommandSyntaxError> { + let entities = Self::get_optional_entities(context, name).await?; + if entities.is_empty() { + Err(NO_ENTITIES_ERROR_TYPE.create_without_context()) + } else { + Ok(entities) + } + } + + /// Tries to get any number of entities from a parsed argument of the provided [`CommandContext`]. + pub async fn get_optional_entities( + context: &CommandContext<'_>, + name: &str, + ) -> Result>, CommandSyntaxError> { + context + .get_argument::(name)? + .find_entities(context.source.as_ref()) + .await + } + + /// Tries to get a single player from a parsed argument of the provided [`CommandContext`]. + pub async fn get_player( + context: &CommandContext<'_>, + name: &str, + ) -> Result, CommandSyntaxError> { + context + .get_argument::(name)? + .find_single_player(context.source.as_ref()) + .await + } + + /// Tries to get at least 1 player from a parsed argument of the provided [`CommandContext`]. + pub async fn get_players( + context: &CommandContext<'_>, + name: &str, + ) -> Result>, CommandSyntaxError> { + let players = Self::get_optional_players(context, name).await?; + if players.is_empty() { + Err(NO_PLAYERS_ERROR_TYPE.create_without_context()) + } else { + Ok(players) + } + } + + /// Tries to get any number of players from a parsed argument of the provided [`CommandContext`]. + pub async fn get_optional_players( + context: &CommandContext<'_>, + name: &str, + ) -> Result>, CommandSyntaxError> { + context + .get_argument::(name)? + .find_players(context.source.as_ref()) + .await + } } diff --git a/pumpkin/src/command/argument_types/entity_selector/mod.rs b/pumpkin/src/command/argument_types/entity_selector/mod.rs index 22a1c1e6a..f1abb69b7 100644 --- a/pumpkin/src/command/argument_types/entity_selector/mod.rs +++ b/pumpkin/src/command/argument_types/entity_selector/mod.rs @@ -2,9 +2,7 @@ mod option; pub mod parser; use crate::command::argument_types::entity; -use crate::command::argument_types::entity::{ - ENTITY_SELECTOR_PERMISSION, NO_ENTITIES_ERROR_TYPE, NO_PLAYERS_ERROR_TYPE, -}; +use crate::command::argument_types::entity::ENTITY_SELECTOR_PERMISSION; use crate::command::argument_types::entity_selector::parser::SELECTORS_NOT_ALLOWED_ERROR_TYPE; use crate::command::context::command_source::CommandSource; use crate::command::errors::command_syntax_error::CommandSyntaxError; @@ -84,7 +82,7 @@ impl EntitySelector { &self, source: &CommandSource, ) -> Result, CommandSyntaxError> { - let list = self.find_optional_entities(source).await?; + let list = self.find_entities(source).await?; match list.as_slice() { [] => Err(entity::NO_ENTITIES_ERROR_TYPE.create_without_context()), [entity] => Ok(entity.clone()), @@ -92,28 +90,14 @@ impl EntitySelector { } } - /// Tries to find any entities represented by this selector. - /// If none are found, an error is returned. - pub async fn find_entities( - &self, - source: &CommandSource, - ) -> Result>, CommandSyntaxError> { - let entities = self.find_optional_entities(source).await?; - if entities.is_empty() { - Err(NO_ENTITIES_ERROR_TYPE.create_without_context()) - } else { - Ok(entities) - } - } - /// Tries to find any entities represented by this selector. If none are found, an empty `Vec` will still be returned. - pub async fn find_optional_entities( + pub async fn find_entities( &self, source: &CommandSource, ) -> Result>, CommandSyntaxError> { self.check_permissions(source).await?; if !self.includes_entities { - self.find_optional_players(source) + self.find_players(source) .await .map(|v| v.into_iter().map(|p| p as Arc).collect()) } else if let Some(name) = self.player_name.as_ref() { @@ -176,7 +160,7 @@ impl EntitySelector { &self, source: &CommandSource, ) -> Result, CommandSyntaxError> { - let list = self.find_optional_players(source).await?; + let list = self.find_players(source).await?; if list.len() == 1 { Ok(list.first().unwrap().clone()) } else { @@ -185,23 +169,9 @@ impl EntitySelector { } /// Tries to find any players represented by this selector. - /// If none are found, an error is returned. pub async fn find_players( &self, source: &CommandSource, - ) -> Result>, CommandSyntaxError> { - let players = self.find_optional_players(source).await?; - if players.is_empty() { - Err(NO_PLAYERS_ERROR_TYPE.create_without_context()) - } else { - Ok(players) - } - } - - /// Tries to find any players represented by this selector. If none are found, an empty `Vec` will still be returned. - pub async fn find_optional_players( - &self, - source: &CommandSource, ) -> Result>, CommandSyntaxError> { self.check_permissions(source).await?; if let Some(name) = self.player_name.as_ref() { diff --git a/pumpkin/src/command/argument_types/mod.rs b/pumpkin/src/command/argument_types/mod.rs index 83277b171..25fa8d5eb 100644 --- a/pumpkin/src/command/argument_types/mod.rs +++ b/pumpkin/src/command/argument_types/mod.rs @@ -51,6 +51,18 @@ macro_rules! assert_parse_err_reset { }; } +/// Macro to implement a single `get()` function for an argument type whose `Item` is `Copy`. +macro_rules! impl_copy_get { + ($ty:ty, $item:ty) => { + impl $ty { + #[doc = concat!("Returns a [`CommandContext`]'s parsed `", stringify!($item), "` argument.")] + pub fn get(context: &$crate::command::context::command_context::CommandContext, name: &str) -> Result<$item, CommandSyntaxError> { + Ok(*context.get_argument(name)?) + } + } + }; +} + const EMPTY_BOUNDS_ERROR_TYPE: CommandErrorType<0> = CommandErrorType::new(translation::ARGUMENT_RANGE_EMPTY); const SWAPPED_BOUNDS_ERROR_TYPE: CommandErrorType<0> = diff --git a/pumpkin/src/command/argument_types/range.rs b/pumpkin/src/command/argument_types/range.rs index bd5372fe0..41b0de18b 100644 --- a/pumpkin/src/command/argument_types/range.rs +++ b/pumpkin/src/command/argument_types/range.rs @@ -32,6 +32,8 @@ impl ArgumentType for IntRangeArgumentType { } } +impl_copy_get!(IntRangeArgumentType, IntBounds); + /// Parses an inclusive range of `f64`s that can be represented in the following ways: /// - `value`: Only includes the number `value`. /// - `min..`: All numbers above or equal to `min`. @@ -55,6 +57,8 @@ impl ArgumentType for FloatRangeArgumentType { } } +impl_copy_get!(FloatRangeArgumentType, DoubleBounds); + #[cfg(test)] mod test { use pumpkin_util::math::bounds::{DoubleBounds, IntBounds}; diff --git a/pumpkin/src/command/argument_types/time.rs b/pumpkin/src/command/argument_types/time.rs index 9801ee21a..6ac9410c4 100644 --- a/pumpkin/src/command/argument_types/time.rs +++ b/pumpkin/src/command/argument_types/time.rs @@ -1,4 +1,5 @@ use crate::command::argument_types::argument_type::{ArgumentType, JavaClientArgumentType}; +use crate::command::context::command_context::CommandContext; use crate::command::errors::command_syntax_error::CommandSyntaxError; use crate::command::errors::error_types::CommandErrorType; use crate::command::string_reader::StringReader; @@ -71,6 +72,14 @@ impl ArgumentType for TimeArgumentType { } } +impl TimeArgumentType { + /// Returns a [`CommandContext`]'s parsed time argument in the form + /// of its duration, in ticks. + pub fn get(context: &CommandContext, name: &str) -> Result { + Ok(*context.get_argument(name)?) + } +} + #[cfg(test)] mod test { use crate::command::{ diff --git a/pumpkin/src/command/commands/kill.rs b/pumpkin/src/command/commands/kill.rs index 90280da5a..613834456 100644 --- a/pumpkin/src/command/commands/kill.rs +++ b/pumpkin/src/command/commands/kill.rs @@ -1,6 +1,5 @@ use crate::command::argument_builder::{ArgumentBuilder, argument, command}; use crate::command::argument_types::entity::EntityArgumentType; -use crate::command::argument_types::entity_selector::EntitySelector; use crate::command::context::command_context::CommandContext; use crate::command::node::dispatcher::CommandDispatcher; use crate::command::node::{CommandExecutor, CommandExecutorResult}; @@ -20,8 +19,7 @@ struct TargetsExecutor; impl CommandExecutor for TargetsExecutor { fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> { Box::pin(async move { - let selector: &EntitySelector = context.get_argument(ARG_TARGETS)?; - let targets = selector.find_entities(&context.source).await?; + let targets = EntityArgumentType::get_entities(context, ARG_TARGETS).await?; let target_count = targets.len(); for target in &targets { diff --git a/pumpkin/src/command/commands/setidletimeout.rs b/pumpkin/src/command/commands/setidletimeout.rs index b3e9ab340..182db5445 100644 --- a/pumpkin/src/command/commands/setidletimeout.rs +++ b/pumpkin/src/command/commands/setidletimeout.rs @@ -23,7 +23,7 @@ struct SetIdleTimeoutExecutor; impl CommandExecutor for SetIdleTimeoutExecutor { fn execute<'a>(&'a self, context: &'a CommandContext) -> CommandExecutorResult<'a> { Box::pin(async move { - let minutes: i32 = *context.get_argument(ARG_MINUTES)?; + let minutes: i32 = IntegerArgumentType::get(context, ARG_MINUTES)?; context .server()