Rework chat processing

This commit is contained in:
CapsizeGlimmer 2020-06-01 22:42:26 -04:00 committed by Forest Anderson
parent 702a21302c
commit b08d717eac
14 changed files with 252 additions and 314 deletions

View File

@ -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 {

View File

@ -112,22 +112,9 @@ pub fn complete(line: &str, client: &Client) -> Vec<String> {
}
}
} 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

View File

@ -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);

View File

@ -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<Self>;
}
/// 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.
///

View File

@ -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,

View File

@ -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,
}

View File

@ -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<i32>, 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,

View File

@ -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<AuthClientError> 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,
}
})
}
}

View File

@ -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::<comp::Player>()
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()));
}
}
}

View File

@ -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<Self>>,
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 => "<Unknown>",
},
match stats.get(entity) {
Some(stat) => &stat.name,
None => "<Unknown>",
},
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()));
}
}
},
_ => {

View File

@ -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<ClientEvent>,
new_messages: &'a mut VecDeque<ChatMsg>,
force_input: Option<String>,
force_cursor: Option<Index>,
force_completions: Option<Vec<String>>,
@ -50,7 +53,7 @@ pub struct Chat<'a> {
impl<'a> Chat<'a> {
pub fn new(
new_messages: &'a mut VecDeque<ClientEvent>,
new_messages: &'a mut VecDeque<ChatMsg>,
global_state: &'a GlobalState,
imgs: &'a Imgs,
fonts: &'a ConrodVoxygenFonts,
@ -105,7 +108,7 @@ impl<'a> Chat<'a> {
}
pub struct State {
messages: VecDeque<ClientEvent>,
messages: VecDeque<ChatMsg>,
input: String,
ids: Ids,
history: VecDeque<String>,
@ -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::<Vec<_>>(),
)
});
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.

View File

@ -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<ClientEvent>,
new_messages: VecDeque<comp::ChatMsg>,
new_notifications: VecDeque<common::msg::Notification>,
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 {

View File

@ -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<VoxygenLocalization>,
client: &'a Client,
new_messages: &'a VecDeque<ClientEvent>,
new_notifications: &'a VecDeque<Notification>,
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<VoxygenLocalization>,
client: &'a Client,
new_messages: &'a VecDeque<ClientEvent>,
new_notifications: &'a VecDeque<Notification>,
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());
});
},
_ => {},
}
}

View File

@ -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 => {