From 29b3c8260b7d3cd1c4251f6878f45a9ceae23640 Mon Sep 17 00:00:00 2001 From: kralverde <80051564+kralverde@users.noreply.github.com> Date: Fri, 25 Oct 2024 17:10:38 -0400 Subject: [PATCH] make consumers async (#187) --- pumpkin/src/client/player_packet.rs | 3 +- pumpkin/src/commands/arg_player.rs | 64 +++++++++++++++++----------- pumpkin/src/commands/cmd_echest.rs | 6 +-- pumpkin/src/commands/cmd_gamemode.rs | 61 +++++++++++++++----------- pumpkin/src/commands/cmd_help.rs | 41 +++++++++++------- pumpkin/src/commands/cmd_kick.rs | 12 +++--- pumpkin/src/commands/cmd_kill.rs | 13 +++--- pumpkin/src/commands/cmd_pumpkin.rs | 6 +-- pumpkin/src/commands/cmd_stop.rs | 6 +-- pumpkin/src/commands/dispatcher.rs | 20 ++++----- pumpkin/src/commands/mod.rs | 6 +-- pumpkin/src/commands/tree.rs | 15 +++++-- pumpkin/src/commands/tree_builder.rs | 2 +- pumpkin/src/world/mod.rs | 11 ----- 14 files changed, 146 insertions(+), 120 deletions(-) diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index ee28c8127..8c84ca216 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -347,8 +347,6 @@ impl Player { } pub async fn handle_chat_message(&self, chat_message: SChatMessage) { - log::debug!("Received chat message"); - let message = chat_message.message; if message.len() > 256 { self.kick(TextComponent::text("Oversized message")).await; @@ -357,6 +355,7 @@ impl Player { // TODO: filter message & validation let gameprofile = &self.gameprofile; + log::info!("{}: {}", gameprofile.name, message); let entity = &self.living_entity.entity; let world = &entity.world; diff --git a/pumpkin/src/commands/arg_player.rs b/pumpkin/src/commands/arg_player.rs index ef59c4dae..f842a44fd 100644 --- a/pumpkin/src/commands/arg_player.rs +++ b/pumpkin/src/commands/arg_player.rs @@ -1,48 +1,60 @@ use std::sync::Arc; +use async_trait::async_trait; + use crate::commands::dispatcher::InvalidTreeError; use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError; use crate::commands::tree::{ConsumedArgs, RawArgs}; use crate::commands::CommandSender; use crate::server::Server; +use super::tree::ArgumentConsumer; + +/// TODO: Seperate base functionality of these two methods into single method + /// todo: implement (so far only own name + @s/@p is implemented) -pub fn consume_arg_player( - src: &CommandSender, - server: &Server, - args: &mut RawArgs, -) -> Result> { - if let Some(arg) = args.pop() { - match arg { - "@s" => { - if src.is_player() { - return Ok(arg.into()); +pub(crate) struct PlayerArgumentConsumer {} + +#[async_trait] +impl ArgumentConsumer for PlayerArgumentConsumer { + async fn consume<'a>( + &self, + src: &CommandSender<'a>, + server: &Server, + args: &mut RawArgs<'a>, + ) -> Result> { + if let Some(arg) = args.pop() { + match arg { + "@s" => { + if src.is_player() { + return Ok(arg.into()); + } + return Err(Some("You are not a Player".into())); } - return Err(Some("You are not a Player".into())); - } - "@p" if src.is_player() => return Ok(arg.into()), - "@r" => todo!(), // todo: implement random player target selector - "@a" | "@e" => todo!(), // todo: implement all players target selector - name => { - // todo: implement any other player than sender - for world in &server.worlds { - if world.get_player_by_name_blocking(name).is_some() { - return Ok(name.into()); + "@p" if src.is_player() => return Ok(arg.into()), + "@r" => todo!(), // todo: implement random player target selector + "@a" | "@e" => todo!(), // todo: implement all players target selector + name => { + // todo: implement any other player than sender + for world in &server.worlds { + if world.get_player_by_name(name).await.is_some() { + return Ok(name.into()); + } } + return Err(Some(format!("Player not found: {arg}"))); } - return Err(Some(format!("Player not found: {arg}"))); } } + Err(None) } - Err(None) } /// todo: implement (so far only own name + @s/@p is implemented) -pub fn parse_arg_player( - src: &mut CommandSender, +pub async fn parse_arg_player<'a>( + src: &mut CommandSender<'a>, server: &Server, arg_name: &str, - consumed_args: &ConsumedArgs, + consumed_args: &ConsumedArgs<'a>, ) -> Result, InvalidTreeError> { let s = consumed_args .get(arg_name) @@ -56,7 +68,7 @@ pub fn parse_arg_player( "@a" | "@e" => todo!(), // todo: implement all players target selector name => { for world in &server.worlds { - if let Some(player) = world.get_player_by_name_blocking(name) { + if let Some(player) = world.get_player_by_name(name).await { return Ok(player); } } diff --git a/pumpkin/src/commands/cmd_echest.rs b/pumpkin/src/commands/cmd_echest.rs index 83824183f..6f6743628 100644 --- a/pumpkin/src/commands/cmd_echest.rs +++ b/pumpkin/src/commands/cmd_echest.rs @@ -14,11 +14,11 @@ struct EchestExecutor {} #[async_trait] impl CommandExecutor for EchestExecutor { - async fn execute( + async fn execute<'a>( &self, - sender: &mut super::CommandSender, + sender: &mut super::CommandSender<'a>, server: &crate::server::Server, - _args: &super::tree::ConsumedArgs, + _args: &super::tree::ConsumedArgs<'a>, ) -> Result<(), super::dispatcher::InvalidTreeError> { if let Some(player) = sender.as_player() { let entity_id = player.entity_id(); diff --git a/pumpkin/src/commands/cmd_gamemode.rs b/pumpkin/src/commands/cmd_gamemode.rs index f8b10b7f8..dd709a73a 100644 --- a/pumpkin/src/commands/cmd_gamemode.rs +++ b/pumpkin/src/commands/cmd_gamemode.rs @@ -6,7 +6,7 @@ use pumpkin_core::GameMode; use crate::TextComponent; -use crate::commands::arg_player::{consume_arg_player, parse_arg_player}; +use crate::commands::arg_player::parse_arg_player; use crate::commands::dispatcher::InvalidTreeError; use crate::commands::dispatcher::InvalidTreeError::{ @@ -18,6 +18,8 @@ use crate::commands::CommandSender; use crate::commands::CommandSender::Player; use crate::server::Server; +use super::arg_player::PlayerArgumentConsumer; +use super::tree::ArgumentConsumer; use super::CommandExecutor; const NAMES: [&str; 1] = ["gamemode"]; @@ -27,27 +29,33 @@ const DESCRIPTION: &str = "Change a player's gamemode."; const ARG_GAMEMODE: &str = "gamemode"; const ARG_TARGET: &str = "target"; -pub fn consume_arg_gamemode( - _src: &CommandSender, - _server: &Server, - args: &mut RawArgs, -) -> Result> { - if let Some(arg) = args.pop() { - if let Ok(id) = arg.parse::() { - match GameMode::from_u8(id) { - None | Some(GameMode::Undefined) => {} - Some(_) => return Ok(arg.into()), +struct GamemodeArgumentConsumer {} + +#[async_trait] +impl ArgumentConsumer for GamemodeArgumentConsumer { + async fn consume<'a>( + &self, + _sender: &CommandSender<'a>, + _server: &Server, + args: &mut RawArgs<'a>, + ) -> Result> { + if let Some(arg) = args.pop() { + if let Ok(id) = arg.parse::() { + match GameMode::from_u8(id) { + None | Some(GameMode::Undefined) => {} + Some(_) => return Ok(arg.into()), + }; }; - }; - match GameMode::from_str(arg) { - Err(_) | Ok(GameMode::Undefined) => { - return Err(Some(format!("Gamemode not found: {arg}"))) + match GameMode::from_str(arg) { + Err(_) | Ok(GameMode::Undefined) => { + return Err(Some(format!("Gamemode not found: {arg}"))) + } + Ok(_) => return Ok(arg.into()), } - Ok(_) => return Ok(arg.into()), } + Err(None) } - Err(None) } pub fn parse_arg_gamemode(consumed_args: &ConsumedArgs) -> Result { @@ -72,11 +80,11 @@ struct GamemodeTargetSelf {} #[async_trait] impl CommandExecutor for GamemodeTargetSelf { - async fn execute( + async fn execute<'a>( &self, - sender: &mut CommandSender, + sender: &mut CommandSender<'a>, _server: &Server, - args: &ConsumedArgs, + args: &ConsumedArgs<'a>, ) -> Result<(), InvalidTreeError> { let gamemode = parse_arg_gamemode(args)?; @@ -106,14 +114,14 @@ struct GamemodeTargetPlayer {} #[async_trait] impl CommandExecutor for GamemodeTargetPlayer { - async fn execute( + async fn execute<'a>( &self, - sender: &mut CommandSender, + sender: &mut CommandSender<'a>, server: &Server, - args: &ConsumedArgs, + args: &ConsumedArgs<'a>, ) -> Result<(), InvalidTreeError> { let gamemode = parse_arg_gamemode(args)?; - let target = parse_arg_player(sender, server, ARG_TARGET, args)?; + let target = parse_arg_player(sender, server, ARG_TARGET, args).await?; if target.gamemode.load() == gamemode { sender @@ -140,10 +148,11 @@ impl CommandExecutor for GamemodeTargetPlayer { pub fn init_command_tree<'a>() -> CommandTree<'a> { CommandTree::new(NAMES, DESCRIPTION).with_child( require(&|sender| sender.permission_lvl() >= 2).with_child( - argument(ARG_GAMEMODE, consume_arg_gamemode) + argument(ARG_GAMEMODE, &GamemodeArgumentConsumer {}) .with_child(require(&|sender| sender.is_player()).execute(&GamemodeTargetSelf {})) .with_child( - argument(ARG_TARGET, consume_arg_player).execute(&GamemodeTargetPlayer {}), + argument(ARG_TARGET, &PlayerArgumentConsumer {}) + .execute(&GamemodeTargetPlayer {}), ), ), ) diff --git a/pumpkin/src/commands/cmd_help.rs b/pumpkin/src/commands/cmd_help.rs index 942bc69d4..14b053950 100644 --- a/pumpkin/src/commands/cmd_help.rs +++ b/pumpkin/src/commands/cmd_help.rs @@ -8,6 +8,7 @@ use crate::commands::tree_builder::argument; use crate::commands::CommandSender; use crate::server::Server; +use super::tree::ArgumentConsumer; use super::CommandExecutor; const NAMES: [&str; 3] = ["help", "h", "?"]; @@ -16,16 +17,22 @@ const DESCRIPTION: &str = "Print a help message."; const ARG_COMMAND: &str = "command"; -fn consume_arg_command( - _src: &CommandSender, - _server: &Server, - _args: &mut RawArgs, -) -> Result> { - // let s = args.pop()?; +struct CommandArgumentConsumer {} - // dispatcher.get_tree(s).ok().map(|tree| tree.names[0].into()) - // TODO - Err(None) +#[async_trait] +impl ArgumentConsumer for CommandArgumentConsumer { + async fn consume<'a>( + &self, + _sender: &CommandSender<'a>, + _server: &Server, + _args: &mut RawArgs<'a>, + ) -> Result> { + //let s = args.pop()?; + + // dispatcher.get_tree(s).ok().map(|tree| tree.names[0].into()) + // TODO: Implement this + Err(None) + } } fn parse_arg_command<'a>( @@ -45,11 +52,11 @@ struct BaseHelpExecutor {} #[async_trait] impl CommandExecutor for BaseHelpExecutor { - async fn execute( + async fn execute<'a>( &self, - sender: &mut CommandSender, + sender: &mut CommandSender<'a>, server: &Server, - args: &ConsumedArgs, + args: &ConsumedArgs<'a>, ) -> Result<(), InvalidTreeError> { let tree = parse_arg_command(args, &server.command_dispatcher)?; @@ -70,11 +77,11 @@ struct CommandHelpExecutor {} #[async_trait] impl CommandExecutor for CommandHelpExecutor { - async fn execute( + async fn execute<'a>( &self, - sender: &mut CommandSender, + sender: &mut CommandSender<'a>, server: &Server, - _args: &ConsumedArgs, + _args: &ConsumedArgs<'a>, ) -> Result<(), InvalidTreeError> { let mut keys: Vec<&str> = server.command_dispatcher.commands.keys().copied().collect(); keys.sort_unstable(); @@ -100,6 +107,8 @@ impl CommandExecutor for CommandHelpExecutor { pub fn init_command_tree<'a>() -> CommandTree<'a> { CommandTree::new(NAMES, DESCRIPTION) - .with_child(argument(ARG_COMMAND, consume_arg_command).execute(&BaseHelpExecutor {})) + .with_child( + argument(ARG_COMMAND, &CommandArgumentConsumer {}).execute(&BaseHelpExecutor {}), + ) .execute(&CommandHelpExecutor {}) } diff --git a/pumpkin/src/commands/cmd_kick.rs b/pumpkin/src/commands/cmd_kick.rs index e0ff1ba5f..a91c05c55 100644 --- a/pumpkin/src/commands/cmd_kick.rs +++ b/pumpkin/src/commands/cmd_kick.rs @@ -6,7 +6,7 @@ use crate::commands::arg_player::parse_arg_player; use crate::commands::tree::CommandTree; use crate::commands::tree_builder::argument; -use super::arg_player::consume_arg_player; +use super::arg_player::PlayerArgumentConsumer; use super::CommandExecutor; const NAMES: [&str; 1] = ["kick"]; @@ -18,13 +18,13 @@ struct KickExecutor {} #[async_trait] impl CommandExecutor for KickExecutor { - async fn execute( + async fn execute<'a>( &self, - sender: &mut super::CommandSender, + sender: &mut super::CommandSender<'a>, server: &crate::server::Server, - args: &super::tree::ConsumedArgs, + args: &super::tree::ConsumedArgs<'a>, ) -> Result<(), super::dispatcher::InvalidTreeError> { - let target = parse_arg_player(sender, server, ARG_TARGET, args)?; + let target = parse_arg_player(sender, server, ARG_TARGET, args).await?; target .kick(TextComponent::text("Kicked by an operator")) .await; @@ -41,5 +41,5 @@ impl CommandExecutor for KickExecutor { pub fn init_command_tree<'a>() -> CommandTree<'a> { CommandTree::new(NAMES, DESCRIPTION) - .with_child(argument(ARG_TARGET, consume_arg_player).execute(&KickExecutor {})) + .with_child(argument(ARG_TARGET, &PlayerArgumentConsumer {}).execute(&KickExecutor {})) } diff --git a/pumpkin/src/commands/cmd_kill.rs b/pumpkin/src/commands/cmd_kill.rs index 049328c6a..c0d0e684f 100644 --- a/pumpkin/src/commands/cmd_kill.rs +++ b/pumpkin/src/commands/cmd_kill.rs @@ -2,10 +2,11 @@ use async_trait::async_trait; use pumpkin_core::text::color::NamedColor; use pumpkin_core::text::TextComponent; -use crate::commands::arg_player::{consume_arg_player, parse_arg_player}; +use crate::commands::arg_player::parse_arg_player; use crate::commands::tree::CommandTree; use crate::commands::tree_builder::argument; +use super::arg_player::PlayerArgumentConsumer; use super::CommandExecutor; const NAMES: [&str; 1] = ["kill"]; @@ -17,14 +18,14 @@ struct KillExecutor {} #[async_trait] impl CommandExecutor for KillExecutor { - async fn execute( + async fn execute<'a>( &self, - sender: &mut super::CommandSender, + sender: &mut super::CommandSender<'a>, server: &crate::server::Server, - args: &super::tree::ConsumedArgs, + args: &super::tree::ConsumedArgs<'a>, ) -> Result<(), super::dispatcher::InvalidTreeError> { // TODO parse entities not only players - let target = parse_arg_player(sender, server, ARG_TARGET, args)?; + let target = parse_arg_player(sender, server, ARG_TARGET, args).await?; target.living_entity.kill().await; sender @@ -39,5 +40,5 @@ impl CommandExecutor for KillExecutor { pub fn init_command_tree<'a>() -> CommandTree<'a> { CommandTree::new(NAMES, DESCRIPTION) - .with_child(argument(ARG_TARGET, consume_arg_player).execute(&KillExecutor {})) + .with_child(argument(ARG_TARGET, &PlayerArgumentConsumer {}).execute(&KillExecutor {})) } diff --git a/pumpkin/src/commands/cmd_pumpkin.rs b/pumpkin/src/commands/cmd_pumpkin.rs index 508fa9575..b6023d4a1 100644 --- a/pumpkin/src/commands/cmd_pumpkin.rs +++ b/pumpkin/src/commands/cmd_pumpkin.rs @@ -14,11 +14,11 @@ struct PumpkinExecutor {} #[async_trait] impl CommandExecutor for PumpkinExecutor { - async fn execute( + async fn execute<'a>( &self, - sender: &mut super::CommandSender, + sender: &mut super::CommandSender<'a>, _server: &crate::server::Server, - _args: &super::tree::ConsumedArgs, + _args: &super::tree::ConsumedArgs<'a>, ) -> Result<(), super::dispatcher::InvalidTreeError> { let version = env!("CARGO_PKG_VERSION"); let description = env!("CARGO_PKG_DESCRIPTION"); diff --git a/pumpkin/src/commands/cmd_stop.rs b/pumpkin/src/commands/cmd_stop.rs index 6ae7d4992..bb34abde6 100644 --- a/pumpkin/src/commands/cmd_stop.rs +++ b/pumpkin/src/commands/cmd_stop.rs @@ -15,11 +15,11 @@ struct StopExecutor {} #[async_trait] impl CommandExecutor for StopExecutor { - async fn execute( + async fn execute<'a>( &self, - sender: &mut super::CommandSender, + sender: &mut super::CommandSender<'a>, _server: &crate::server::Server, - _args: &super::tree::ConsumedArgs, + _args: &super::tree::ConsumedArgs<'a>, ) -> Result<(), super::dispatcher::InvalidTreeError> { sender .send_message(TextComponent::text("Stopping Server").color_named(NamedColor::Red)) diff --git a/pumpkin/src/commands/dispatcher.rs b/pumpkin/src/commands/dispatcher.rs index 72a552e7a..a9c76b9ef 100644 --- a/pumpkin/src/commands/dispatcher.rs +++ b/pumpkin/src/commands/dispatcher.rs @@ -120,18 +120,16 @@ impl<'a> CommandDispatcher<'a> { return Ok(Err(None)); } } - NodeType::Argument { - consumer: consume, - name, - .. - } => match consume(src, server, &mut raw_args) { - Ok(consumed) => { - parsed_args.insert(name, consumed); - } - Err(err) => { - return Ok(Err(err)); + NodeType::Argument { consumer, name, .. } => { + match consumer.consume(src, server, &mut raw_args).await { + Ok(consumed) => { + parsed_args.insert(name, consumed); + } + Err(err) => { + return Ok(Err(err)); + } } - }, + } NodeType::Require { predicate, .. } => { if !predicate(src) { return Ok(Err(None)); diff --git a/pumpkin/src/commands/mod.rs b/pumpkin/src/commands/mod.rs index c9ec0213d..f4ce90931 100644 --- a/pumpkin/src/commands/mod.rs +++ b/pumpkin/src/commands/mod.rs @@ -77,10 +77,10 @@ pub fn default_dispatcher<'a>() -> Arc> { #[async_trait] pub(crate) trait CommandExecutor: Sync { - async fn execute( + async fn execute<'a>( &self, - sender: &mut CommandSender, + sender: &mut CommandSender<'a>, server: &Server, - args: &ConsumedArgs, + args: &ConsumedArgs<'a>, ) -> Result<(), InvalidTreeError>; } diff --git a/pumpkin/src/commands/tree.rs b/pumpkin/src/commands/tree.rs index e9a69ea13..73bea31b2 100644 --- a/pumpkin/src/commands/tree.rs +++ b/pumpkin/src/commands/tree.rs @@ -1,3 +1,5 @@ +use async_trait::async_trait; + use super::CommandExecutor; use crate::{commands::CommandSender, server::Server}; use std::collections::{HashMap, VecDeque}; @@ -10,8 +12,15 @@ pub type ConsumedArgs<'a> = HashMap<&'a str, String>; /// see [`crate::commands::tree_builder::argument`] /// Provide value or an Optional error message, If no Error message provided the default will be used -pub type ArgumentConsumer<'a> = - fn(&CommandSender, &Server, &mut RawArgs) -> Result>; +#[async_trait] +pub(crate) trait ArgumentConsumer: Sync { + async fn consume<'a>( + &self, + sender: &CommandSender<'a>, + server: &Server, + args: &mut RawArgs<'a>, + ) -> Result>; +} pub struct Node<'a> { pub(crate) children: Vec, @@ -27,7 +36,7 @@ pub enum NodeType<'a> { }, Argument { name: &'a str, - consumer: ArgumentConsumer<'a>, + consumer: &'a dyn ArgumentConsumer, }, Require { predicate: &'a (dyn Fn(&CommandSender) -> bool + Sync), diff --git a/pumpkin/src/commands/tree_builder.rs b/pumpkin/src/commands/tree_builder.rs index aa645c1c7..7efa787b6 100644 --- a/pumpkin/src/commands/tree_builder.rs +++ b/pumpkin/src/commands/tree_builder.rs @@ -140,7 +140,7 @@ pub const fn literal(string: &str) -> NonLeafNodeBuilder { /// [`NonLeafNodeBuilder::execute`] nodes in a [`ConsumedArgs`] instance. It must remove consumed arg(s) /// from [`RawArgs`] and return them. It must return None if [`RawArgs`] are invalid. [`RawArgs`] is /// reversed, so [`Vec::pop`] can be used to obtain args in ltr order. -pub fn argument<'a>(name: &'a str, consumer: ArgumentConsumer) -> NonLeafNodeBuilder<'a> { +pub fn argument<'a>(name: &'a str, consumer: &'a dyn ArgumentConsumer) -> NonLeafNodeBuilder<'a> { NonLeafNodeBuilder { node_type: NodeType::Argument { name, consumer }, child_nodes: Vec::new(), diff --git a/pumpkin/src/world/mod.rs b/pumpkin/src/world/mod.rs index 86deec4d7..77fe25990 100644 --- a/pumpkin/src/world/mod.rs +++ b/pumpkin/src/world/mod.rs @@ -466,17 +466,6 @@ impl World { None } - /// Gets a Player by username (Blocking - Legacy use only) - pub fn get_player_by_name_blocking(&self, name: &str) -> Option> { - // TODO: Remove this blocking functions when commands are async. - for player in self.current_players.blocking_lock().values() { - if player.gameprofile.name == name { - return Some(player.clone()); - } - } - None - } - /// Gets a Player by UUID pub async fn get_player_by_uuid(&self, id: uuid::Uuid) -> Option> { return self.current_players.lock().await.get(&id).cloned();