mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Rework chat processing
This commit is contained in:
parent
702a21302c
commit
b08d717eac
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
///
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
});
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 => {
|
||||
|
Loading…
Reference in New Issue
Block a user