From b3050b5f29f868f8dfe335b12a0fc3a19fc14bef Mon Sep 17 00:00:00 2001 From: Zoey Date: Tue, 23 Apr 2024 11:53:43 -0700 Subject: [PATCH] implement help command --- src/command.rs | 66 +++++++++++++++++++++++++++++++++++++++++-- src/server/network.rs | 50 ++++++++++++++++++++++++++++++-- 2 files changed, 111 insertions(+), 5 deletions(-) diff --git a/src/command.rs b/src/command.rs index 477e9f6..bb3e95e 100644 --- a/src/command.rs +++ b/src/command.rs @@ -5,6 +5,10 @@ const CMD_SAY: &str = "say"; const CMD_SET_PERM: &str = "setperm"; const CMD_KICK: &str = "kick"; const CMD_STOP: &str = "stop"; +const CMD_HELP: &str = "help"; + +/// list of commands available on the server +pub const COMMANDS_LIST: &[&str] = &[CMD_ME, CMD_SAY, CMD_SET_PERM, CMD_KICK, CMD_STOP, CMD_HELP]; /// enum for possible commands #[derive(Debug, Clone)] @@ -27,6 +31,8 @@ pub enum Command<'m> { }, /// command to stop the server Stop, + /// gets help about the given command, or about all commands if no command is given + Help { command: Option<&'m str> }, } impl<'m> Command<'m> { @@ -50,19 +56,73 @@ impl<'m> Command<'m> { Self::Kick { username, message } } CMD_STOP => Self::Stop, + CMD_HELP => Self::Help { + command: (!arguments.is_empty()).then_some(arguments), + }, _ => return Err(format!("Unknown command: {command_name}")), }) } + /// gets the command's name + pub fn command_name(&self) -> &'static str { + match self { + Self::Me { .. } => CMD_ME, + Self::Say { .. } => CMD_SAY, + Self::SetPermissions { .. } => CMD_SET_PERM, + Self::Kick { .. } => CMD_KICK, + Self::Stop => CMD_STOP, + Self::Help { .. } => CMD_HELP, + } + } + /// checks which permissions are required to run this command pub fn perms_required(&self) -> PlayerType { - match self { - Self::Me { .. } => PlayerType::Normal, - Self::Stop => PlayerType::Operator, + Self::perms_required_by_name(self.command_name()) + } + + /// checks which permissions are required to run a command by name + pub fn perms_required_by_name(cmd: &str) -> PlayerType { + match cmd { + CMD_ME => PlayerType::Normal, + CMD_STOP => PlayerType::Operator, + CMD_HELP => PlayerType::Normal, _ => PlayerType::Moderator, } } + /// gets help about the given command + pub fn help(cmd: &str) -> Vec { + let c = |t: &str| format!("&f{}{cmd} {t}", Self::PREFIX); + + match cmd { + CMD_ME => vec![ + c(""), + "&fDisplays an action as if you're doing it.".to_string(), + ], + CMD_SAY => vec![ + c(""), + "&fSends a message as being from the server.".to_string(), + ], + CMD_SET_PERM => vec![ + c(" "), + "&fSets a player's permission level.".to_string(), + ], + CMD_KICK => vec![ + c(" [reason]"), + "&fKicks a player from the server.".to_string(), + ], + CMD_STOP => vec![ + c(""), + "&fStops the server while saving the level.".to_string(), + ], + CMD_HELP => vec![ + c("[command]"), + "&fGets a list of commands or help about a command.".to_string(), + ], + _ => vec!["&eUnknown command!".to_string()], + } + } + /// gets the next string argument from the command fn next_string(args: &mut &'m str) -> Result<&'m str, String> { if args.is_empty() { diff --git a/src/server/network.rs b/src/server/network.rs index 2c551e1..6b99ad0 100644 --- a/src/server/network.rs +++ b/src/server/network.rs @@ -10,9 +10,11 @@ use tokio::{ }; use crate::{ - command::Command, + command::{Command, COMMANDS_LIST}, level::{block::BLOCK_INFO, BlockUpdate, Level}, - packet::{client::ClientPacket, server::ServerPacket, PacketWriter, ARRAY_LENGTH}, + packet::{ + client::ClientPacket, server::ServerPacket, PacketWriter, ARRAY_LENGTH, STRING_LENGTH, + }, player::{Player, PlayerType}, server::config::ServerProtectionMode, }; @@ -365,6 +367,7 @@ async fn handle_stream_inner( } ); } + Command::Say { message } => { let message = format!("&d[SERVER] &f{message}"); @@ -376,6 +379,7 @@ async fn handle_stream_inner( } ); } + Command::SetPermissions { player_username, permissions, @@ -471,6 +475,48 @@ async fn handle_stream_inner( Command::Stop => { data.stop = true; } + + Command::Help { command } => { + let messages = + if let Some(command) = command { + Command::help(command) + } else { + let mut messages = vec![ + "Commands available to you:" + .to_string(), + ]; + let mut current_message = + "&f".to_string(); + for command in COMMANDS_LIST.iter() + { + if Command::perms_required_by_name(command) > player.player_type { + continue; + } + if current_message.len() + + 3 + command.len() + > STRING_LENGTH + { + messages.push(format!( + "{current_message}," + )); + current_message = + "&f".to_string(); + } + if current_message.len() == 2 { + current_message = format!("{current_message}{command}"); + } else { + current_message = format!("{current_message}, {command}"); + } + } + if !current_message.is_empty() { + messages.push(current_message); + } + messages + }; + for msg in messages { + msg!(msg); + } + } } } Err(msg) => {