diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs index a4bc1eab3a..bf2908ae9d 100644 --- a/chat-cli/src/main.rs +++ b/chat-cli/src/main.rs @@ -84,7 +84,8 @@ fn main() { for event in events { match event { - Event::Chat { message, .. } => println!("{}", message), + // TODO client is now responsible for formatting the `[{player_name}] {}` + Event::Chat(m) => println!("{}", m.message), Event::Disconnect => {}, // TODO Event::DisconnectionNotification(time) => { let message = match time { diff --git a/client/src/cmd.rs b/client/src/cmd.rs index 03e77479c3..de523bd39b 100644 --- a/client/src/cmd.rs +++ b/client/src/cmd.rs @@ -112,22 +112,9 @@ pub fn complete(line: &str, client: &Client) -> Vec { } } } else { - // Complete past the last argument - match cmd.data().args.last() { - Some(ArgumentSpec::SubCommand) => { - if let Some(index) = nth_word(line, cmd.data().args.len()) { - complete(&line[index..], &client) - } else { - vec![] - } - }, - Some(ArgumentSpec::Message) => complete_player(word, &client), - _ => vec![], // End of command. Nothing to complete - } + // Completing for unknown chat command + complete_player(word, &client) } - } else { - // Completing for unknown chat command - complete_player(word, &client) } } else { // Not completing a command diff --git a/client/src/lib.rs b/client/src/lib.rs index 10b4b0467d..69b400e169 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -32,7 +32,6 @@ use common::{ sync::{Uid, UidAllocator, WorldSyncExt}, terrain::{block::Block, TerrainChunk, TerrainChunkSize}, vol::RectVolSize, - ChatType, }; use hashbrown::HashMap; use image::DynamicImage; @@ -55,10 +54,7 @@ const SERVER_TIMEOUT: f64 = 20.0; const SERVER_TIMEOUT_GRACE_PERIOD: f64 = 14.0; pub enum Event { - Chat { - chat_type: ChatType, - message: String, - }, + Chat(comp::ChatMsg), Disconnect, DisconnectionNotification(u64), Notification(Notification), @@ -465,8 +461,8 @@ impl Client { /// Send a chat message to the server. pub fn send_chat(&mut self, message: String) { match validate_chat_msg(&message) { - Ok(()) => self.postbox.send_message(ClientMsg::ChatMsg { message }), - Err(ChatMsgValidationError::TooLong) => warn!( + Ok(()) => self.postbox.send_message(ClientMsg::ChatMsg(message)), + Err(ChatMsgValidationError::TooLong) => tracing::warn!( "Attempted to send a message that's too long (Over {} bytes)", MAX_BYTES_CHAT_MSG ), @@ -824,9 +820,7 @@ impl Client { self.last_ping_delta = (self.state.get_time() - self.last_server_ping).round(); }, - ServerMsg::ChatMsg { message, chat_type } => { - frontend_events.push(Event::Chat { message, chat_type }) - }, + ServerMsg::ChatMsg(m) => frontend_events.push(Event::Chat(m)), ServerMsg::SetPlayerEntity(uid) => { if let Some(entity) = self.state.ecs().entity_from_uid(uid) { self.entity = entity; @@ -869,12 +863,13 @@ impl Client { ServerMsg::InventoryUpdate(inventory, event) => { match event { InventoryUpdateEvent::CollectFailed => { - frontend_events.push(Event::Chat { + // TODO This might not be the best way to show an error + frontend_events.push(Event::Chat(comp::ChatMsg { message: String::from( "Failed to collect item. Your inventory may be full!", ), - chat_type: ChatType::Meta, - }) + chat_type: comp::ChatType::Private, + })) }, _ => { self.state.write_component(self.entity, inventory); diff --git a/common/src/comp/chat.rs b/common/src/comp/chat.rs index 0788b67d24..260114c2e7 100644 --- a/common/src/comp/chat.rs +++ b/common/src/comp/chat.rs @@ -1,10 +1,12 @@ -use specs::{Component, Entity}; +use crate::sync::Uid; +use specs::Component; use specs_idvs::IDVStorage; -/// Limit chat to a subset of players +/// A player's current chat mode. +#[derive(Copy, Clone, Debug)] pub enum ChatMode { - /// Private message to another player (by entity) - Tell(Entity), + /// Private message to another player (by uuid) + Tell(Uid), /// Talk to players within shouting distance Say, /// Talk to players in your region of the world @@ -20,6 +22,52 @@ impl Component for ChatMode { type Storage = IDVStorage; } +/// List of chat types. Note that this is a superset of `ChatMode`; this is +/// because `ChatType::Kill`, `ChatType::Broadcast`, and `ChatType::Private` +/// cannot be sent by players. +#[derive(Copy, Debug, Clone, Serialize, Deserialize)] +pub enum ChatType { + /// Tell all players something (such as players connecting or alias changes) + Broadcast, + /// Private messages from the server (such as results of chat commands) + Private, + /// Inform players that someone died + Kill, + /// One-on-one chat (from, to) + Tell(Uid, Uid), + /// Chat with nearby players + Say(Uid), + /// Group chat + Group(Uid), + /// Factional chat + Faction(Uid), + /// Regional chat + Region(Uid), + /// World chat + World(Uid), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct ChatMsg { + pub chat_type: ChatType, + pub message: String, +} + +impl ChatMode { + /// Create a message from your current chat mode and uuid. + pub fn msg_from(&self, from: Uid, message: String) -> ChatMsg { + let chat_type = match self { + ChatMode::Tell(to) => ChatType::Tell(from, *to), + ChatMode::Say => ChatType::Say(from), + ChatMode::Region => ChatType::Region(from), + ChatMode::Group => ChatType::Group(from), + ChatMode::Faction => ChatType::Faction(from), + ChatMode::World => ChatType::World(from), + }; + ChatMsg { chat_type, message } + } +} + /// Player groups are useful when forming raiding parties and coordinating /// gameplay. /// diff --git a/common/src/comp/mod.rs b/common/src/comp/mod.rs index 27cc405391..d56fad45a0 100644 --- a/common/src/comp/mod.rs +++ b/common/src/comp/mod.rs @@ -25,7 +25,7 @@ pub use body::{ humanoid, object, quadruped_medium, quadruped_small, AllBodies, Body, BodyData, }; pub use character_state::{Attacking, CharacterState, StateUpdate}; -pub use chat::{ChatMode, Faction, Group}; +pub use chat::{ChatMode, ChatMsg, ChatType, Faction, Group}; pub use controller::{ Climb, ControlAction, ControlEvent, Controller, ControllerInputs, Input, InventoryManip, MountState, Mounting, diff --git a/common/src/lib.rs b/common/src/lib.rs index 6d845f65c5..a53cba2716 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -66,25 +66,3 @@ pub use loadout_builder::LoadoutBuilder; /// assert_eq!("bar", scon.next_message().unwrap()); /// ``` pub mod net; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum ChatType { - /// Tell all players something (such as players connecting or alias changes) - Broadcast, - Chat, // TODO Is this still needed? - GameUpdate, // TODO What is this? - Private, // TODO What is this? - /// One-on-one chat - Tell, - /// Chat with nearby players - Say, - /// Group chat - Group, - /// Factional chat - Faction, - Meta, // TODO What is this? - /// Inform players that someone died - Kill, - /// Regional chat - Region, -} diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index 0ff1e38c91..a609c751ea 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -1,6 +1,7 @@ use crate::{comp, terrain::block::Block}; use vek::*; +/// Messages sent from the client to the server #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ClientMsg { Register { @@ -27,9 +28,8 @@ pub enum ClientMsg { PlaceBlock(Vec3, Block), Ping, Pong, - ChatMsg { - message: String, - }, + /// Send the chat message or command to be processed by the server + ChatMsg(String), PlayerPhysics { pos: comp::Pos, vel: comp::Vel, diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index 5f20f6bd40..12b6c6f87f 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -3,7 +3,6 @@ use crate::{ character::CharacterItem, comp, state, sync, terrain::{Block, TerrainChunk}, - ChatType, }; use authc::AuthClientError; use hashbrown::HashMap; @@ -45,6 +44,7 @@ pub enum Notification { WaypointSaved, } +/// Messages sent from the server to the client #[derive(Debug, Clone, Serialize, Deserialize)] pub enum ServerMsg { InitialSync { @@ -66,10 +66,9 @@ pub enum ServerMsg { ExitIngameCleanup, Ping, Pong, - ChatMsg { - chat_type: ChatType, - message: String, - }, + /// A message to go into the client chat box. The client is responsible for + /// formatting the message. + ChatMsg(comp::ChatMsg), SetPlayerEntity(u64), TimeOfDay(state::TimeOfDay), EntitySync(sync::EntitySyncPackage), @@ -113,82 +112,29 @@ impl From for RegisterError { } impl ServerMsg { - // TODO is this needed? - pub fn chat(message: String) -> ServerMsg { - ServerMsg::ChatMsg { - chat_type: ChatType::Chat, - message, - } - } - - pub fn tell(message: String) -> ServerMsg { - ServerMsg::ChatMsg { - chat_type: ChatType::Tell, - message, - } - } - - pub fn game(message: String) -> ServerMsg { - ServerMsg::ChatMsg { - chat_type: ChatType::GameUpdate, - message, - } + /// Sends either say, world, group, etc. based on the player's current chat mode. + pub fn chat(mode: comp::ChatMode, uid: sync::Uid, message: String) -> ServerMsg { + ServerMsg::ChatMsg(mode.msg_from(uid, message)) } pub fn broadcast(message: String) -> ServerMsg { - ServerMsg::ChatMsg { - chat_type: ChatType::Broadcast, + ServerMsg::ChatMsg(comp::ChatMsg { + chat_type: comp::ChatType::Broadcast, message, - } + }) } - // TODO is this needed? pub fn private(message: String) -> ServerMsg { - ServerMsg::ChatMsg { - chat_type: ChatType::Private, + ServerMsg::ChatMsg(comp::ChatMsg { + chat_type: comp::ChatType::Private, message, - } - } - - pub fn group(message: String) -> ServerMsg { - ServerMsg::ChatMsg { - chat_type: ChatType::Group, - message, - } - } - - pub fn region(message: String) -> ServerMsg { - ServerMsg::ChatMsg { - chat_type: ChatType::Region, - message, - } - } - - pub fn say(message: String) -> ServerMsg { - ServerMsg::ChatMsg { - chat_type: ChatType::Say, - message, - } - } - - pub fn faction(message: String) -> ServerMsg { - ServerMsg::ChatMsg { - chat_type: ChatType::Faction, - message, - } - } - - pub fn world(message: String) -> ServerMsg { - ServerMsg::ChatMsg { - chat_type: ChatType::Chat, - message, - } + }) } pub fn kill(message: String) -> ServerMsg { - ServerMsg::ChatMsg { - chat_type: ChatType::Kill, + ServerMsg::ChatMsg(comp::ChatMsg { + chat_type: comp::ChatType::Kill, message, - } + }) } } diff --git a/server/src/cmd.rs b/server/src/cmd.rs index 6edf5ac659..ee4e771b39 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -975,7 +975,7 @@ fn handle_tell( // This happens when [ab]using /sudo server.notify_client( client, - ServerMsg::tell(String::from("It's rude to impersonate people")), + ServerMsg::private(String::from("It's rude to impersonate people")), ); return; } @@ -994,26 +994,23 @@ fn handle_tell( ); return; } - if msg.is_empty() { - server.notify_client( - client, - ServerMsg::private(format!("[{}] wants to talk to you.", alias)), - ); - return; - } - if let Some(name) = ecs - .read_storage::() + let client_uid = *ecs + .read_storage() .get(client) - .map(|s| s.alias.clone()) - { - server.notify_client(player, ServerMsg::tell(format!("[{}] tells:{}", name, msg))); - server.notify_client(client, ServerMsg::tell(format!("To [{}]:{}", alias, msg))); + .expect("Player must have uid"); + let player_uid = *ecs + .read_storage() + .get(player) + .expect("Player must have uid"); + let mode = comp::ChatMode::Tell(player_uid); + let _ = server.state.ecs().write_storage().insert(client, mode); + let msg = if msg.is_empty() { + format!("{} wants to talk to you.", alias) } else { - server.notify_client( - client, - ServerMsg::private(String::from("Failed to send message.")), - ); - } + msg.to_string() + }; + server.notify_client(player, ServerMsg::chat(mode, client_uid, msg.clone())); + server.notify_client(client, ServerMsg::chat(mode, client_uid, msg)); } else { server.notify_client( client, @@ -1039,19 +1036,18 @@ fn handle_faction( // This happens when [ab]using /sudo server.notify_client( client, - ServerMsg::tell(String::from("It's rude to impersonate people")), + ServerMsg::private(String::from("It's rude to impersonate people")), ); return; } - let _ = server - .state - .ecs() - .write_storage() - .insert(client, comp::ChatMode::Faction); + let mode = comp::ChatMode::Faction; + let _ = server.state.ecs().write_storage().insert(client, mode); if !msg.is_empty() { - server - .state - .notify_registered_clients(ServerMsg::faction(msg.to_string())); + if let Some(uid) = server.state.ecs().read_storage().get(client) { + server + .state + .notify_registered_clients(ServerMsg::chat(mode, *uid, msg.to_string())); + } } } @@ -1066,19 +1062,18 @@ fn handle_group( // This happens when [ab]using /sudo server.notify_client( client, - ServerMsg::tell(String::from("It's rude to impersonate people")), + ServerMsg::private(String::from("It's rude to impersonate people")), ); return; } - let _ = server - .state - .ecs() - .write_storage() - .insert(client, comp::ChatMode::Group); + let mode = comp::ChatMode::Group; + let _ = server.state.ecs().write_storage().insert(client, mode); if !msg.is_empty() { - server - .state - .notify_registered_clients(ServerMsg::group(msg.to_string())); + if let Some(uid) = server.state.ecs().read_storage().get(client) { + server + .state + .notify_registered_clients(ServerMsg::chat(mode, *uid, msg.to_string())); + } } } @@ -1093,19 +1088,18 @@ fn handle_region( // This happens when [ab]using /sudo server.notify_client( client, - ServerMsg::tell(String::from("It's rude to impersonate people")), + ServerMsg::private(String::from("It's rude to impersonate people")), ); return; } - let _ = server - .state - .ecs() - .write_storage() - .insert(client, comp::ChatMode::Region); + let mode = comp::ChatMode::Region; + let _ = server.state.ecs().write_storage().insert(client, mode); if !msg.is_empty() { - server - .state - .notify_registered_clients(ServerMsg::region(msg.to_string())); + if let Some(uid) = server.state.ecs().read_storage().get(client) { + server + .state + .notify_registered_clients(ServerMsg::chat(mode, *uid, msg.to_string())); + } } } @@ -1120,19 +1114,18 @@ fn handle_say( // This happens when [ab]using /sudo server.notify_client( client, - ServerMsg::tell(String::from("It's rude to impersonate people")), + ServerMsg::private(String::from("It's rude to impersonate people")), ); return; } - let _ = server - .state - .ecs() - .write_storage() - .insert(client, comp::ChatMode::Say); + let mode = comp::ChatMode::Say; + let _ = server.state.ecs().write_storage().insert(client, mode); if !msg.is_empty() { - server - .state - .notify_registered_clients(ServerMsg::say(msg.to_string())); + if let Some(uid) = server.state.ecs().read_storage().get(client) { + server + .state + .notify_registered_clients(ServerMsg::chat(mode, *uid, msg.to_string())); + } } } @@ -1147,19 +1140,18 @@ fn handle_world( // This happens when [ab]using /sudo server.notify_client( client, - ServerMsg::tell(String::from("It's rude to impersonate people")), + ServerMsg::private(String::from("It's rude to impersonate people")), ); return; } - let _ = server - .state - .ecs() - .write_storage() - .insert(client, comp::ChatMode::World); + let mode = comp::ChatMode::World; + let _ = server.state.ecs().write_storage().insert(client, mode); if !msg.is_empty() { - server - .state - .notify_registered_clients(ServerMsg::world(msg.to_string())); + if let Some(uid) = server.state.ecs().read_storage().get(client) { + server + .state + .notify_registered_clients(ServerMsg::chat(mode, *uid, msg.to_string())); + } } } diff --git a/server/src/sys/message.rs b/server/src/sys/message.rs index b40379d11c..6f6f3bda2b 100644 --- a/server/src/sys/message.rs +++ b/server/src/sys/message.rs @@ -5,8 +5,7 @@ use crate::{ }; use common::{ comp::{ - Admin, CanBuild, ControlEvent, Controller, ForceUpdate, Ori, Player, Pos, SpeechBubble, - Stats, Vel, + CanBuild, ChatMode, ControlEvent, Controller, ForceUpdate, Ori, Player, Pos, Stats, Vel, }, event::{EventBus, ServerEvent}, msg::{ @@ -22,7 +21,6 @@ use hashbrown::HashMap; use specs::{ Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage, }; -use tracing::warn; /// This system will handle new messages from clients pub struct Sys; @@ -37,9 +35,9 @@ impl<'a> System<'a> for Sys { Write<'a, SysTimer>, ReadStorage<'a, Uid>, ReadStorage<'a, CanBuild>, - ReadStorage<'a, Admin>, ReadStorage<'a, ForceUpdate>, ReadStorage<'a, Stats>, + ReadStorage<'a, ChatMode>, WriteExpect<'a, AuthProvider>, Write<'a, BlockChange>, WriteStorage<'a, Pos>, @@ -66,9 +64,9 @@ impl<'a> System<'a> for Sys { mut timer, uids, can_build, - admins, force_updates, stats, + chat_modes, mut accounts, mut block_changes, mut positions, @@ -320,16 +318,24 @@ impl<'a> System<'a> for Sys { }, ClientState::Pending => {}, }, - ClientMsg::ChatMsg { message } => match client.client_state { + ClientMsg::ChatMsg(message) => match client.client_state { ClientState::Connected => client.error_state(RequestStateError::Impossible), ClientState::Registered | ClientState::Spectator | ClientState::Character => match validate_chat_msg(&message) { - Ok(()) => new_chat_msgs.push((Some(entity), ServerMsg::chat(message))), + Ok(()) => { + if let Some(from) = uids.get(entity) { + let mode = chat_modes.get(entity).unwrap_or(&ChatMode::World); + let msg = ServerMsg::chat(*mode, *from, message); + new_chat_msgs.push((Some(entity), msg)); + } else { + tracing::error!("Could not send message. Missing player uid"); + } + }, Err(ChatMsgValidationError::TooLong) => { let max = MAX_BYTES_CHAT_MSG; let len = message.len(); - warn!(?len, ?max, "Recieved a chat message that's too long") + tracing::warn!(?len, ?max, "Recieved a chat message that's too long") }, }, ClientState::Pending => {}, @@ -446,39 +452,18 @@ impl<'a> System<'a> for Sys { // Handle new chat messages. for (entity, msg) in new_chat_msgs { match msg { - ServerMsg::ChatMsg { chat_type, message } => { - let message = if let Some(entity) = entity { - // Handle chat commands. - if message.starts_with("/") && message.len() > 1 { - let argv = String::from(&message[1..]); + ServerMsg::ChatMsg(msg) => { + // Handle chat commands. + if msg.message.starts_with("/") { + if let (Some(entity), true) = (entity, msg.message.len() > 1) { + let argv = String::from(&msg.message[1..]); server_emitter.emit(ServerEvent::ChatCmd(entity, argv)); - continue; - } else { - let bubble = SpeechBubble::player_new(message.clone(), *time); - let _ = speech_bubbles.insert(entity, bubble); - format!( - "{}[{}] {}: {}", - match admins.get(entity) { - Some(_) => "[ADMIN]", - None => "", - }, - match players.get(entity) { - Some(player) => &player.alias, - None => "", - }, - match stats.get(entity) { - Some(stat) => &stat.name, - None => "", - }, - message - ) } } else { - message - }; - let msg = ServerMsg::ChatMsg { chat_type, message }; - for client in (&mut clients).join().filter(|c| c.is_registered()) { - client.notify(msg.clone()); + // TODO FIXME speech bubbles and prefixes are handled by the client now + for client in (&mut clients).join().filter(|c| c.is_registered()) { + client.notify(ServerMsg::ChatMsg(msg.clone())); + } } }, _ => { diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index 33f45936ed..bf94a4c7a4 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -1,10 +1,13 @@ use super::{ - img_ids::Imgs, BROADCAST_COLOR, FACTION_COLOR, GAME_UPDATE_COLOR, GROUP_COLOR, KILL_COLOR, - META_COLOR, PRIVATE_COLOR, REGION_COLOR, SAY_COLOR, TELL_COLOR, TEXT_COLOR, + img_ids::Imgs, BROADCAST_COLOR, FACTION_COLOR, GROUP_COLOR, KILL_COLOR, PRIVATE_COLOR, + REGION_COLOR, SAY_COLOR, TELL_COLOR, TEXT_COLOR, WORLD_COLOR, }; use crate::{ui::fonts::ConrodVoxygenFonts, GlobalState}; -use client::{cmd, Client, Event as ClientEvent}; -use common::{msg::validate_chat_msg, ChatType}; +use client::{cmd, Client}; +use common::{ + comp::{ChatMsg, ChatType}, + msg::validate_chat_msg, +}; use conrod_core::{ input::Key, position::Dimension, @@ -32,7 +35,7 @@ const MAX_MESSAGES: usize = 100; #[derive(WidgetCommon)] pub struct Chat<'a> { - new_messages: &'a mut VecDeque, + new_messages: &'a mut VecDeque, force_input: Option, force_cursor: Option, force_completions: Option>, @@ -50,7 +53,7 @@ pub struct Chat<'a> { impl<'a> Chat<'a> { pub fn new( - new_messages: &'a mut VecDeque, + new_messages: &'a mut VecDeque, global_state: &'a GlobalState, imgs: &'a Imgs, fonts: &'a ConrodVoxygenFonts, @@ -105,7 +108,7 @@ impl<'a> Chat<'a> { } pub struct State { - messages: VecDeque, + messages: VecDeque, input: String, ids: Ids, history: VecDeque, @@ -153,7 +156,17 @@ impl<'a> Widget for Chat<'a> { let transp = self.global_state.settings.gameplay.chat_transp; // Maintain scrolling. if !self.new_messages.is_empty() { - state.update(|s| s.messages.extend(self.new_messages.drain(..))); + state.update(|s| { + s.messages.extend( + self.new_messages + .drain(..) + .map(|msg| { + // TODO format!([{}] {}, name, msg) + msg + }) + .collect::>(), + ) + }); ui.scroll_widget(state.ids.message_box, [0.0, std::f64::MAX]); } @@ -312,36 +325,29 @@ impl<'a> Widget for Chat<'a> { // This would be easier if conrod used the v-metrics from rusttype. let widget = if item.i < state.messages.len() { let msg = &state.messages[item.i]; - match msg { - ClientEvent::Chat { chat_type, message } => { - let color = match chat_type { - ChatType::Meta => META_COLOR, - ChatType::Tell => TELL_COLOR, - ChatType::Chat => TEXT_COLOR, - ChatType::Private => PRIVATE_COLOR, - ChatType::Broadcast => BROADCAST_COLOR, - ChatType::GameUpdate => GAME_UPDATE_COLOR, - ChatType::Say => SAY_COLOR, - ChatType::Group => GROUP_COLOR, - ChatType::Faction => FACTION_COLOR, - ChatType::Region => REGION_COLOR, - ChatType::Kill => KILL_COLOR, - }; - let text = Text::new(&message) - .font_size(self.fonts.opensans.scale(15)) - .font_id(self.fonts.opensans.conrod_id) - .w(470.0) - .color(color) - .line_spacing(2.0); - // Add space between messages. - let y = match text.get_y_dimension(ui) { - Dimension::Absolute(y) => y + 2.0, - _ => 0.0, - }; - Some(text.h(y)) - }, - _ => None, - } + let color = match msg.chat_type { + ChatType::Tell(_, _) => TELL_COLOR, + ChatType::Private => PRIVATE_COLOR, + ChatType::Broadcast => BROADCAST_COLOR, + ChatType::Say(_) => SAY_COLOR, + ChatType::Group(_) => GROUP_COLOR, + ChatType::Faction(_) => FACTION_COLOR, + ChatType::Region(_) => REGION_COLOR, + ChatType::Kill => KILL_COLOR, + ChatType::World(_) => WORLD_COLOR, + }; + let text = Text::new(&msg.message) + .font_size(self.fonts.opensans.scale(15)) + .font_id(self.fonts.opensans.conrod_id) + .w(470.0) + .color(color) + .line_spacing(2.0); + // Add space between messages. + let y = match text.get_y_dimension(ui) { + Dimension::Absolute(y) => y + 2.0, + _ => 0.0, + }; + Some(text.h(y)) } else { // Spacer at bottom of the last message so that it is not cut off. // Needs to be larger than the space above. diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index 505187460b..c9d1156e93 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -46,7 +46,7 @@ use crate::{ window::{Event as WinEvent, GameInput}, GlobalState, }; -use client::{Client, Event as ClientEvent}; +use client::Client; use common::{assets::load_expect, comp, terrain::TerrainChunk, vol::RectRasterableVol}; use conrod_core::{ text::cursor::Index, @@ -74,16 +74,24 @@ const MANA_COLOR: Color = Color::Rgba(0.29, 0.62, 0.75, 0.9); //const RAGE_COLOR: Color = Color::Rgba(0.5, 0.04, 0.13, 1.0); // Chat Colors -const META_COLOR: Color = Color::Rgba(1.0, 1.0, 0.0, 1.0); +/// Color for a private message from another player const TELL_COLOR: Color = Color::Rgba(0.98, 0.71, 1.0, 1.0); -const PRIVATE_COLOR: Color = Color::Rgba(1.0, 1.0, 0.0, 1.0); // Difference between private and tell? +/// Color for private messages from the server (such as /cmd) +const PRIVATE_COLOR: Color = Color::Rgba(1.0, 1.0, 0.0, 1.0); +/// Color for public messages from the server const BROADCAST_COLOR: Color = Color::Rgba(0.28, 0.83, 0.71, 1.0); -const GAME_UPDATE_COLOR: Color = Color::Rgba(1.0, 1.0, 0.0, 1.0); -const SAY_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0); +/// Color for local chat +const SAY_COLOR: Color = Color::Rgba(0.9, 0.2, 0.2, 1.0); +/// Color for group chat const GROUP_COLOR: Color = Color::Rgba(0.47, 0.84, 1.0, 1.0); +/// Color for factional chat const FACTION_COLOR: Color = Color::Rgba(0.24, 1.0, 0.48, 1.0); -const REGION_COLOR: Color = Color::Rgba(1.0, 1.0, 0.0, 1.0); +/// Color for regional chat +const REGION_COLOR: Color = Color::Rgba(0.2, 0.2, 1.0, 1.0); +/// Color for death messages const KILL_COLOR: Color = Color::Rgba(1.0, 0.17, 0.17, 1.0); +/// Color for global messages +const WORLD_COLOR: Color = Color::Rgba(0.9, 1.0, 0.9, 1.0); // UI Color-Theme const UI_MAIN: Color = Color::Rgba(0.61, 0.70, 0.70, 1.0); // Greenish Blue @@ -455,7 +463,8 @@ pub struct Hud { item_imgs: ItemImgs, fonts: ConrodVoxygenFonts, rot_imgs: ImgsRot, - new_messages: VecDeque, + new_messages: VecDeque, + new_notifications: VecDeque, show: Show, //never_show: bool, //intro: bool, @@ -523,6 +532,7 @@ impl Hud { new_messages: VecDeque::new(), //intro: false, //intro_2: false, + new_notifications: VecDeque::new(), show: Show { help: false, intro: true, @@ -1441,11 +1451,11 @@ impl Hud { } } - // Popup + // Popup (waypoint saved and similar notifications) Popup::new( &self.voxygen_i18n, client, - &self.new_messages, + &self.new_notifications, &self.fonts, &self.show, ) @@ -1541,16 +1551,6 @@ impl Hud { .set(self.ids.skillbar, ui_widgets); } - // The chat box breaks if it has non-chat messages left in the queue, so take - // them out. - self.new_messages.retain(|msg| { - if let ClientEvent::Chat { .. } = &msg { - true - } else { - false - } - }); - // Chat box match Chat::new( &mut self.new_messages, @@ -1578,6 +1578,7 @@ impl Hud { } self.new_messages = VecDeque::new(); + self.new_notifications = VecDeque::new(); // Windows @@ -1904,7 +1905,11 @@ impl Hud { events } - pub fn new_message(&mut self, msg: ClientEvent) { self.new_messages.push_back(msg); } + pub fn new_message(&mut self, msg: comp::ChatMsg) { self.new_messages.push_back(msg); } + + pub fn new_notification(&mut self, msg: common::msg::Notification) { + self.new_notifications.push_back(msg); + } pub fn scale_change(&mut self, scale_change: ScaleChange) -> ScaleMode { let scale_mode = match scale_change { diff --git a/voxygen/src/hud/popup.rs b/voxygen/src/hud/popup.rs index cf59572368..2f0e33b005 100644 --- a/voxygen/src/hud/popup.rs +++ b/voxygen/src/hud/popup.rs @@ -1,6 +1,6 @@ use super::Show; use crate::{i18n::VoxygenLocalization, ui::fonts::ConrodVoxygenFonts}; -use client::{self, Client, Event as ClientEvent}; +use client::{self, Client}; use common::msg::Notification; use conrod_core::{ widget::{self, Text}, @@ -23,7 +23,7 @@ widget_ids! { pub struct Popup<'a> { voxygen_i18n: &'a std::sync::Arc, client: &'a Client, - new_messages: &'a VecDeque, + new_notifications: &'a VecDeque, fonts: &'a ConrodVoxygenFonts, #[conrod(common_builder)] common: widget::CommonBuilder, @@ -36,14 +36,14 @@ impl<'a> Popup<'a> { pub fn new( voxygen_i18n: &'a std::sync::Arc, client: &'a Client, - new_messages: &'a VecDeque, + new_notifications: &'a VecDeque, fonts: &'a ConrodVoxygenFonts, show: &'a Show, ) -> Self { Self { voxygen_i18n, client, - new_messages, + new_notifications, fonts, common: widget::CommonBuilder::default(), show, @@ -119,9 +119,9 @@ impl<'a> Widget for Popup<'a> { } // Push waypoint to message queue - for notification in self.new_messages { + for notification in self.new_notifications { match notification { - ClientEvent::Notification(Notification::WaypointSaved) => { + Notification::WaypointSaved => { state.update(|s| { if s.infos.is_empty() { s.last_info_update = Instant::now(); @@ -130,7 +130,6 @@ impl<'a> Widget for Popup<'a> { s.infos.push_back(text.to_string()); }); }, - _ => {}, } } diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 72d30ae500..194a4a0210 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -9,17 +9,16 @@ use crate::{ window::{AnalogGameInput, Event, GameInput}, Direction, Error, GlobalState, PlayState, PlayStateResult, }; -use client::{self, Client, Event::Chat}; +use client::{self, Client}; use common::{ assets::{load_watched, watch}, clock::Clock, comp, comp::{Pos, Vel, MAX_PICKUP_RANGE_SQR}, - msg::{ClientState, Notification}, + msg::ClientState, terrain::{Block, BlockKind}, util::Dir, vol::ReadVol, - ChatType, }; use specs::{Join, WorldExt}; use std::{cell::RefCell, rc::Rc, time::Duration}; @@ -84,12 +83,8 @@ impl SessionState { crate::ecs::sys::add_local_systems, )? { match event { - Chat { - chat_type: _, - ref message, - } => { - info!("[CHAT] {}", message); - self.hud.new_message(event); + client::Event::Chat(m) => { + self.hud.new_message(m); }, client::Event::Disconnect => return Ok(TickAction::Disconnect), client::Event::DisconnectionNotification(time) => { @@ -98,14 +93,13 @@ impl SessionState { _ => format!("Connection lost. Kicking in {} seconds", time), }; - self.hud.new_message(Chat { - chat_type: ChatType::Meta, + self.hud.new_message(comp::ChatMsg { + chat_type: comp::ChatType::Private, message, }); }, - client::Event::Notification(Notification::WaypointSaved) => { - self.hud - .new_message(client::Event::Notification(Notification::WaypointSaved)); + client::Event::Notification(n) => { + self.hud.new_notification(n); }, client::Event::SetViewDistance(vd) => { global_state.settings.graphics.view_distance = vd; @@ -507,10 +501,12 @@ impl PlayState for SessionState { self.scene.handle_input_event(Event::AnalogGameInput(other)); }, }, - Event::ScreenshotMessage(screenshot_message) => self.hud.new_message(Chat { - chat_type: ChatType::Meta, - message: screenshot_message, - }), + Event::ScreenshotMessage(screenshot_message) => { + self.hud.new_message(comp::ChatMsg { + chat_type: comp::ChatType::Private, + message: screenshot_message, + }) + }, // Pass all other events to the scene event => {