mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Implement chat filtering for /say /region /group etc.
This commit is contained in:
parent
5cbecb29e6
commit
c984bdcdf1
@ -1,4 +1,4 @@
|
||||
use crate::path::Chaser;
|
||||
use crate::{path::Chaser, state::Time};
|
||||
use specs::{Component, Entity as EcsEntity};
|
||||
use specs_idvs::IDVStorage;
|
||||
use vek::*;
|
||||
@ -128,9 +128,6 @@ pub struct SpeechBubble {
|
||||
pub timeout: Option<Time>,
|
||||
// TODO add icon enum for player chat type / npc quest+trade
|
||||
}
|
||||
impl Component for SpeechBubble {
|
||||
type Storage = FlaggedStorage<Self, HashMapStorage<Self>>;
|
||||
}
|
||||
impl SpeechBubble {
|
||||
pub fn npc_new(i18n_key: String, now: Time) -> Self {
|
||||
let message = SpeechBubbleMessage::Localized(i18n_key, rand::random());
|
||||
|
@ -26,7 +26,7 @@ impl Component for ChatMode {
|
||||
|
||||
impl ChatMode {
|
||||
/// Create a message from your current chat mode and uuid.
|
||||
pub fn msg_from(&self, from: Uid, message: String) -> ChatMsg {
|
||||
pub fn new_message(&self, from: Uid, message: String) -> ChatMsg {
|
||||
let chat_type = match self {
|
||||
ChatMode::Tell(to) => ChatType::Tell(from, *to),
|
||||
ChatMode::Say => ChatType::Say(from),
|
||||
@ -79,6 +79,15 @@ pub struct ChatMsg {
|
||||
}
|
||||
|
||||
impl ChatMsg {
|
||||
pub const NPC_DISTANCE: f32 = 100.0;
|
||||
pub const REGION_DISTANCE: f32 = 1000.0;
|
||||
pub const SAY_DISTANCE: f32 = 100.0;
|
||||
|
||||
pub fn broadcast(message: String) -> Self {
|
||||
let chat_type = ChatType::Broadcast;
|
||||
Self { chat_type, message }
|
||||
}
|
||||
|
||||
pub fn npc(uid: Uid, message: String) -> Self {
|
||||
let chat_type = ChatType::Npc(uid, rand::random());
|
||||
Self { chat_type, message }
|
||||
@ -105,6 +114,21 @@ impl ChatMsg {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn uid(&self) -> Option<Uid> {
|
||||
match &self.chat_type {
|
||||
ChatType::Broadcast => None,
|
||||
ChatType::Private => None,
|
||||
ChatType::Kill => None,
|
||||
ChatType::Tell(u, _t) => Some(*u),
|
||||
ChatType::Say(u) => Some(*u),
|
||||
ChatType::Group(u, _s) => Some(*u),
|
||||
ChatType::Faction(u, _s) => Some(*u),
|
||||
ChatType::Region(u) => Some(*u),
|
||||
ChatType::World(u) => Some(*u),
|
||||
ChatType::Npc(u, _r) => Some(*u),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Player groups are useful when forming raiding parties and coordinating
|
||||
|
@ -116,12 +116,6 @@ impl From<AuthClientError> for RegisterError {
|
||||
}
|
||||
|
||||
impl ServerMsg {
|
||||
/// 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(comp::ChatMsg {
|
||||
chat_type: comp::ChatType::Broadcast,
|
||||
|
@ -1033,11 +1033,7 @@ fn handle_tell(
|
||||
} else {
|
||||
msg.to_string()
|
||||
};
|
||||
server.notify_client(
|
||||
player,
|
||||
ServerMsg::chat(mode.clone(), client_uid, msg.clone()),
|
||||
);
|
||||
server.notify_client(client, ServerMsg::chat(mode, client_uid, msg));
|
||||
server.state.send_chat(mode.new_message(client_uid, msg));
|
||||
} else {
|
||||
server.notify_client(
|
||||
client,
|
||||
@ -1073,11 +1069,9 @@ fn handle_faction(
|
||||
let _ = ecs.write_storage().insert(client, mode.clone());
|
||||
if !msg.is_empty() {
|
||||
if let Some(uid) = ecs.read_storage().get(client) {
|
||||
server.state.notify_registered_clients(ServerMsg::chat(
|
||||
mode,
|
||||
*uid,
|
||||
msg.to_string(),
|
||||
));
|
||||
server
|
||||
.state
|
||||
.send_chat(mode.new_message(*uid, msg.to_string()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1109,11 +1103,9 @@ fn handle_group(
|
||||
let _ = ecs.write_storage().insert(client, mode.clone());
|
||||
if !msg.is_empty() {
|
||||
if let Some(uid) = ecs.read_storage().get(client) {
|
||||
server.state.notify_registered_clients(ServerMsg::chat(
|
||||
mode,
|
||||
*uid,
|
||||
msg.to_string(),
|
||||
));
|
||||
server
|
||||
.state
|
||||
.send_chat(mode.new_message(*uid, msg.to_string()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -1149,7 +1141,7 @@ fn handle_region(
|
||||
if let Some(uid) = server.state.ecs().read_storage().get(client) {
|
||||
server
|
||||
.state
|
||||
.notify_registered_clients(ServerMsg::chat(mode, *uid, msg.to_string()));
|
||||
.send_chat(mode.new_message(*uid, msg.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1179,7 +1171,7 @@ fn handle_say(
|
||||
if let Some(uid) = server.state.ecs().read_storage().get(client) {
|
||||
server
|
||||
.state
|
||||
.notify_registered_clients(ServerMsg::chat(mode, *uid, msg.to_string()));
|
||||
.send_chat(mode.new_message(*uid, msg.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1209,7 +1201,7 @@ fn handle_world(
|
||||
if let Some(uid) = server.state.ecs().read_storage().get(client) {
|
||||
server
|
||||
.state
|
||||
.notify_registered_clients(ServerMsg::chat(mode, *uid, msg.to_string()));
|
||||
.send_chat(mode.new_message(*uid, msg.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
use crate::{state_ext::StateExt, Server};
|
||||
use common::{
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::ServerMsg,
|
||||
};
|
||||
use common::event::{EventBus, ServerEvent};
|
||||
use entity_creation::{
|
||||
handle_create_npc, handle_create_waypoint, handle_initialize_character,
|
||||
handle_loaded_character_data, handle_shoot,
|
||||
@ -23,16 +20,9 @@ mod inventory_manip;
|
||||
mod player;
|
||||
|
||||
pub enum Event {
|
||||
ClientConnected {
|
||||
entity: EcsEntity,
|
||||
},
|
||||
ClientDisconnected {
|
||||
entity: EcsEntity,
|
||||
},
|
||||
Chat {
|
||||
entity: Option<EcsEntity>,
|
||||
msg: String,
|
||||
},
|
||||
ClientConnected { entity: EcsEntity },
|
||||
ClientDisconnected { entity: EcsEntity },
|
||||
Chat { entity: Option<EcsEntity>, msg: String },
|
||||
}
|
||||
|
||||
impl Server {
|
||||
@ -41,6 +31,7 @@ impl Server {
|
||||
|
||||
let mut requested_chunks = Vec::new();
|
||||
let mut chat_commands = Vec::new();
|
||||
let mut chat_messages = Vec::new();
|
||||
|
||||
let events = self
|
||||
.state
|
||||
@ -107,8 +98,7 @@ impl Server {
|
||||
chat_commands.push((entity, cmd));
|
||||
},
|
||||
ServerEvent::Chat(msg) => {
|
||||
self.state
|
||||
.notify_registered_clients(ServerMsg::ChatMsg(msg));
|
||||
chat_messages.push(msg);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -122,6 +112,10 @@ impl Server {
|
||||
self.process_chat_cmd(entity, cmd);
|
||||
}
|
||||
|
||||
for msg in chat_messages {
|
||||
self.state.send_chat(msg);
|
||||
}
|
||||
|
||||
frontend_events
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use common::{
|
||||
msg::{ClientState, PlayerListUpdate, ServerMsg},
|
||||
sync::{Uid, UidAllocator},
|
||||
};
|
||||
use specs::{saveload::MarkerAllocator, Builder, Entity as EcsEntity, Join, WorldExt};
|
||||
use specs::{saveload::MarkerAllocator, Builder, Entity as EcsEntity, WorldExt};
|
||||
use tracing::error;
|
||||
|
||||
pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) {
|
||||
@ -60,19 +60,12 @@ pub fn handle_client_disconnect(server: &mut Server, entity: EcsEntity) -> Event
|
||||
|
||||
// Make sure to remove the player from the logged in list. (See AuthProvider)
|
||||
// And send a disconnected message
|
||||
{
|
||||
let players = state.ecs().read_storage::<Player>();
|
||||
if let Some(player) = state.ecs().read_storage::<Player>().get(entity) {
|
||||
let mut accounts = state.ecs().write_resource::<AuthProvider>();
|
||||
let mut clients = state.ecs().write_storage::<Client>();
|
||||
accounts.logout(player.uuid());
|
||||
|
||||
if let Some(player) = players.get(entity) {
|
||||
accounts.logout(player.uuid());
|
||||
|
||||
let msg = ServerMsg::broadcast(format!("{} went offline.", &player.alias));
|
||||
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
||||
client.notify(msg.clone());
|
||||
}
|
||||
}
|
||||
let msg = ServerMsg::broadcast(format!("{} went offline.", &player.alias));
|
||||
state.notify_registered_clients(msg);
|
||||
}
|
||||
|
||||
// Sync the player's character data to the database
|
||||
|
@ -7,11 +7,14 @@ use common::{
|
||||
effect::Effect,
|
||||
msg::{CharacterInfo, ClientState, PlayerListUpdate, ServerMsg},
|
||||
state::State,
|
||||
sync::{Uid, WorldSyncExt},
|
||||
sync::{Uid, UidAllocator, WorldSyncExt},
|
||||
util::Dir,
|
||||
};
|
||||
use specs::{Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder, Join, WorldExt};
|
||||
use tracing::warn;
|
||||
use specs::{
|
||||
saveload::MarkerAllocator, Builder, Entity as EcsEntity, EntityBuilder as EcsEntityBuilder,
|
||||
Join, WorldExt,
|
||||
};
|
||||
use vek::*;
|
||||
|
||||
pub trait StateExt {
|
||||
@ -43,6 +46,14 @@ pub trait StateExt {
|
||||
/// Performed after loading component data from the database
|
||||
fn update_character_data(&mut self, entity: EcsEntity, components: PersistedComponents);
|
||||
/// Iterates over registered clients and send each `ServerMsg`
|
||||
fn create_player_character(
|
||||
&mut self,
|
||||
entity: EcsEntity,
|
||||
character_id: i32,
|
||||
body: comp::Body,
|
||||
server_settings: &ServerSettings,
|
||||
);
|
||||
fn send_chat(&self, msg: comp::ChatMsg);
|
||||
fn notify_registered_clients(&self, msg: ServerMsg);
|
||||
/// Delete an entity, recording the deletion in [`DeletedEntities`]
|
||||
fn delete_entity_recorded(
|
||||
@ -229,6 +240,117 @@ impl StateExt for State {
|
||||
self.write_component(entity, comp::ForceUpdate);
|
||||
}
|
||||
|
||||
/// Send the chat message to the proper players. Say and region are limited
|
||||
/// by location. Faction and group are limited by component.
|
||||
fn send_chat(&self, msg: comp::ChatMsg) {
|
||||
let ecs = self.ecs();
|
||||
let is_within = |target, a: &comp::Pos, b_opt: Option<&comp::Pos>| {
|
||||
if let Some(b) = b_opt {
|
||||
a.0.distance(b.0) < target
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
match &msg.chat_type {
|
||||
comp::ChatType::Broadcast
|
||||
| comp::ChatType::Kill
|
||||
| comp::ChatType::Private
|
||||
| comp::ChatType::World(_) => {
|
||||
self.notify_registered_clients(ServerMsg::ChatMsg(msg.clone()))
|
||||
},
|
||||
comp::ChatType::Tell(u, t) => {
|
||||
for (client, uid) in (
|
||||
&mut ecs.write_storage::<Client>(),
|
||||
&ecs.read_storage::<Uid>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
if uid == u || uid == t {
|
||||
client.notify(ServerMsg::ChatMsg(msg.clone()));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
comp::ChatType::Say(_u) => {
|
||||
for (client, pos) in (
|
||||
&mut ecs.write_storage::<Client>(),
|
||||
&ecs.read_storage::<comp::Pos>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let entity_opt = msg.uid().and_then(|uid| {
|
||||
(*ecs.read_resource::<UidAllocator>()).retrieve_entity_internal(uid.0)
|
||||
});
|
||||
let positions = ecs.read_storage::<comp::Pos>();
|
||||
let pos_opt = entity_opt.and_then(|e| positions.get(e));
|
||||
if is_within(comp::ChatMsg::SAY_DISTANCE, pos, pos_opt) {
|
||||
client.notify(ServerMsg::ChatMsg(msg.clone()));
|
||||
}
|
||||
}
|
||||
},
|
||||
comp::ChatType::Region(_u) => {
|
||||
for (client, pos) in (
|
||||
&mut ecs.write_storage::<Client>(),
|
||||
&ecs.read_storage::<comp::Pos>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let entity_opt = msg.uid().and_then(|uid| {
|
||||
(*ecs.read_resource::<UidAllocator>()).retrieve_entity_internal(uid.0)
|
||||
});
|
||||
let positions = ecs.read_storage::<comp::Pos>();
|
||||
let pos_opt = entity_opt.and_then(|e| positions.get(e));
|
||||
if is_within(comp::ChatMsg::REGION_DISTANCE, pos, pos_opt) {
|
||||
client.notify(ServerMsg::ChatMsg(msg.clone()));
|
||||
}
|
||||
}
|
||||
},
|
||||
comp::ChatType::Npc(_u, _r) => {
|
||||
for (client, pos) in (
|
||||
&mut ecs.write_storage::<Client>(),
|
||||
&ecs.read_storage::<comp::Pos>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
let entity_opt = msg.uid().and_then(|uid| {
|
||||
(*ecs.read_resource::<UidAllocator>()).retrieve_entity_internal(uid.0)
|
||||
});
|
||||
let positions = ecs.read_storage::<comp::Pos>();
|
||||
let pos_opt = entity_opt.and_then(|e| positions.get(e));
|
||||
if is_within(comp::ChatMsg::NPC_DISTANCE, pos, pos_opt) {
|
||||
client.notify(ServerMsg::ChatMsg(msg.clone()));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
comp::ChatType::Faction(_u, s) => {
|
||||
for (client, faction) in (
|
||||
&mut ecs.write_storage::<Client>(),
|
||||
&ecs.read_storage::<comp::Faction>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
if s == &faction.0 {
|
||||
client.notify(ServerMsg::ChatMsg(msg.clone()));
|
||||
}
|
||||
}
|
||||
},
|
||||
comp::ChatType::Group(_u, s) => {
|
||||
for (client, group) in (
|
||||
&mut ecs.write_storage::<Client>(),
|
||||
&ecs.read_storage::<comp::Group>(),
|
||||
)
|
||||
.join()
|
||||
{
|
||||
if s == &group.0 {
|
||||
client.notify(ServerMsg::ChatMsg(msg.clone()));
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Sends the message to all connected clients
|
||||
fn notify_registered_clients(&self, msg: ServerMsg) {
|
||||
for client in (&mut self.ecs().write_storage::<Client>())
|
||||
.join()
|
||||
|
@ -5,8 +5,8 @@ use crate::{
|
||||
};
|
||||
use common::{
|
||||
comp::{
|
||||
Admin, CanBuild, ChatMode, ControlEvent, Controller, ForceUpdate, Ori, Player, Pos, Stats,
|
||||
Vel,
|
||||
Admin, CanBuild, ChatMode, ChatMsg, ControlEvent, Controller, ForceUpdate, Ori, Player,
|
||||
Pos, Stats, Vel,
|
||||
},
|
||||
event::{EventBus, ServerEvent},
|
||||
msg::{
|
||||
@ -230,7 +230,11 @@ impl<'a> System<'a> for Sys {
|
||||
// Become Registered first.
|
||||
ClientState::Connected => client.error_state(RequestStateError::Impossible),
|
||||
ClientState::Registered | ClientState::Spectator => {
|
||||
if let Some(player) = players.get(entity) {
|
||||
// Only send login message if it wasn't already
|
||||
// sent previously
|
||||
if let (Some(player), false) =
|
||||
(players.get(entity), client.login_msg_sent)
|
||||
{
|
||||
// Send a request to load the character's component data from the
|
||||
// DB. Once loaded, persisted components such as stats and inventory
|
||||
// will be inserted for the entity
|
||||
@ -240,6 +244,10 @@ impl<'a> System<'a> for Sys {
|
||||
character_id,
|
||||
);
|
||||
|
||||
server_emitter.emit(ServerEvent::Chat(ChatMsg::broadcast(
|
||||
format!("[{}] is now online.", &player.alias),
|
||||
)));
|
||||
|
||||
// Start inserting non-persisted/default components for the entity
|
||||
// while we load the DB data
|
||||
server_emitter.emit(ServerEvent::InitCharacterData {
|
||||
@ -332,7 +340,7 @@ impl<'a> System<'a> for Sys {
|
||||
.get(entity)
|
||||
.map(Clone::clone)
|
||||
.unwrap_or(ChatMode::default());
|
||||
let msg = ServerMsg::chat(mode, *from, message);
|
||||
let msg = mode.new_message(*from, message);
|
||||
new_chat_msgs.push((Some(entity), msg));
|
||||
} else {
|
||||
tracing::error!("Could not send message. Missing player uid");
|
||||
@ -458,25 +466,15 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
// Handle new chat messages.
|
||||
for (entity, msg) in new_chat_msgs {
|
||||
match msg {
|
||||
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));
|
||||
}
|
||||
} else {
|
||||
// Send speech bubble and chat message
|
||||
// TODO filter group, faction, say, and bubble distance.
|
||||
for client in (&mut clients).join().filter(|c| c.is_registered()) {
|
||||
client.notify(ServerMsg::ChatMsg(msg.clone()));
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
panic!("Invalid message type.");
|
||||
},
|
||||
// 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));
|
||||
}
|
||||
} else {
|
||||
// Send chat message
|
||||
server_emitter.emit(ServerEvent::Chat(msg));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user