From 3d14e1310b8378a9b708e691ead51f435657e3fd Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Sat, 7 Sep 2024 20:26:19 +0200 Subject: [PATCH 1/2] Command system rewrite Note: Help commands is currently disabled --- pumpkin/src/client/player_packet.rs | 5 +- pumpkin/src/commands/cmd_help.rs | 148 ++++++++++++++-------------- pumpkin/src/commands/dispatcher.rs | 13 ++- pumpkin/src/commands/mod.rs | 30 +----- pumpkin/src/commands/tree.rs | 1 + pumpkin/src/main.rs | 8 +- pumpkin/src/rcon/mod.rs | 5 +- pumpkin/src/server/mod.rs | 13 ++- 8 files changed, 114 insertions(+), 109 deletions(-) diff --git a/pumpkin/src/client/player_packet.rs b/pumpkin/src/client/player_packet.rs index bc7936b88..db709f172 100644 --- a/pumpkin/src/client/player_packet.rs +++ b/pumpkin/src/client/player_packet.rs @@ -1,7 +1,7 @@ use std::f32::consts::PI; use crate::{ - commands::{handle_command, CommandSender}, + commands::CommandSender, entity::player::{ChatMode, Hand, Player}, server::Server, world::player_chunker, @@ -214,7 +214,8 @@ impl Player { } pub fn handle_chat_command(&mut self, server: &mut Server, command: SChatCommand) { - handle_command(&mut CommandSender::Player(self), server, &command.command); + let dispatcher = server.command_dispatcher.clone(); + dispatcher.handle_command(&mut CommandSender::Player(self), server, &command.command); } pub fn handle_player_ground(&mut self, _server: &mut Server, ground: SSetPlayerGround) { diff --git a/pumpkin/src/commands/cmd_help.rs b/pumpkin/src/commands/cmd_help.rs index 8d432a8dd..a2f3238a8 100644 --- a/pumpkin/src/commands/cmd_help.rs +++ b/pumpkin/src/commands/cmd_help.rs @@ -1,74 +1,74 @@ -use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError; -use crate::commands::dispatcher::{CommandDispatcher, InvalidTreeError}; -use crate::commands::tree::{Command, CommandTree, ConsumedArgs, RawArgs}; -use crate::commands::tree_builder::argument; -use crate::commands::{dispatcher_init, CommandSender, DISPATCHER}; -use pumpkin_core::text::TextComponent; - -const NAMES: [&str; 3] = ["help", "h", "?"]; - -const DESCRIPTION: &str = "Print a help message."; - -const ARG_COMMAND: &str = "command"; - -fn consume_arg_command(_src: &CommandSender, args: &mut RawArgs) -> Option { - let s = args.pop()?; - - let dispatcher = DISPATCHER.get_or_init(dispatcher_init); - - dispatcher.get_tree(s).ok().map(|tree| tree.names[0].into()) -} - -fn parse_arg_command<'a>( - consumed_args: &'a ConsumedArgs, - dispatcher: &'a CommandDispatcher, -) -> Result<&'a CommandTree<'a>, InvalidTreeError> { - let command_name = consumed_args - .get(ARG_COMMAND) - .ok_or(InvalidConsumptionError(None))?; - - dispatcher - .get_tree(command_name) - .map_err(|_| InvalidConsumptionError(Some(command_name.into()))) -} - -pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { - CommandTree::new(NAMES, DESCRIPTION) - .with_child( - argument(ARG_COMMAND, consume_arg_command).execute(&|sender, _, args| { - let dispatcher = DISPATCHER.get_or_init(dispatcher_init); - - let tree = parse_arg_command(args, dispatcher)?; - - sender.send_message(TextComponent::text(&format!( - "{} - {} Usage: {}", - tree.names.join("/"), - tree.description, - tree - ))); - - Ok(()) - }), - ) - .execute(&|sender, _, _args| { - let dispatcher = DISPATCHER.get_or_init(dispatcher_init); - - let mut keys: Vec<&str> = dispatcher.commands.keys().copied().collect(); - keys.sort(); - - for key in keys { - let Command::Tree(tree) = &dispatcher.commands[key] else { - continue; - }; - - sender.send_message(TextComponent::text(&format!( - "{} - {} Usage: {}", - tree.names.join("/"), - tree.description, - tree - ))); - } - - Ok(()) - }) -} +// use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError; +// use crate::commands::dispatcher::{CommandDispatcher, InvalidTreeError}; +// use crate::commands::tree::{Command, CommandTree, ConsumedArgs, RawArgs}; +// use crate::commands::tree_builder::argument; +// use crate::commands::{CommandSender}; +// use pumpkin_core::text::TextComponent; + +// const NAMES: [&str; 3] = ["help", "h", "?"]; + +// const DESCRIPTION: &str = "Print a help message."; + +// const ARG_COMMAND: &str = "command"; + +// fn consume_arg_command(_src: &CommandSender, args: &mut RawArgs) -> Option { +// let s = args.pop()?; + +// let dispatcher = DISPATCHER.get_or_init(dispatcher_init); + +// dispatcher.get_tree(s).ok().map(|tree| tree.names[0].into()) +// } + +// fn parse_arg_command<'a>( +// consumed_args: &'a ConsumedArgs, +// dispatcher: &'a CommandDispatcher, +// ) -> Result<&'a CommandTree<'a>, InvalidTreeError> { +// let command_name = consumed_args +// .get(ARG_COMMAND) +// .ok_or(InvalidConsumptionError(None))?; + +// dispatcher +// .get_tree(command_name) +// .map_err(|_| InvalidConsumptionError(Some(command_name.into()))) +// } + +// pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { +// CommandTree::new(NAMES, DESCRIPTION) +// .with_child( +// argument(ARG_COMMAND, consume_arg_command).execute(&|sender, _, args| { +// let dispatcher = DISPATCHER.get_or_init(dispatcher_init); + +// let tree = parse_arg_command(args, dispatcher)?; + +// sender.send_message(TextComponent::text(&format!( +// "{} - {} Usage: {}", +// tree.names.join("/"), +// tree.description, +// tree +// ))); + +// Ok(()) +// }), +// ) +// .execute(&|sender, _, _args| { +// let dispatcher = DISPATCHER.get_or_init(dispatcher_init); + +// let mut keys: Vec<&str> = dispatcher.commands.keys().copied().collect(); +// keys.sort(); + +// for key in keys { +// let Command::Tree(tree) = &dispatcher.commands[key] else { +// continue; +// }; + +// sender.send_message(TextComponent::text(&format!( +// "{} - {} Usage: {}", +// tree.names.join("/"), +// tree.description, +// tree +// ))); +// } + +// Ok(()) +// }) +// } diff --git a/pumpkin/src/commands/dispatcher.rs b/pumpkin/src/commands/dispatcher.rs index 25e912055..04ccc3b77 100644 --- a/pumpkin/src/commands/dispatcher.rs +++ b/pumpkin/src/commands/dispatcher.rs @@ -1,3 +1,5 @@ +use pumpkin_core::text::TextComponent; + use crate::commands::dispatcher::InvalidTreeError::{ InvalidConsumptionError, InvalidRequirementError, }; @@ -17,12 +19,21 @@ pub(crate) enum InvalidTreeError { InvalidRequirementError, } -pub(crate) struct CommandDispatcher<'a> { +#[derive(Default)] +pub struct CommandDispatcher<'a> { pub(crate) commands: HashMap<&'a str, Command<'a>>, } /// Stores registered [CommandTree]s and dispatches commands to them. impl<'a> CommandDispatcher<'a> { + pub fn handle_command(&self, sender: &mut CommandSender, server: &mut Server, cmd: &str) { + if let Err(err) = self.dispatch(sender, server, cmd) { + sender.send_message( + TextComponent::text(&err).color_named(pumpkin_core::text::color::NamedColor::Red), + ) + } + } + /// Execute a command using its corresponding [CommandTree]. pub(crate) fn dispatch( &'a self, diff --git a/pumpkin/src/commands/mod.rs b/pumpkin/src/commands/mod.rs index 1321ec665..e92cfdd79 100644 --- a/pumpkin/src/commands/mod.rs +++ b/pumpkin/src/commands/mod.rs @@ -1,16 +1,13 @@ use pumpkin_core::text::TextComponent; -use std::collections::HashMap; -use std::sync::OnceLock; use crate::commands::dispatcher::CommandDispatcher; use crate::entity::player::Player; -use crate::server::Server; mod arg_player; mod cmd_gamemode; mod cmd_help; mod cmd_pumpkin; mod cmd_stop; -mod dispatcher; +pub mod dispatcher; mod tree; mod tree_builder; mod tree_format; @@ -64,32 +61,13 @@ impl<'a> CommandSender<'a> { } } -/// todo: reconsider using constant -/// -/// Central point from which commands are dispatched. Should always be initialized using -/// [dispatcher_init]. -static DISPATCHER: OnceLock = OnceLock::new(); - -/// create [CommandDispatcher] instance for [DISPATCHER] -fn dispatcher_init<'a>() -> CommandDispatcher<'a> { - let mut dispatcher = CommandDispatcher { - commands: HashMap::new(), - }; +pub fn default_dispatcher<'a>() -> CommandDispatcher<'a> { + let mut dispatcher = CommandDispatcher::default(); dispatcher.register(cmd_pumpkin::init_command_tree()); dispatcher.register(cmd_gamemode::init_command_tree()); dispatcher.register(cmd_stop::init_command_tree()); - dispatcher.register(cmd_help::init_command_tree()); + // dispatcher.register(cmd_help::init_command_tree()); dispatcher } - -pub fn handle_command(sender: &mut CommandSender, server: &mut Server, cmd: &str) { - let dispatcher = DISPATCHER.get_or_init(dispatcher_init); - - if let Err(err) = dispatcher.dispatch(sender, server, cmd) { - sender.send_message( - TextComponent::text(&err).color_named(pumpkin_core::text::color::NamedColor::Red), - ) - } -} diff --git a/pumpkin/src/commands/tree.rs b/pumpkin/src/commands/tree.rs index 63243be59..6caf9c0d6 100644 --- a/pumpkin/src/commands/tree.rs +++ b/pumpkin/src/commands/tree.rs @@ -44,6 +44,7 @@ pub(crate) struct CommandTree<'a> { pub(crate) nodes: Vec>, pub(crate) children: Vec, pub(crate) names: Vec<&'a str>, + #[expect(dead_code)] pub(crate) description: &'a str, } diff --git a/pumpkin/src/main.rs b/pumpkin/src/main.rs index 3ac0c7840..3f88fd85d 100644 --- a/pumpkin/src/main.rs +++ b/pumpkin/src/main.rs @@ -10,7 +10,6 @@ use std::collections::HashMap; use std::io::{self}; use client::{interrupted, Client}; -use commands::handle_command; use server::Server; // Setup some tokens to allow us to identify which event is for which socket. @@ -98,7 +97,12 @@ fn main() -> io::Result<()> { if !out.is_empty() { let mut server = server1.lock().await; - handle_command(&mut commands::CommandSender::Console, &mut server, &out); + let dispatcher = server.command_dispatcher.clone(); + dispatcher.handle_command( + &mut commands::CommandSender::Console, + &mut server, + &out, + ); } } }); diff --git a/pumpkin/src/rcon/mod.rs b/pumpkin/src/rcon/mod.rs index 19a5b17d6..126ad5735 100644 --- a/pumpkin/src/rcon/mod.rs +++ b/pumpkin/src/rcon/mod.rs @@ -12,7 +12,7 @@ use packet::{Packet, PacketError, PacketType}; use pumpkin_config::RCONConfig; use thiserror::Error; -use crate::{commands::handle_command, server::Server}; +use crate::server::Server; mod packet; @@ -194,7 +194,8 @@ impl RCONClient { if self.logged_in { let mut output = Vec::new(); let mut server = server.lock().await; - handle_command( + let dispatcher = server.command_dispatcher.clone(); + dispatcher.handle_command( &mut crate::commands::CommandSender::Rcon(&mut output), &mut server, packet.get_body(), diff --git a/pumpkin/src/server/mod.rs b/pumpkin/src/server/mod.rs index 8791677d8..9f3721e1c 100644 --- a/pumpkin/src/server/mod.rs +++ b/pumpkin/src/server/mod.rs @@ -24,7 +24,12 @@ use pumpkin_world::dimension::Dimension; use pumpkin_registry::Registry; use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey}; -use crate::{client::Client, entity::player::Player, world::World}; +use crate::{ + client::Client, + commands::{default_dispatcher, dispatcher::CommandDispatcher}, + entity::player::Player, + world::World, +}; pub const CURRENT_MC_VERSION: &str = "1.21.1"; @@ -35,6 +40,8 @@ pub struct Server { pub plugin_loader: PluginLoader, + pub command_dispatcher: Arc>, + pub worlds: Vec>>, pub status_response: StatusResponse, // We cache the json response here so we don't parse it every time someone makes a Status request. @@ -81,6 +88,8 @@ impl Server { None }; + // First register default command, after that plugins can put in their own + let command_dispatcher = default_dispatcher(); log::info!("Loading Plugins"); let plugin_loader = PluginLoader::load(); @@ -88,7 +97,6 @@ impl Server { // TODO: load form config "./world".parse().unwrap(), )); - Self { plugin_loader, cached_registry: Registry::get_static(), @@ -98,6 +106,7 @@ impl Server { public_key, cached_server_brand, private_key, + command_dispatcher: Arc::new(command_dispatcher), status_response, status_response_json, public_key_der, From 870059a31762289e774199cb61880c6fce1b77cb Mon Sep 17 00:00:00 2001 From: Snowiiii Date: Sat, 7 Sep 2024 20:32:28 +0200 Subject: [PATCH 2/2] Added Help command back I just was to lazy and wanted to get it working fast, Now help is back --- pumpkin/src/commands/cmd_help.rs | 144 +++++++++++++++---------------- pumpkin/src/commands/mod.rs | 2 +- pumpkin/src/commands/tree.rs | 1 - 3 files changed, 71 insertions(+), 76 deletions(-) diff --git a/pumpkin/src/commands/cmd_help.rs b/pumpkin/src/commands/cmd_help.rs index a2f3238a8..59788b221 100644 --- a/pumpkin/src/commands/cmd_help.rs +++ b/pumpkin/src/commands/cmd_help.rs @@ -1,74 +1,70 @@ -// use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError; -// use crate::commands::dispatcher::{CommandDispatcher, InvalidTreeError}; -// use crate::commands::tree::{Command, CommandTree, ConsumedArgs, RawArgs}; -// use crate::commands::tree_builder::argument; -// use crate::commands::{CommandSender}; -// use pumpkin_core::text::TextComponent; - -// const NAMES: [&str; 3] = ["help", "h", "?"]; - -// const DESCRIPTION: &str = "Print a help message."; - -// const ARG_COMMAND: &str = "command"; - -// fn consume_arg_command(_src: &CommandSender, args: &mut RawArgs) -> Option { -// let s = args.pop()?; - -// let dispatcher = DISPATCHER.get_or_init(dispatcher_init); - -// dispatcher.get_tree(s).ok().map(|tree| tree.names[0].into()) -// } - -// fn parse_arg_command<'a>( -// consumed_args: &'a ConsumedArgs, -// dispatcher: &'a CommandDispatcher, -// ) -> Result<&'a CommandTree<'a>, InvalidTreeError> { -// let command_name = consumed_args -// .get(ARG_COMMAND) -// .ok_or(InvalidConsumptionError(None))?; - -// dispatcher -// .get_tree(command_name) -// .map_err(|_| InvalidConsumptionError(Some(command_name.into()))) -// } - -// pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { -// CommandTree::new(NAMES, DESCRIPTION) -// .with_child( -// argument(ARG_COMMAND, consume_arg_command).execute(&|sender, _, args| { -// let dispatcher = DISPATCHER.get_or_init(dispatcher_init); - -// let tree = parse_arg_command(args, dispatcher)?; - -// sender.send_message(TextComponent::text(&format!( -// "{} - {} Usage: {}", -// tree.names.join("/"), -// tree.description, -// tree -// ))); - -// Ok(()) -// }), -// ) -// .execute(&|sender, _, _args| { -// let dispatcher = DISPATCHER.get_or_init(dispatcher_init); - -// let mut keys: Vec<&str> = dispatcher.commands.keys().copied().collect(); -// keys.sort(); - -// for key in keys { -// let Command::Tree(tree) = &dispatcher.commands[key] else { -// continue; -// }; - -// sender.send_message(TextComponent::text(&format!( -// "{} - {} Usage: {}", -// tree.names.join("/"), -// tree.description, -// tree -// ))); -// } - -// Ok(()) -// }) -// } +use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError; +use crate::commands::dispatcher::{CommandDispatcher, InvalidTreeError}; +use crate::commands::tree::{Command, CommandTree, ConsumedArgs, RawArgs}; +use crate::commands::tree_builder::argument; +use crate::commands::CommandSender; +use pumpkin_core::text::TextComponent; + +const NAMES: [&str; 3] = ["help", "h", "?"]; + +const DESCRIPTION: &str = "Print a help message."; + +const ARG_COMMAND: &str = "command"; + +fn consume_arg_command(_src: &CommandSender, _args: &mut RawArgs) -> Option { + // let s = args.pop()?; + + // dispatcher.get_tree(s).ok().map(|tree| tree.names[0].into()) + // TODO + None +} + +fn parse_arg_command<'a>( + consumed_args: &'a ConsumedArgs, + dispatcher: &'a CommandDispatcher, +) -> Result<&'a CommandTree<'a>, InvalidTreeError> { + let command_name = consumed_args + .get(ARG_COMMAND) + .ok_or(InvalidConsumptionError(None))?; + + dispatcher + .get_tree(command_name) + .map_err(|_| InvalidConsumptionError(Some(command_name.into()))) +} + +pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> { + CommandTree::new(NAMES, DESCRIPTION) + .with_child( + argument(ARG_COMMAND, consume_arg_command).execute(&|sender, server, args| { + let tree = parse_arg_command(args, &server.command_dispatcher)?; + + sender.send_message(TextComponent::text(&format!( + "{} - {} Usage: {}", + tree.names.join("/"), + tree.description, + tree + ))); + + Ok(()) + }), + ) + .execute(&|sender, server, _args| { + let mut keys: Vec<&str> = server.command_dispatcher.commands.keys().copied().collect(); + keys.sort(); + + for key in keys { + let Command::Tree(tree) = &server.command_dispatcher.commands[key] else { + continue; + }; + + sender.send_message(TextComponent::text(&format!( + "{} - {} Usage: {}", + tree.names.join("/"), + tree.description, + tree + ))); + } + + Ok(()) + }) +} diff --git a/pumpkin/src/commands/mod.rs b/pumpkin/src/commands/mod.rs index e92cfdd79..4600ffbe9 100644 --- a/pumpkin/src/commands/mod.rs +++ b/pumpkin/src/commands/mod.rs @@ -67,7 +67,7 @@ pub fn default_dispatcher<'a>() -> CommandDispatcher<'a> { dispatcher.register(cmd_pumpkin::init_command_tree()); dispatcher.register(cmd_gamemode::init_command_tree()); dispatcher.register(cmd_stop::init_command_tree()); - // dispatcher.register(cmd_help::init_command_tree()); + dispatcher.register(cmd_help::init_command_tree()); dispatcher } diff --git a/pumpkin/src/commands/tree.rs b/pumpkin/src/commands/tree.rs index 6caf9c0d6..63243be59 100644 --- a/pumpkin/src/commands/tree.rs +++ b/pumpkin/src/commands/tree.rs @@ -44,7 +44,6 @@ pub(crate) struct CommandTree<'a> { pub(crate) nodes: Vec>, pub(crate) children: Vec, pub(crate) names: Vec<&'a str>, - #[expect(dead_code)] pub(crate) description: &'a str, }