diff --git a/CHANGELOG.md b/CHANGELOG.md index 7210e05b44..eed4144612 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Unlockable recipes - Localization support for prompt dialogs, diary sections, trade and group invitations. - Added Freezing Potion +- Clear command to delete chat messages. ### Changed @@ -41,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Broad entity and loot updates to Gnarling Fortifications - Reworked spacing and labels in crafting menu. - Reworked design of repair equipment interface in crafting menu. +- Help command can be used on client commands too. ### Removed diff --git a/voxygen/src/cmd.rs b/voxygen/src/cmd.rs index e05013d49d..e093762fcf 100644 --- a/voxygen/src/cmd.rs +++ b/voxygen/src/cmd.rs @@ -25,6 +25,7 @@ use strum::{EnumIter, IntoEnumIterator}; // Please keep this sorted alphabetically, same as with server commands :-) #[derive(Clone, Copy, strum::EnumIter)] pub enum ClientChatCommand { + Clear, ExperimentalShader, Help, Mute, @@ -37,6 +38,11 @@ impl ClientChatCommand { use Requirement::*; let cmd = ChatCommandData::new; match self { + ClientChatCommand::Clear => cmd( + Vec::new(), + "Clears all messages in chat. Affects all chat tabs.", + None, + ), ClientChatCommand::ExperimentalShader => cmd( vec![Enum( "Shader", @@ -68,6 +74,7 @@ impl ClientChatCommand { pub fn keyword(&self) -> &'static str { match self { + ClientChatCommand::Clear => "clear", ClientChatCommand::ExperimentalShader => "experimental_shader", ClientChatCommand::Help => "help", ClientChatCommand::Mute => "mute", @@ -301,16 +308,15 @@ pub fn run_command( preproccess_command(session_state, &command, &mut args)?; - let client = &mut session_state.client.borrow_mut(); - match command { ChatCommandKind::Server(cmd) => { - client.send_command(cmd.keyword().into(), args); + session_state + .client + .borrow_mut() + .send_command(cmd.keyword().into(), args); Ok(None) // The server will provide a response when the command is run }, - ChatCommandKind::Client(cmd) => { - Ok(Some(run_client_command(client, global_state, cmd, args)?)) - }, + ChatCommandKind::Client(cmd) => run_client_command(session_state, global_state, cmd, args), } } @@ -344,29 +350,43 @@ fn invalid_command_message(client: &Client, user_entered_invalid_command: String } fn run_client_command( - client: &mut Client, + session_state: &mut SessionState, global_state: &mut GlobalState, command: ClientChatCommand, args: Vec, -) -> Result { +) -> CommandResult { let command = match command { + ClientChatCommand::Clear => handle_clear, ClientChatCommand::ExperimentalShader => handle_experimental_shader, ClientChatCommand::Help => handle_help, ClientChatCommand::Mute => handle_mute, ClientChatCommand::Unmute => handle_unmute, }; - command(client, global_state, args) + command(session_state, global_state, args) +} + +fn handle_clear( + session_state: &mut SessionState, + _global_state: &mut GlobalState, + _args: Vec, +) -> CommandResult { + session_state.hud.clear_chat(); + Ok(None) } fn handle_help( - client: &Client, + session_state: &mut SessionState, _global_state: &mut GlobalState, args: Vec, -) -> Result { - if let Some(cmd) = parse_cmd_args!(args, ServerChatCommand) { - Ok(cmd.help_string()) +) -> CommandResult { + if let Some(cmd) = parse_cmd_args!(&args, ServerChatCommand) { + Ok(Some(cmd.help_string())) + } else if let Some(cmd) = parse_cmd_args!(&args, ClientChatCommand) { + Ok(Some(cmd.help_string())) } else { + let client = &mut session_state.client.borrow_mut(); + let mut message = String::new(); let entity_role = client .state() @@ -392,16 +412,18 @@ fn handle_help( .for_each(|(k, cmd)| { message += &format!(" /{} => /{}", k, cmd.keyword()); }); - Ok(message) + Ok(Some(message)) } } fn handle_mute( - client: &Client, + session_state: &mut SessionState, global_state: &mut GlobalState, args: Vec, -) -> Result { +) -> CommandResult { if let Some(alias) = parse_cmd_args!(args, String) { + let client = &mut session_state.client.borrow_mut(); + let target = client .player_list() .values() @@ -420,7 +442,7 @@ fn handle_mute( .insert(target.uuid, alias.clone()) .is_none() { - Ok(format!("Successfully muted player {}.", alias)) + Ok(Some(format!("Successfully muted player {}.", alias))) } else { Err(format!("{} is already muted.", alias)) } @@ -430,10 +452,10 @@ fn handle_mute( } fn handle_unmute( - client: &Client, + session_state: &mut SessionState, global_state: &mut GlobalState, args: Vec, -) -> Result { +) -> CommandResult { // Note that we don't care if this is a real player, so that it's possible // to unmute someone when they're offline if let Some(alias) = parse_cmd_args!(args, String) { @@ -444,6 +466,8 @@ fn handle_unmute( .find(|(_, v)| **v == alias) .map(|(k, _)| *k) { + let client = &mut session_state.client.borrow_mut(); + if let Some(me) = client.uid().and_then(|uid| client.player_list().get(&uid)) { if uuid == me.uuid { return Err("You cannot unmute yourself.".to_string()); @@ -451,7 +475,7 @@ fn handle_unmute( } global_state.profile.mutelist.remove(&uuid); - Ok(format!("Successfully unmuted player {}.", alias)) + Ok(Some(format!("Successfully unmuted player {}.", alias))) } else { Err(format!("Could not find a muted player named {}.", alias)) } @@ -461,35 +485,37 @@ fn handle_unmute( } fn handle_experimental_shader( - _client: &Client, + _session_state: &mut SessionState, global_state: &mut GlobalState, args: Vec, -) -> Result { +) -> CommandResult { if args.is_empty() { - ExperimentalShader::iter() - .map(|s| { - let is_active = global_state - .settings - .graphics - .render_mode - .experimental_shaders - .contains(&s); - format!("[{}] {}", if is_active { "x" } else { " " }, s) - }) - .reduce(|mut a, b| { - a.push('\n'); - a.push_str(&b); - a - }) - .ok_or("There are no experimental shaders.".to_string()) + Ok(Some( + ExperimentalShader::iter() + .map(|s| { + let is_active = global_state + .settings + .graphics + .render_mode + .experimental_shaders + .contains(&s); + format!("[{}] {}", if is_active { "x" } else { " " }, s) + }) + .reduce(|mut a, b| { + a.push('\n'); + a.push_str(&b); + a + }) + .ok_or("There are no experimental shaders.".to_string())?, + )) } else if let Some(item) = parse_cmd_args!(args, String) { if let Ok(shader) = ExperimentalShader::from_str(&item) { let mut new_render_mode = global_state.settings.graphics.render_mode.clone(); let res = if new_render_mode.experimental_shaders.remove(&shader) { - Ok(format!("Disabled {item}.")) + Ok(Some(format!("Disabled {item}."))) } else { new_render_mode.experimental_shaders.insert(shader); - Ok(format!("Enabled {item}.")) + Ok(Some(format!("Enabled {item}."))) }; change_render_mode( diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index 20d29d5d62..240fd058ed 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -87,6 +87,7 @@ pub struct Chat<'a> { scale: Scale, localized_strings: &'a Localization, + clear_messages: bool, } impl<'a> Chat<'a> { @@ -99,6 +100,7 @@ impl<'a> Chat<'a> { fonts: &'a Fonts, localized_strings: &'a Localization, scale: Scale, + clear_messages: bool, ) -> Self { Self { pulse, @@ -114,6 +116,7 @@ impl<'a> Chat<'a> { history_max: 32, localized_strings, scale, + clear_messages, } } @@ -240,6 +243,10 @@ impl<'a> Widget for Chat<'a> { let chat_pos = Vec2::new(chat_settings.chat_pos_x, chat_settings.chat_pos_y); let chat_box_input_width = chat_size.x - CHAT_ICON_WIDTH - 12.0; + if self.clear_messages { + state.update(|s| s.messages.clear()); + } + // Empty old messages state.update(|s| { while s.messages.len() > MAX_MESSAGES { diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 5f080808f0..555c9bc3eb 100755 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -1298,6 +1298,7 @@ pub struct Hud { voxel_minimap: VoxelMinimap, map_drag: Vec2, force_chat: bool, + clear_chat: bool, } impl Hud { @@ -1435,9 +1436,12 @@ impl Hud { }, map_drag: Vec2::zero(), force_chat: false, + clear_chat: false, } } + pub fn clear_chat(&mut self) { self.clear_chat = true; } + pub fn set_prompt_dialog(&mut self, prompt_dialog: PromptDialogSettings) { self.show.prompt_dialog = Some(prompt_dialog); } @@ -3524,6 +3528,7 @@ impl Hud { &self.fonts, i18n, scale, + self.clear_chat, ) .and_then(self.force_chat_input.take(), |c, input| c.input(input)) .and_then(self.tab_complete.take(), |c, input| { @@ -3575,6 +3580,7 @@ impl Hud { self.new_messages.clear(); self.new_notifications.clear(); + self.clear_chat = false; // Windows diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs index 9697f03575..b4051bc7b3 100644 --- a/voxygen/src/session/mod.rs +++ b/voxygen/src/session/mod.rs @@ -102,7 +102,7 @@ pub struct SessionState { scene: Scene, pub(crate) client: Rc>, metadata: UpdateCharacterMetadata, - hud: Hud, + pub(crate) hud: Hud, key_state: KeyState, inputs: comp::ControllerInputs, inputs_state: HashSet,