diff --git a/chat-cli/src/main.rs b/chat-cli/src/main.rs index e58dca26fa..cb8b111de2 100644 --- a/chat-cli/src/main.rs +++ b/chat-cli/src/main.rs @@ -68,7 +68,7 @@ fn main() { for event in events { match event { - Event::Chat(msg) => println!("{}", msg), + Event::ChatMsg(msg) => println!("{}", msg), Event::Disconnect => {} // TODO } } diff --git a/client/src/lib.rs b/client/src/lib.rs index be6f82bd67..1be02e2527 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -14,6 +14,7 @@ use common::{ state::State, terrain::{block::Block, chonk::ChonkMetrics, TerrainChunk, TerrainChunkSize}, vol::VolSize, + ChatType, }; use log::{info, log_enabled, warn}; use std::{ @@ -28,7 +29,10 @@ use vek::*; const SERVER_TIMEOUT: Duration = Duration::from_secs(20); pub enum Event { - Chat(String), + Chat { + chat_type: ChatType, + message: String, + }, Disconnect, } @@ -187,7 +191,7 @@ impl Client { /// Send a chat message to the server. #[allow(dead_code)] pub fn send_chat(&mut self, msg: String) { - self.postbox.send_message(ClientMsg::Chat(msg)) + self.postbox.send_message(ClientMsg::chat(msg)) } /// Remove all cached terrain @@ -385,7 +389,16 @@ impl Client { .duration_since(self.last_server_ping) .as_secs_f64() } - ServerMsg::Chat(msg) => frontend_events.push(Event::Chat(msg)), + ServerMsg::ChatMsg { chat_type, msg } => match chat_type { + ChatType::Chat => frontend_events.push(Event::Chat { + chat_type: ChatType::Chat, + message: msg, + }), + ChatType::Tell => frontend_events.push(Event::Chat { + chat_type: ChatType::Tell, + message: msg, + }), + }, ServerMsg::SetPlayerEntity(uid) => { self.entity = self.state.ecs().entity_from_uid(uid).unwrap() } // TODO: Don't unwrap here! diff --git a/common/src/lib.rs b/common/src/lib.rs index a9706ef52e..67330defb8 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -50,3 +50,9 @@ pub mod volumes; /// assert_eq!("bar", scon.next_message().unwrap()); /// ``` pub mod net; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum ChatType { + Chat, + Tell, +} diff --git a/common/src/msg/client.rs b/common/src/msg/client.rs index 1da55e7857..c5ab98c029 100644 --- a/common/src/msg/client.rs +++ b/common/src/msg/client.rs @@ -1,6 +1,6 @@ use super::ClientState; -use crate::comp; use crate::terrain::block::Block; +use crate::{comp, ChatType}; use vek::*; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -19,7 +19,10 @@ pub enum ClientMsg { PlaceBlock(Vec3, Block), Ping, Pong, - Chat(String), + ChatMsg { + chat_type: ChatType, + msg: String, + }, PlayerPhysics { pos: comp::Pos, vel: comp::Vel, @@ -30,3 +33,18 @@ pub enum ClientMsg { }, Disconnect, } + +impl ClientMsg { + pub fn chat(message: String) -> crate::msg::client::ClientMsg { + crate::msg::client::ClientMsg::ChatMsg { + chat_type: ChatType::Chat, + msg: message, + } + } + pub fn tell(message: String) -> crate::msg::client::ClientMsg { + crate::msg::client::ClientMsg::ChatMsg { + chat_type: ChatType::Tell, + msg: message, + } + } +} diff --git a/common/src/msg/server.rs b/common/src/msg/server.rs index 499df4f4e3..f71e065560 100644 --- a/common/src/msg/server.rs +++ b/common/src/msg/server.rs @@ -1,9 +1,5 @@ use super::{ClientState, EcsCompPacket, EcsResPacket}; -use crate::{ - comp, - terrain::{Block, TerrainChunk}, -}; -use fxhash::FxHashMap; +use crate::{comp, terrain::TerrainChunk, ChatType}; use vek::*; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -32,7 +28,10 @@ pub enum ServerMsg { ForceState(ClientState), Ping, Pong, - Chat(String), + ChatMsg { + chat_type: ChatType, + msg: String, + }, SetPlayerEntity(u64), EcsSync(sphynx::SyncPackage), EntityPhysics { @@ -57,3 +56,18 @@ pub enum ServerError { TooManyPlayers, //TODO: InvalidAlias, } + +impl ServerMsg { + pub fn chat(message: String) -> crate::msg::server::ServerMsg { + crate::msg::server::ServerMsg::ChatMsg { + chat_type: ChatType::Chat, + msg: message, + } + } + pub fn tell(message: String) -> crate::msg::server::ServerMsg { + crate::msg::server::ServerMsg::ChatMsg { + chat_type: ChatType::Tell, + msg: message, + } + } +} diff --git a/server/src/cmd.rs b/server/src/cmd.rs index b78319e8b1..abde945bc6 100644 --- a/server/src/cmd.rs +++ b/server/src/cmd.rs @@ -166,13 +166,13 @@ fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &Ch } None => server.clients.notify( entity, - ServerMsg::Chat(String::from("You have no position!")), + ServerMsg::chat(String::from("You have no position!")), ), } } _ => server .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))), + .notify(entity, ServerMsg::chat(String::from(action.help_string))), } } @@ -188,12 +188,12 @@ fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &Ch } _ => server .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))), + .notify(entity, ServerMsg::chat(String::from(action.help_string))), }, None => { server.clients.notify( entity, - ServerMsg::Chat(String::from("You don't have any position!")), + ServerMsg::chat(String::from("You don't have any position!")), ); } } @@ -221,14 +221,14 @@ fn handle_time(server: &mut Server, entity: EcsEntity, args: String, action: &Ch Err(_) => { server .clients - .notify(entity, ServerMsg::Chat(format!("'{}' is not a time!", n))); + .notify(entity, ServerMsg::chat(format!("'{}' is not a time!", n))); return; } }, None => { server.clients.notify( entity, - ServerMsg::Chat("You must specify a time!".to_string()), + ServerMsg::chat("You must specify a time!".to_string()), ); return; } @@ -249,13 +249,13 @@ fn handle_health(server: &mut Server, entity: EcsEntity, args: String, action: & None => { server.clients.notify( entity, - ServerMsg::Chat(String::from("You must specify health amount!")), + ServerMsg::chat(String::from("You must specify health amount!")), ); } }, None => server.clients.notify( entity, - ServerMsg::Chat(String::from("You have no position.")), + ServerMsg::chat(String::from("You have no position.")), ), } } @@ -273,7 +273,7 @@ fn handle_alias(server: &mut Server, entity: EcsEntity, args: String, action: &C } None => server .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))), + .notify(entity, ServerMsg::chat(String::from(action.help_string))), } } @@ -295,29 +295,29 @@ fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &Chat } None => server.clients.notify( entity, - ServerMsg::Chat(format!("Unable to teleport to player '{}'!", alias)), + ServerMsg::chat(format!("Unable to teleport to player '{}'!", alias)), ), }, None => { server.clients.notify( entity, - ServerMsg::Chat(format!("Player '{}' not found!", alias)), + ServerMsg::chat(format!("Player '{}' not found!", alias)), ); server .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))); + .notify(entity, ServerMsg::chat(String::from(action.help_string))); } }, None => { server .clients - .notify(entity, ServerMsg::Chat(format!("You have no position!"))); + .notify(entity, ServerMsg::chat(format!("You have no position!"))); } } } None => server .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))), + .notify(entity, ServerMsg::chat(String::from(action.help_string))), } } @@ -351,17 +351,17 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C } server.clients.notify( entity, - ServerMsg::Chat(format!("Spawned {} entities", amount).to_owned()), + ServerMsg::chat(format!("Spawned {} entities", amount).to_owned()), ); } None => server .clients - .notify(entity, ServerMsg::Chat("You have no position!".to_owned())), + .notify(entity, ServerMsg::chat("You have no position!".to_owned())), } } _ => server .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))), + .notify(entity, ServerMsg::chat(String::from(action.help_string))), } } @@ -381,11 +381,11 @@ fn handle_players(server: &mut Server, entity: EcsEntity, _args: String, _action server .clients - .notify(entity, ServerMsg::Chat(header_message + &player_list)); + .notify(entity, ServerMsg::chat(header_message + &player_list)); } else { server .clients - .notify(entity, ServerMsg::Chat(header_message)); + .notify(entity, ServerMsg::chat(header_message)); } } @@ -403,7 +403,7 @@ fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action: .remove(entity); server.clients.notify( entity, - ServerMsg::Chat(String::from("Toggled off build mode!")), + ServerMsg::chat(String::from("Toggled off build mode!")), ); } else { let _ = server @@ -413,7 +413,7 @@ fn handle_build(server: &mut Server, entity: EcsEntity, _args: String, _action: .insert(entity, comp::CanBuild); server.clients.notify( entity, - ServerMsg::Chat(String::from("Toggled on build mode!")), + ServerMsg::chat(String::from("Toggled on build mode!")), ); } } @@ -422,7 +422,7 @@ fn handle_help(server: &mut Server, entity: EcsEntity, _args: String, _action: & for cmd in CHAT_COMMANDS.iter() { server .clients - .notify(entity, ServerMsg::Chat(String::from(cmd.help_string))); + .notify(entity, ServerMsg::chat(String::from(cmd.help_string))); } } @@ -460,7 +460,7 @@ fn handle_killnpcs(server: &mut Server, entity: EcsEntity, _args: String, _actio } else { "No NPCs on server.".to_string() }; - server.clients.notify(entity, ServerMsg::Chat(text)); + server.clients.notify(entity, ServerMsg::chat(text)); } fn handle_object(server: &mut Server, entity: EcsEntity, args: String, _action: &ChatCommand) { @@ -613,29 +613,23 @@ fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &Ch let msg = &args[alias.len()..args.len()]; match opt_player { Some(player) => { - if player != entity { - if msg.len() > 1 { - let opt_name = ecs - .read_storage::() - .get(entity) - .map(|s| s.alias.clone()); - match opt_name { - Some(name) => { - server.clients.notify( - player, - ServerMsg::Chat(format!("{} tells you:{}", name, msg)), - ); - server.clients.notify( - entity, - ServerMsg::Chat(format!("You tell {}:{}", alias, msg)), - ); - } - None => { - server.clients.notify( - entity, - ServerMsg::Chat(String::from("You do not exist!")), - ); - } + if msg.len() > 1 { + let opt_name = ecs + .read_storage::() + .get(entity) + .map(|s| s.alias.clone()); + match opt_name { + Some(name) => { + server.clients.notify( + player, + ServerMsg::tell(format!("{} tells you:{}", name, msg)), + ); + } + None => { + server.clients.notify( + entity, + ServerMsg::chat(String::from("You do not exist!")), + ); } } else { server.clients.notify( @@ -647,21 +641,25 @@ fn handle_tell(server: &mut Server, entity: EcsEntity, args: String, action: &Ch ); } } else { - server - .clients - .notify(entity, ServerMsg::Chat(format!("Don't be crazy!"))); + server.clients.notify( + entity, + ServerMsg::chat(format!( + "You really should say something to {}!", + alias + )), + ); } } None => { server.clients.notify( entity, - ServerMsg::Chat(format!("Player '{}' not found!", alias)), + ServerMsg::chat(format!("Player '{}' not found!", alias)), ); } } } None => server .clients - .notify(entity, ServerMsg::Chat(String::from(action.help_string))), + .notify(entity, ServerMsg::chat(String::from(action.help_string))), } } diff --git a/server/src/lib.rs b/server/src/lib.rs index b73eea4284..65473961d1 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -526,14 +526,17 @@ impl Server { } ClientState::Pending => {} }, - ClientMsg::Chat(msg) => match client.client_state { + ClientMsg::ChatMsg { + chat_type: _, + msg: message, + } => match client.client_state { ClientState::Connected => { client.error_state(RequestStateError::Impossible) } ClientState::Registered | ClientState::Spectator | ClientState::Dead - | ClientState::Character => new_chat_msgs.push((Some(entity), msg)), + | ClientState::Character => new_chat_msgs.push((Some(entity), message)), ClientState::Pending => {} }, ClientMsg::PlayerPhysics { pos, vel, ori } => match client.client_state { @@ -622,7 +625,7 @@ impl Server { let argv = String::from(&msg[1..]); self.process_chat_cmd(entity, argv); } else { - self.clients.notify_registered(ServerMsg::Chat( + self.clients.notify_registered(ServerMsg::chat( match self.state.ecs().read_storage::().get(entity) { Some(player) => format!("[{}] {}", &player.alias, msg), None => format!("[] {}", msg), @@ -630,7 +633,7 @@ impl Server { )); } } else { - self.clients.notify_registered(ServerMsg::Chat(msg.clone())); + self.clients.notify_registered(ServerMsg::chat(msg.clone())); } frontend_events.push(Event::Chat { entity, msg }); } @@ -719,7 +722,7 @@ impl Server { } .unwrap_or(format!("{} died", &player.alias)); - clients.notify_registered(ServerMsg::Chat(msg)); + clients.notify_registered(ServerMsg::chat(msg)); } entity @@ -849,7 +852,7 @@ impl Server { None => { self.clients.notify( entity, - ServerMsg::Chat(format!( + ServerMsg::chat(format!( "Unrecognised command: '/{}'\ntype '/help' for a list of available commands", kwd )), diff --git a/voxygen/src/hud/chat.rs b/voxygen/src/hud/chat.rs index d6e29d8e72..f11a58a2a4 100644 --- a/voxygen/src/hud/chat.rs +++ b/voxygen/src/hud/chat.rs @@ -1,4 +1,6 @@ -use super::{img_ids::Imgs, Fonts, TEXT_COLOR}; +use super::{img_ids::Imgs, Fonts, TELL_COLOR, TEXT_COLOR}; +use client::Event as ClientEvent; +use common::ChatType; use conrod_core::{ input::Key, position::Dimension, @@ -22,7 +24,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, @@ -34,7 +36,11 @@ pub struct Chat<'a> { } impl<'a> Chat<'a> { - pub fn new(new_messages: &'a mut VecDeque, imgs: &'a Imgs, fonts: &'a Fonts) -> Self { + pub fn new( + new_messages: &'a mut VecDeque, + imgs: &'a Imgs, + fonts: &'a Fonts, + ) -> Self { Self { new_messages, force_input: None, @@ -71,7 +77,7 @@ impl<'a> Chat<'a> { } pub struct State { - messages: VecDeque, + messages: VecDeque, input: String, ids: Ids, @@ -179,27 +185,44 @@ impl<'a> Widget for Chat<'a> { while let Some(item) = items.next(ui) { // This would be easier if conrod used the v-metrics from rusttype. let widget = if item.i < state.messages.len() { - let text = Text::new(&state.messages[item.i]) - .font_size(15) - .font_id(self.fonts.opensans) - .w(470.0) - .color(TEXT_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, - }; - text.h(y) + let msg = &state.messages[item.i]; + match msg { + ClientEvent::Chat { chat_type, message } => { + let color = match chat_type { + ChatType::Tell => TELL_COLOR, + _ => TEXT_COLOR, + }; + let text = Text::new(&message) + .font_size(15) + .font_id(self.fonts.opensans) + .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, + } } else { // Spacer at bottom of the last message so that it is not cut off. // Needs to be larger than the space above. - Text::new("") - .font_size(6) - .font_id(self.fonts.opensans) - .w(470.0) + Some( + Text::new("") + .font_size(6) + .font_id(self.fonts.opensans) + .w(470.0), + ) }; - item.set(widget, ui); + match widget { + Some(widget) => { + item.set(widget, ui); + } + None => {} + } } // Chat Arrow diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index c257a13bb5..3cbb4fa615 100644 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -31,7 +31,7 @@ use crate::{ window::{Event as WinEvent, GameInput, Window}, GlobalState, }; -use client::Client; +use client::{Client, Event as ClientEvent}; use common::{comp, terrain::TerrainChunkSize, vol::VolSize}; use conrod_core::{ text::cursor::Index, @@ -50,6 +50,7 @@ const TEXT_COLOR: Color = Color::Rgba(1.0, 1.0, 1.0, 1.0); const TEXT_COLOR_2: Color = Color::Rgba(0.0, 0.0, 0.0, 1.0); const HP_COLOR: Color = Color::Rgba(0.33, 0.63, 0.0, 1.0); const MANA_COLOR: Color = Color::Rgba(0.42, 0.41, 0.66, 1.0); +const TELL_COLOR: Color = Color::Rgba(1.0, 1.0, 0.0, 1.0); widget_ids! { struct Ids { @@ -269,7 +270,7 @@ pub struct Hud { ids: Ids, imgs: Imgs, fonts: Fonts, - new_messages: VecDeque, + new_messages: VecDeque, inventory_space: usize, show: Show, to_focus: Option>, @@ -743,7 +744,7 @@ impl Hud { events } - pub fn new_message(&mut self, msg: String) { + pub fn new_message(&mut self, msg: ClientEvent) { self.new_messages.push_back(msg); } diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 882d78cb2b..1bfb22d706 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -44,8 +44,11 @@ impl SessionState { fn tick(&mut self, dt: Duration) -> Result<(), Error> { for event in self.client.borrow_mut().tick(self.controller.clone(), dt)? { match event { - client::Event::Chat(msg) => { - self.hud.new_message(msg); + client::Event::Chat { + chat_type: _, + message: _, + } => { + self.hud.new_message(event); } client::Event::Disconnect => {} // TODO }