diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 404cffd4f7..90b7f13b7d 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -30,6 +30,7 @@ use common::{ }; use common_net::{msg::ServerGeneral, sync::WorldSyncExt}; use common_sys::state::BlockChange; +use comp::chat::GenericChatMsg; use hashbrown::HashSet; use specs::{join::Join, saveload::MarkerAllocator, Entity as EcsEntity, WorldExt}; use tracing::error; @@ -130,8 +131,8 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc // Chat message // If it was a player that died if let Some(_player) = state.ecs().read_storage::().get(entity) { - if let Some(_uid) = state.ecs().read_storage::().get(entity) { - let _kill_source = match cause { + if let Some(uid) = state.ecs().read_storage::().get(entity) { + let kill_source = match cause { HealthSource::Damage { kind: DamageSource::Melee, by: Some(by), @@ -169,13 +170,11 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc | HealthSource::Heal { by: _ } | HealthSource::Unknown => KillSource::Other, }; - // TODO Reactivate and only show death messages for group members - // and players within close range (e.g. same range as /say) to avoid - // chat spamming - /*state.notify_players(ServerGeneral::server_msg( - comp::ChatType::Kill(kill_source, *uid), - "".to_string(), - ));*/ + + state.send_chat(GenericChatMsg { + chat_type: comp::ChatType::Kill(kill_source, *uid), + message: "".to_string(), + }); } } diff --git a/server/src/settings.rs b/server/src/settings.rs index 1ffc79b051..d193ac657a 100644 --- a/server/src/settings.rs +++ b/server/src/settings.rs @@ -44,6 +44,7 @@ pub struct Settings { pub client_timeout: Duration, pub spawn_town: Option, pub safe_spawn: bool, + pub max_player_for_kill_broadcast: Option, } impl Default for Settings { @@ -63,6 +64,7 @@ impl Default for Settings { client_timeout: Duration::from_secs(40), spawn_town: None, safe_spawn: true, + max_player_for_kill_broadcast: None, } } } diff --git a/server/src/state_ext.rs b/server/src/state_ext.rs index ab4a9eb04c..a0f5294031 100644 --- a/server/src/state_ext.rs +++ b/server/src/state_ext.rs @@ -1,5 +1,5 @@ use crate::{ - client::Client, persistence::PersistedComponents, presence::Presence, + client::Client, persistence::PersistedComponents, presence::Presence, settings::Settings, sys::sentinel::DeletedEntities, SpawnPoint, }; use common::{ @@ -8,7 +8,7 @@ use common::{ comp::{ self, skills::{GeneralSkill, Skill}, - Inventory, + Group, Inventory, }, effect::Effect, slowjob::SlowJobPool, @@ -503,7 +503,6 @@ impl StateExt for State { | comp::ChatType::CommandInfo | comp::ChatType::CommandError | comp::ChatType::Loot - | comp::ChatType::Kill(_, _) | comp::ChatType::Meta | comp::ChatType::World(_) => self.notify_players(ServerGeneral::ChatMsg(resolved_msg)), comp::ChatType::Online(u) => { @@ -524,7 +523,48 @@ impl StateExt for State { } } }, + comp::ChatType::Kill(kill_source, uid) => { + let comp::chat::GenericChatMsg { message, .. } = msg; + let clients = ecs.read_storage::(); + let clients_count = clients.count(); + // Avoid chat spam, send kill message only to group or nearby players if a + // certain amount of clients are online + if clients_count + > ecs + .fetch::() + .max_player_for_kill_broadcast + .unwrap_or_default() + { + // Send kill message to the dead player's group + let killed_entity = + (*ecs.read_resource::()).retrieve_entity_internal(uid.0); + let groups = ecs.read_storage::(); + let killed_group = killed_entity.and_then(|e| groups.get(e)); + if let Some(g) = &killed_group { + send_to_group(g, ecs, &resolved_msg); + } + // Send kill message to nearby players that aren't part of the deceased's group + let positions = ecs.read_storage::(); + if let Some(died_player_pos) = killed_entity.and_then(|e| positions.get(e)) { + for (ent, client, pos) in (&*ecs.entities(), &clients, &positions).join() { + let client_group = groups.get(ent); + let is_different_group = + !(killed_group == client_group && client_group.is_some()); + if is_within(comp::ChatMsg::SAY_DISTANCE, pos, died_player_pos) + && is_different_group + { + client.send_fallible(ServerGeneral::ChatMsg(resolved_msg.clone())); + } + } + } + } else { + self.notify_players(ServerGeneral::server_msg( + comp::ChatType::Kill(kill_source.clone(), *uid), + message, + )) + } + }, comp::ChatType::Say(uid) => { let entity_opt = (*ecs.read_resource::()).retrieve_entity_internal(uid.0);