mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Addressed comments.
This commit is contained in:
parent
bda7fefdc0
commit
a0af315930
@ -188,13 +188,14 @@ https://account.veloren.net.
|
|||||||
"hud.chat.pvp_ranged_kill_msg": "[{attacker}] hat [{victim}] aus der Ferne getötet",
|
"hud.chat.pvp_ranged_kill_msg": "[{attacker}] hat [{victim}] aus der Ferne getötet",
|
||||||
"hud.chat.pvp_explosion_kill_msg": "[{attacker}] hat [{victim}] hochgejagt",
|
"hud.chat.pvp_explosion_kill_msg": "[{attacker}] hat [{victim}] hochgejagt",
|
||||||
"hud.chat.pvp_energy_kill_msg": "[{attacker}] hat [{victim}] mit Magie erledigt",
|
"hud.chat.pvp_energy_kill_msg": "[{attacker}] hat [{victim}] mit Magie erledigt",
|
||||||
"hud.chat.pvp_buff_kill_msg" : "[{attacker}] hat [{victim}] mit Magie erledigt",
|
"hud.chat.pvp_other_kill_msg" : "[{attacker}] hat [{victim}] mit Magie erledigt",
|
||||||
|
|
||||||
|
|
||||||
"hud.chat.npc_melee_kill_msg": "{attacker} tötete [{victim}]",
|
"hud.chat.npc_melee_kill_msg": "{attacker} tötete [{victim}]",
|
||||||
"hud.chat.npc_ranged_kill_msg": "{attacker} tötete [{victim}]",
|
"hud.chat.npc_ranged_kill_msg": "{attacker} tötete [{victim}]",
|
||||||
"hud.chat.npc_explosion_kill_msg": "{attacker} hat [{victim}] hochgejagt",
|
"hud.chat.npc_explosion_kill_msg": "{attacker} hat [{victim}] hochgejagt",
|
||||||
"hud.chat.npc_buff_kill_msg": "[{attacker}] tötete [{victim}]",
|
"hud.chat.npc_energy_kill_msg": "[{attacker}] hat [{victim}] mit Magie erledigt",
|
||||||
|
"hud.chat.npc_other_kill_msg": "[{attacker}] tötete [{victim}]",
|
||||||
|
|
||||||
// SCT outputs
|
// SCT outputs
|
||||||
"hud.sct.experience": "{amount} Erf",
|
"hud.sct.experience": "{amount} Erf",
|
||||||
|
@ -184,13 +184,14 @@ https://account.veloren.net."#,
|
|||||||
"hud.chat.pvp_ranged_kill_msg": "[{attacker}] shot [{victim}]",
|
"hud.chat.pvp_ranged_kill_msg": "[{attacker}] shot [{victim}]",
|
||||||
"hud.chat.pvp_explosion_kill_msg": "[{attacker}] blew up [{victim}]",
|
"hud.chat.pvp_explosion_kill_msg": "[{attacker}] blew up [{victim}]",
|
||||||
"hud.chat.pvp_energy_kill_msg": "[{attacker}] killed [{victim}] with magic",
|
"hud.chat.pvp_energy_kill_msg": "[{attacker}] killed [{victim}] with magic",
|
||||||
"hud.chat.pvp_buff_kill_msg": "[{attacker}] killed [{victim}]",
|
"hud.chat.pvp_other_kill_msg": "[{attacker}] killed [{victim}]",
|
||||||
|
|
||||||
|
|
||||||
"hud.chat.npc_melee_kill_msg": "{attacker} killed [{victim}]",
|
"hud.chat.npc_melee_kill_msg": "{attacker} killed [{victim}]",
|
||||||
"hud.chat.npc_ranged_kill_msg": "{attacker} shot [{victim}]",
|
"hud.chat.npc_ranged_kill_msg": "{attacker} shot [{victim}]",
|
||||||
"hud.chat.npc_explosion_kill_msg": "{attacker} blew up [{victim}]",
|
"hud.chat.npc_explosion_kill_msg": "{attacker} blew up [{victim}]",
|
||||||
"hud.chat.npc_buff_kill_msg": "[{attacker}] killed [{victim}]",
|
"hud.chat.npc_energy_kill_msg": "[{attacker}] killed [{victim}] with magic",
|
||||||
|
"hud.chat.npc_other_kill_msg": "[{attacker}] killed [{victim}]",
|
||||||
|
|
||||||
"hud.chat.loot_msg": "You picked up [{item}]",
|
"hud.chat.loot_msg": "You picked up [{item}]",
|
||||||
"hud.chat.loot_fail": "Your Inventory is full!",
|
"hud.chat.loot_fail": "Your Inventory is full!",
|
||||||
|
@ -184,13 +184,14 @@ bir hesap oluşturabilirsin."#,
|
|||||||
"hud.chat.pvp_ranged_kill_msg": "[{victim}], [{attacker}] tarafından vuruldu.",
|
"hud.chat.pvp_ranged_kill_msg": "[{victim}], [{attacker}] tarafından vuruldu.",
|
||||||
"hud.chat.pvp_explosion_kill_msg": "[{victim}], [{attacker}] tarafından havaya uçuruldu.",
|
"hud.chat.pvp_explosion_kill_msg": "[{victim}], [{attacker}] tarafından havaya uçuruldu.",
|
||||||
"hud.chat.pvp_energy_kill_msg": "[{victim}], [{attacker}] tarafından büyü ile mağlup edildi.",
|
"hud.chat.pvp_energy_kill_msg": "[{victim}], [{attacker}] tarafından büyü ile mağlup edildi.",
|
||||||
"hud.chat.pvp_buff_kill_msg": "[{victim}], [{attacker}] tarafından öldürüldü.",
|
"hud.chat.pvp_other_kill_msg": "[{victim}], [{attacker}] tarafından öldürüldü.",
|
||||||
|
|
||||||
|
|
||||||
"hud.chat.npc_melee_kill_msg": "[{victim}], [{attacker}] tarafından mağlup edildi.",
|
"hud.chat.npc_melee_kill_msg": "[{victim}], [{attacker}] tarafından mağlup edildi.",
|
||||||
"hud.chat.npc_ranged_kill_msg": "[{victim}], [{attacker}] tarafından vuruldu.",
|
"hud.chat.npc_ranged_kill_msg": "[{victim}], [{attacker}] tarafından vuruldu.",
|
||||||
"hud.chat.npc_explosion_kill_msg": "[{victim}], [{attacker}] tarafından havaya uçuruldu.",
|
"hud.chat.npc_explosion_kill_msg": "[{victim}], [{attacker}] tarafından havaya uçuruldu.",
|
||||||
"hud.chat.npc_buff_kill_msg": "[{victim}], [{attacker}] tarafından öldürüldü.",
|
"hud.chat.npc_energy_kill_msg": "[{victim}], [{attacker}] tarafından büyü ile mağlup edildi.",
|
||||||
|
"hud.chat.npc_other_kill_msg": "[{victim}], [{attacker}] tarafından öldürüldü.",
|
||||||
|
|
||||||
"hud.chat.loot_msg": "[{item}] topladın.",
|
"hud.chat.loot_msg": "[{item}] topladın.",
|
||||||
"hud.chat.loot_fail": "Envanterin dolu!",
|
"hud.chat.loot_fail": "Envanterin dolu!",
|
||||||
|
@ -1733,7 +1733,7 @@ impl Client {
|
|||||||
alias_of_uid(attacker_uid),
|
alias_of_uid(attacker_uid),
|
||||||
alias_of_uid(victim)
|
alias_of_uid(victim)
|
||||||
),
|
),
|
||||||
KillSource::Player(attacker_uid, KillType::Buff) => format!(
|
KillSource::Player(attacker_uid, KillType::Other) => format!(
|
||||||
"[{}] killed [{}]",
|
"[{}] killed [{}]",
|
||||||
alias_of_uid(attacker_uid),
|
alias_of_uid(attacker_uid),
|
||||||
alias_of_uid(victim)
|
alias_of_uid(victim)
|
||||||
@ -1752,7 +1752,7 @@ impl Client {
|
|||||||
attacker_name,
|
attacker_name,
|
||||||
alias_of_uid(victim)
|
alias_of_uid(victim)
|
||||||
),
|
),
|
||||||
KillSource::NonPlayer(attacker_name, KillType::Buff) => {
|
KillSource::NonPlayer(attacker_name, KillType::Other) => {
|
||||||
format!("{} killed [{}]", attacker_name, alias_of_uid(victim))
|
format!("{} killed [{}]", attacker_name, alias_of_uid(victim))
|
||||||
},
|
},
|
||||||
KillSource::Environment(environment) => {
|
KillSource::Environment(environment) => {
|
||||||
@ -1780,7 +1780,7 @@ impl Client {
|
|||||||
KillSource::Player(attacker_uid, KillType::Energy) => message
|
KillSource::Player(attacker_uid, KillType::Energy) => message
|
||||||
.replace("{attacker}", &alias_of_uid(attacker_uid))
|
.replace("{attacker}", &alias_of_uid(attacker_uid))
|
||||||
.replace("{victim}", &alias_of_uid(victim)),
|
.replace("{victim}", &alias_of_uid(victim)),
|
||||||
KillSource::Player(attacker_uid, KillType::Buff) => message
|
KillSource::Player(attacker_uid, KillType::Other) => message
|
||||||
.replace("{attacker}", &alias_of_uid(attacker_uid))
|
.replace("{attacker}", &alias_of_uid(attacker_uid))
|
||||||
.replace("{victim}", &alias_of_uid(victim)),
|
.replace("{victim}", &alias_of_uid(victim)),
|
||||||
KillSource::NonPlayer(attacker_name, KillType::Melee) => message
|
KillSource::NonPlayer(attacker_name, KillType::Melee) => message
|
||||||
@ -1795,7 +1795,7 @@ impl Client {
|
|||||||
KillSource::NonPlayer(attacker_name, KillType::Energy) => message
|
KillSource::NonPlayer(attacker_name, KillType::Energy) => message
|
||||||
.replace("{attacker}", attacker_name)
|
.replace("{attacker}", attacker_name)
|
||||||
.replace("{victim}", &alias_of_uid(victim)),
|
.replace("{victim}", &alias_of_uid(victim)),
|
||||||
KillSource::NonPlayer(attacker_name, KillType::Buff) => message
|
KillSource::NonPlayer(attacker_name, KillType::Other) => message
|
||||||
.replace("{attacker}", attacker_name)
|
.replace("{attacker}", attacker_name)
|
||||||
.replace("{victim}", &alias_of_uid(victim)),
|
.replace("{victim}", &alias_of_uid(victim)),
|
||||||
KillSource::Environment(environment) => message
|
KillSource::Environment(environment) => message
|
||||||
|
@ -12,7 +12,7 @@ pub enum GroupTarget {
|
|||||||
OutOfGroup,
|
OutOfGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
pub enum DamageSource {
|
pub enum DamageSource {
|
||||||
Melee,
|
Melee,
|
||||||
Healing,
|
Healing,
|
||||||
@ -21,6 +21,7 @@ pub enum DamageSource {
|
|||||||
Falling,
|
Falling,
|
||||||
Shockwave,
|
Shockwave,
|
||||||
Energy,
|
Energy,
|
||||||
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
@ -50,7 +51,10 @@ impl Damage {
|
|||||||
|
|
||||||
HealthChange {
|
HealthChange {
|
||||||
amount: -damage as i32,
|
amount: -damage as i32,
|
||||||
cause: HealthSource::Attack { by: uid.unwrap() },
|
cause: HealthSource::Damage {
|
||||||
|
kind: self.source,
|
||||||
|
by: uid,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DamageSource::Projectile => {
|
DamageSource::Projectile => {
|
||||||
@ -64,7 +68,10 @@ impl Damage {
|
|||||||
|
|
||||||
HealthChange {
|
HealthChange {
|
||||||
amount: -damage as i32,
|
amount: -damage as i32,
|
||||||
cause: HealthSource::Projectile { owner: uid },
|
cause: HealthSource::Damage {
|
||||||
|
kind: self.source,
|
||||||
|
by: uid,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DamageSource::Explosion => {
|
DamageSource::Explosion => {
|
||||||
@ -74,7 +81,10 @@ impl Damage {
|
|||||||
|
|
||||||
HealthChange {
|
HealthChange {
|
||||||
amount: -damage as i32,
|
amount: -damage as i32,
|
||||||
cause: HealthSource::Explosion { owner: uid },
|
cause: HealthSource::Damage {
|
||||||
|
kind: self.source,
|
||||||
|
by: uid,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DamageSource::Shockwave => {
|
DamageSource::Shockwave => {
|
||||||
@ -84,7 +94,10 @@ impl Damage {
|
|||||||
|
|
||||||
HealthChange {
|
HealthChange {
|
||||||
amount: -damage as i32,
|
amount: -damage as i32,
|
||||||
cause: HealthSource::Attack { by: uid.unwrap() },
|
cause: HealthSource::Damage {
|
||||||
|
kind: self.source,
|
||||||
|
by: uid,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DamageSource::Energy => {
|
DamageSource::Energy => {
|
||||||
@ -94,12 +107,15 @@ impl Damage {
|
|||||||
|
|
||||||
HealthChange {
|
HealthChange {
|
||||||
amount: -damage as i32,
|
amount: -damage as i32,
|
||||||
cause: HealthSource::Energy { owner: uid },
|
cause: HealthSource::Damage {
|
||||||
|
kind: self.source,
|
||||||
|
by: uid,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
DamageSource::Healing => HealthChange {
|
DamageSource::Healing => HealthChange {
|
||||||
amount: damage as i32,
|
amount: damage as i32,
|
||||||
cause: HealthSource::Healing { by: uid },
|
cause: HealthSource::Heal { by: uid },
|
||||||
},
|
},
|
||||||
DamageSource::Falling => {
|
DamageSource::Falling => {
|
||||||
// Armor
|
// Armor
|
||||||
@ -112,6 +128,13 @@ impl Damage {
|
|||||||
cause: HealthSource::World,
|
cause: HealthSource::World,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
DamageSource::Other => HealthChange {
|
||||||
|
amount: -damage as i32,
|
||||||
|
cause: HealthSource::Damage {
|
||||||
|
kind: self.source,
|
||||||
|
by: uid,
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ pub enum KillType {
|
|||||||
Projectile,
|
Projectile,
|
||||||
Explosion,
|
Explosion,
|
||||||
Energy,
|
Energy,
|
||||||
Buff,
|
Other,
|
||||||
// Projectile(String), TODO: add projectile name when available
|
// Projectile(String), TODO: add projectile name when available
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::{comp::Body, sync::Uid};
|
use crate::{comp::Body, sync::Uid, DamageSource};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{Component, FlaggedStorage};
|
use specs::{Component, FlaggedStorage};
|
||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
@ -12,18 +12,20 @@ pub struct HealthChange {
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum HealthSource {
|
pub enum HealthSource {
|
||||||
Attack { by: Uid }, // TODO: Implement weapon
|
Damage { kind: DamageSource, by: Option<Uid> },
|
||||||
Projectile { owner: Option<Uid> },
|
Heal { by: Option<Uid> },
|
||||||
Explosion { owner: Option<Uid> },
|
//Attack { by: Uid }, // TODO: Implement weapon
|
||||||
Energy { owner: Option<Uid> },
|
//Projectile { owner: Option<Uid> },
|
||||||
Buff { owner: Option<Uid> },
|
//Explosion { owner: Option<Uid> },
|
||||||
|
//Energy { owner: Option<Uid> },
|
||||||
|
//Buff { owner: Option<Uid> },
|
||||||
Suicide,
|
Suicide,
|
||||||
World,
|
World,
|
||||||
Revive,
|
Revive,
|
||||||
Command,
|
Command,
|
||||||
LevelUp,
|
LevelUp,
|
||||||
Item,
|
Item,
|
||||||
Healing { by: Option<Uid> },
|
//Healing { by: Option<Uid> },
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -605,11 +605,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
if let Some(my_health) = healths.get(entity) {
|
if let Some(my_health) = healths.get(entity) {
|
||||||
// Only if the attack was recent
|
// Only if the attack was recent
|
||||||
if my_health.last_change.0 < 3.0 {
|
if my_health.last_change.0 < 3.0 {
|
||||||
if let comp::HealthSource::Attack { by }
|
if let comp::HealthSource::Damage { by: Some(by), .. } =
|
||||||
| comp::HealthSource::Projectile { owner: Some(by) }
|
|
||||||
| comp::HealthSource::Energy { owner: Some(by) }
|
|
||||||
| comp::HealthSource::Buff { owner: Some(by) }
|
|
||||||
| comp::HealthSource::Explosion { owner: Some(by) } =
|
|
||||||
my_health.last_change.1.cause
|
my_health.last_change.1.cause
|
||||||
{
|
{
|
||||||
if !agent.activity.is_attack() {
|
if !agent.activity.is_attack() {
|
||||||
@ -660,7 +656,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Attack owner's attacker
|
// Attack owner's attacker
|
||||||
let owner_health = healths.get(owner)?;
|
let owner_health = healths.get(owner)?;
|
||||||
if owner_health.last_change.0 < 5.0 && owner_health.last_change.1.amount < 0 {
|
if owner_health.last_change.0 < 5.0 && owner_health.last_change.1.amount < 0 {
|
||||||
if let comp::HealthSource::Attack { by } = owner_health.last_change.1.cause
|
if let comp::HealthSource::Damage { by: Some(by), .. } =
|
||||||
|
owner_health.last_change.1.cause
|
||||||
{
|
{
|
||||||
if !agent.activity.is_attack() {
|
if !agent.activity.is_attack() {
|
||||||
let attacker = uid_allocator.retrieve_entity_internal(by.id())?;
|
let attacker = uid_allocator.retrieve_entity_internal(by.id())?;
|
||||||
|
@ -178,7 +178,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
amount: (-change.amount as f32
|
amount: (-change.amount as f32
|
||||||
* beam_segment.lifesteal_eff)
|
* beam_segment.lifesteal_eff)
|
||||||
as i32,
|
as i32,
|
||||||
cause: HealthSource::Healing {
|
cause: HealthSource::Heal {
|
||||||
by: beam_segment.owner,
|
by: beam_segment.owner,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -5,6 +5,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
state::DeltaTime,
|
state::DeltaTime,
|
||||||
|
DamageSource,
|
||||||
};
|
};
|
||||||
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
@ -86,9 +87,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
|| buff.time.map_or(false, |dur| dur == Duration::default())
|
|| buff.time.map_or(false, |dur| dur == Duration::default())
|
||||||
{
|
{
|
||||||
let cause = if *accumulated > 0.0 {
|
let cause = if *accumulated > 0.0 {
|
||||||
HealthSource::Healing { by: buff_owner }
|
HealthSource::Heal { by: buff_owner }
|
||||||
} else {
|
} else {
|
||||||
HealthSource::Buff { owner: buff_owner }
|
HealthSource::Damage {
|
||||||
|
kind: DamageSource::Other,
|
||||||
|
by: buff_owner,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
server_emitter.emit(ServerEvent::Damage {
|
server_emitter.emit(ServerEvent::Damage {
|
||||||
entity,
|
entity,
|
||||||
|
@ -19,7 +19,7 @@ use common::{
|
|||||||
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
Explosion, LoadoutBuilder, RadiusEffect,
|
Damage, DamageSource, Explosion, LoadoutBuilder, RadiusEffect,
|
||||||
};
|
};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
|
use specs::{Builder, Entity as EcsEntity, Join, WorldExt};
|
||||||
@ -394,7 +394,10 @@ fn handle_kill(
|
|||||||
let reason = if client == target {
|
let reason = if client == target {
|
||||||
comp::HealthSource::Suicide
|
comp::HealthSource::Suicide
|
||||||
} else if let Some(uid) = server.state.read_storage::<Uid>().get(client) {
|
} else if let Some(uid) = server.state.read_storage::<Uid>().get(client) {
|
||||||
comp::HealthSource::Attack { by: *uid }
|
comp::HealthSource::Damage {
|
||||||
|
kind: DamageSource::Other,
|
||||||
|
by: Some(*uid),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
comp::HealthSource::Command
|
comp::HealthSource::Command
|
||||||
};
|
};
|
||||||
@ -1160,9 +1163,9 @@ fn handle_explosion(
|
|||||||
effects: vec![
|
effects: vec![
|
||||||
RadiusEffect::Entity(
|
RadiusEffect::Entity(
|
||||||
None,
|
None,
|
||||||
Effect::Health(comp::HealthChange {
|
Effect::Damage(Damage {
|
||||||
amount: (-100.0 * power) as i32,
|
source: DamageSource::Explosion,
|
||||||
cause: comp::HealthSource::Explosion { owner: None },
|
value: 100.0 * power,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
RadiusEffect::TerrainDestruction(power),
|
RadiusEffect::TerrainDestruction(power),
|
||||||
|
@ -73,7 +73,10 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
if let Some(_player) = state.ecs().read_storage::<Player>().get(entity) {
|
if let Some(_player) = state.ecs().read_storage::<Player>().get(entity) {
|
||||||
if let Some(uid) = state.ecs().read_storage::<Uid>().get(entity) {
|
if let Some(uid) = state.ecs().read_storage::<Uid>().get(entity) {
|
||||||
let kill_source = match cause {
|
let kill_source = match cause {
|
||||||
HealthSource::Attack { by } => {
|
HealthSource::Damage {
|
||||||
|
kind: DamageSource::Melee,
|
||||||
|
by: Some(by),
|
||||||
|
} => {
|
||||||
// Get attacker entity
|
// Get attacker entity
|
||||||
if let Some(char_entity) = state.ecs().entity_from_uid(by.into()) {
|
if let Some(char_entity) = state.ecs().entity_from_uid(by.into()) {
|
||||||
// Check if attacker is another player or entity with stats (npc)
|
// Check if attacker is another player or entity with stats (npc)
|
||||||
@ -95,7 +98,10 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
KillSource::NonPlayer("<?>".to_string(), KillType::Melee)
|
KillSource::NonPlayer("<?>".to_string(), KillType::Melee)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
HealthSource::Projectile { owner: Some(by) } => {
|
HealthSource::Damage {
|
||||||
|
kind: DamageSource::Projectile,
|
||||||
|
by: Some(by),
|
||||||
|
} => {
|
||||||
// Get projectile owner entity TODO: add names to projectiles and send in
|
// Get projectile owner entity TODO: add names to projectiles and send in
|
||||||
// message
|
// message
|
||||||
if let Some(char_entity) = state.ecs().entity_from_uid(by.into()) {
|
if let Some(char_entity) = state.ecs().entity_from_uid(by.into()) {
|
||||||
@ -118,7 +124,10 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
KillSource::NonPlayer("<?>".to_string(), KillType::Projectile)
|
KillSource::NonPlayer("<?>".to_string(), KillType::Projectile)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
HealthSource::Explosion { owner: Some(by) } => {
|
HealthSource::Damage {
|
||||||
|
kind: DamageSource::Explosion,
|
||||||
|
by: Some(by),
|
||||||
|
} => {
|
||||||
// Get explosion owner entity
|
// Get explosion owner entity
|
||||||
if let Some(char_entity) = state.ecs().entity_from_uid(by.into()) {
|
if let Some(char_entity) = state.ecs().entity_from_uid(by.into()) {
|
||||||
// Check if attacker is another player or entity with stats (npc)
|
// Check if attacker is another player or entity with stats (npc)
|
||||||
@ -140,7 +149,10 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
KillSource::NonPlayer("<?>".to_string(), KillType::Explosion)
|
KillSource::NonPlayer("<?>".to_string(), KillType::Explosion)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
HealthSource::Energy { owner: Some(by) } => {
|
HealthSource::Damage {
|
||||||
|
kind: DamageSource::Energy,
|
||||||
|
by: Some(by),
|
||||||
|
} => {
|
||||||
// Get energy owner entity
|
// Get energy owner entity
|
||||||
if let Some(char_entity) = state.ecs().entity_from_uid(by.into()) {
|
if let Some(char_entity) = state.ecs().entity_from_uid(by.into()) {
|
||||||
// Check if attacker is another player or entity with stats (npc)
|
// Check if attacker is another player or entity with stats (npc)
|
||||||
@ -162,7 +174,10 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
KillSource::NonPlayer("<?>".to_string(), KillType::Energy)
|
KillSource::NonPlayer("<?>".to_string(), KillType::Energy)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
HealthSource::Buff { owner: Some(by) } => {
|
HealthSource::Damage {
|
||||||
|
kind: DamageSource::Other,
|
||||||
|
by: Some(by),
|
||||||
|
} => {
|
||||||
// Get energy owner entity
|
// Get energy owner entity
|
||||||
if let Some(char_entity) = state.ecs().entity_from_uid(by.into()) {
|
if let Some(char_entity) = state.ecs().entity_from_uid(by.into()) {
|
||||||
// Check if attacker is another player or entity with stats (npc)
|
// Check if attacker is another player or entity with stats (npc)
|
||||||
@ -172,29 +187,26 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
.get(char_entity)
|
.get(char_entity)
|
||||||
.is_some()
|
.is_some()
|
||||||
{
|
{
|
||||||
KillSource::Player(by, KillType::Buff)
|
KillSource::Player(by, KillType::Other)
|
||||||
} else if let Some(stats) =
|
} else if let Some(stats) =
|
||||||
state.ecs().read_storage::<Stats>().get(char_entity)
|
state.ecs().read_storage::<Stats>().get(char_entity)
|
||||||
{
|
{
|
||||||
KillSource::NonPlayer(stats.name.clone(), KillType::Buff)
|
KillSource::NonPlayer(stats.name.clone(), KillType::Other)
|
||||||
} else {
|
} else {
|
||||||
KillSource::NonPlayer("<?>".to_string(), KillType::Buff)
|
KillSource::NonPlayer("<?>".to_string(), KillType::Other)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
KillSource::NonPlayer("<?>".to_string(), KillType::Buff)
|
KillSource::NonPlayer("<?>".to_string(), KillType::Other)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
HealthSource::World => KillSource::FallDamage,
|
HealthSource::World => KillSource::FallDamage,
|
||||||
HealthSource::Suicide => KillSource::Suicide,
|
HealthSource::Suicide => KillSource::Suicide,
|
||||||
HealthSource::Projectile { owner: None }
|
HealthSource::Damage { .. }
|
||||||
| HealthSource::Explosion { owner: None }
|
|
||||||
| HealthSource::Energy { owner: None }
|
|
||||||
| HealthSource::Buff { owner: None }
|
|
||||||
| HealthSource::Revive
|
| HealthSource::Revive
|
||||||
| HealthSource::Command
|
| HealthSource::Command
|
||||||
| HealthSource::LevelUp
|
| HealthSource::LevelUp
|
||||||
| HealthSource::Item
|
| HealthSource::Item
|
||||||
| HealthSource::Healing { by: _ }
|
| HealthSource::Heal { by: _ }
|
||||||
| HealthSource::Unknown => KillSource::Other,
|
| HealthSource::Unknown => KillSource::Other,
|
||||||
};
|
};
|
||||||
state
|
state
|
||||||
@ -205,12 +217,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
|
|||||||
// Give EXP to the killer if entity had stats
|
// Give EXP to the killer if entity had stats
|
||||||
(|| {
|
(|| {
|
||||||
let mut stats = state.ecs().write_storage::<Stats>();
|
let mut stats = state.ecs().write_storage::<Stats>();
|
||||||
let by = if let HealthSource::Attack { by }
|
let by = if let HealthSource::Damage { by: Some(by), .. } = cause {
|
||||||
| HealthSource::Projectile { owner: Some(by) }
|
|
||||||
| HealthSource::Energy { owner: Some(by) }
|
|
||||||
| HealthSource::Buff { owner: Some(by) }
|
|
||||||
| HealthSource::Explosion { owner: Some(by) } = cause
|
|
||||||
{
|
|
||||||
by
|
by
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
@ -516,7 +523,7 @@ pub fn handle_explosion(
|
|||||||
// Uses radius as outcome power, makes negative if explosion has healing effect
|
// Uses radius as outcome power, makes negative if explosion has healing effect
|
||||||
let outcome_power = explosion.radius
|
let outcome_power = explosion.radius
|
||||||
* if explosion.effects.iter().any(
|
* if explosion.effects.iter().any(
|
||||||
|e| matches!(e, RadiusEffect::Entity(_, e) if matches!(e, Effect::Damage(d) if matches!(d.source, DamageSource::Healing))),
|
|e| matches!(e, RadiusEffect::Entity(_, Effect::Damage(Damage { source: DamageSource::Healing, .. })))
|
||||||
) {
|
) {
|
||||||
-1.0
|
-1.0
|
||||||
} else {
|
} else {
|
||||||
@ -530,7 +537,7 @@ pub fn handle_explosion(
|
|||||||
is_attack: explosion
|
is_attack: explosion
|
||||||
.effects
|
.effects
|
||||||
.iter()
|
.iter()
|
||||||
.any(|e| matches!(e, RadiusEffect::Entity(_, e) if matches!(e, Effect::Damage(_)))),
|
.any(|e| matches!(e, RadiusEffect::Entity(_, Effect::Damage(_)))),
|
||||||
reagent,
|
reagent,
|
||||||
});
|
});
|
||||||
let owner_entity = owner.and_then(|uid| {
|
let owner_entity = owner.and_then(|uid| {
|
||||||
|
@ -86,14 +86,12 @@ impl StateExt for State {
|
|||||||
.map(|stats| stats.exp.change_by(xp));
|
.map(|stats| stats.exp.change_by(xp));
|
||||||
},
|
},
|
||||||
Effect::Damage(damage) => {
|
Effect::Damage(damage) => {
|
||||||
#[allow(irrefutable_let_patterns)]
|
let loadouts = self.ecs().read_storage::<comp::Loadout>();
|
||||||
if let loadout = self.ecs().read_storage::<comp::Loadout>().get(entity) {
|
let change = damage.modify_damage(loadouts.get(entity), source);
|
||||||
let change = damage.modify_damage(loadout, source);
|
self.ecs()
|
||||||
self.ecs()
|
.write_storage::<comp::Health>()
|
||||||
.write_storage::<comp::Health>()
|
.get_mut(entity)
|
||||||
.get_mut(entity)
|
.map(|health| health.change_by(change));
|
||||||
.map(|health| health.change_by(change));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,743 +0,0 @@
|
|||||||
use super::SysTimer;
|
|
||||||
use crate::{
|
|
||||||
alias_validator::AliasValidator,
|
|
||||||
character_creator,
|
|
||||||
client::Client,
|
|
||||||
login_provider::LoginProvider,
|
|
||||||
metrics::{NetworkRequestMetrics, PlayerMetrics},
|
|
||||||
persistence::character_loader::CharacterLoader,
|
|
||||||
EditableSettings, Settings,
|
|
||||||
};
|
|
||||||
use common::{
|
|
||||||
comp::{
|
|
||||||
Admin, CanBuild, ChatMode, ChatType, ControlEvent, Controller, ForceUpdate, Health, Ori,
|
|
||||||
Player, Pos, Stats, UnresolvedChatMsg, Vel,
|
|
||||||
},
|
|
||||||
event::{EventBus, ServerEvent},
|
|
||||||
msg::{
|
|
||||||
validate_chat_msg, CharacterInfo, ChatMsgValidationError, ClientGeneral, ClientInGame,
|
|
||||||
ClientRegister, DisconnectReason, PingMsg, PlayerInfo, PlayerListUpdate, RegisterError,
|
|
||||||
ServerGeneral, ServerRegisterAnswer, MAX_BYTES_CHAT_MSG,
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
state::{BlockChange, Time},
|
|
||||||
sync::Uid,
|
|
||||||
terrain::{TerrainChunkSize, TerrainGrid},
|
|
||||||
vol::{ReadVol, RectVolSize},
|
|
||||||
};
|
|
||||||
use futures_executor::block_on;
|
|
||||||
use futures_timer::Delay;
|
|
||||||
use futures_util::{select, FutureExt};
|
|
||||||
use hashbrown::HashMap;
|
|
||||||
use specs::{
|
|
||||||
Entities, Join, Read, ReadExpect, ReadStorage, System, Write, WriteExpect, WriteStorage,
|
|
||||||
};
|
|
||||||
use tracing::{debug, error, info, trace, warn};
|
|
||||||
|
|
||||||
impl Sys {
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn handle_client_msg(
|
|
||||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
|
||||||
new_chat_msgs: &mut Vec<(Option<specs::Entity>, UnresolvedChatMsg)>,
|
|
||||||
entity: specs::Entity,
|
|
||||||
client: &mut Client,
|
|
||||||
player_metrics: &ReadExpect<'_, PlayerMetrics>,
|
|
||||||
uids: &ReadStorage<'_, Uid>,
|
|
||||||
chat_modes: &ReadStorage<'_, ChatMode>,
|
|
||||||
msg: ClientGeneral,
|
|
||||||
) -> Result<(), crate::error::Error> {
|
|
||||||
match msg {
|
|
||||||
ClientGeneral::ChatMsg(message) => {
|
|
||||||
if client.registered {
|
|
||||||
match validate_chat_msg(&message) {
|
|
||||||
Ok(()) => {
|
|
||||||
if let Some(from) = uids.get(entity) {
|
|
||||||
let mode = chat_modes.get(entity).cloned().unwrap_or_default();
|
|
||||||
let msg = mode.new_message(*from, message);
|
|
||||||
new_chat_msgs.push((Some(entity), msg));
|
|
||||||
} else {
|
|
||||||
error!("Could not send message. Missing player uid");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(ChatMsgValidationError::TooLong) => {
|
|
||||||
let max = MAX_BYTES_CHAT_MSG;
|
|
||||||
let len = message.len();
|
|
||||||
warn!(?len, ?max, "Received a chat message that's too long")
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ClientGeneral::Disconnect => {
|
|
||||||
client.send_msg(ServerGeneral::Disconnect(DisconnectReason::Requested));
|
|
||||||
},
|
|
||||||
ClientGeneral::Terminate => {
|
|
||||||
debug!(?entity, "Client send message to termitate session");
|
|
||||||
player_metrics
|
|
||||||
.clients_disconnected
|
|
||||||
.with_label_values(&["gracefully"])
|
|
||||||
.inc();
|
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
|
|
||||||
},
|
|
||||||
_ => unreachable!("not a client_general msg"),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn handle_client_in_game_msg(
|
|
||||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
|
||||||
entity: specs::Entity,
|
|
||||||
client: &mut Client,
|
|
||||||
terrain: &ReadExpect<'_, TerrainGrid>,
|
|
||||||
network_metrics: &ReadExpect<'_, NetworkRequestMetrics>,
|
|
||||||
can_build: &ReadStorage<'_, CanBuild>,
|
|
||||||
force_updates: &ReadStorage<'_, ForceUpdate>,
|
|
||||||
stats: &mut WriteStorage<'_, Stats>,
|
|
||||||
healths: &mut WriteStorage<'_, Health>,
|
|
||||||
block_changes: &mut Write<'_, BlockChange>,
|
|
||||||
positions: &mut WriteStorage<'_, Pos>,
|
|
||||||
velocities: &mut WriteStorage<'_, Vel>,
|
|
||||||
orientations: &mut WriteStorage<'_, Ori>,
|
|
||||||
players: &mut WriteStorage<'_, Player>,
|
|
||||||
controllers: &mut WriteStorage<'_, Controller>,
|
|
||||||
settings: &Read<'_, Settings>,
|
|
||||||
msg: ClientGeneral,
|
|
||||||
) -> Result<(), crate::error::Error> {
|
|
||||||
if client.in_game.is_none() {
|
|
||||||
debug!(?entity, "client is not in_game, ignoring msg");
|
|
||||||
trace!(?msg, "ignored msg content");
|
|
||||||
if matches!(msg, ClientGeneral::TerrainChunkRequest{ .. }) {
|
|
||||||
network_metrics.chunks_request_dropped.inc();
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
match msg {
|
|
||||||
// Go back to registered state (char selection screen)
|
|
||||||
ClientGeneral::ExitInGame => {
|
|
||||||
client.in_game = None;
|
|
||||||
server_emitter.emit(ServerEvent::ExitIngame { entity });
|
|
||||||
client.send_msg(ServerGeneral::ExitInGameSuccess);
|
|
||||||
},
|
|
||||||
ClientGeneral::SetViewDistance(view_distance) => {
|
|
||||||
players.get_mut(entity).map(|player| {
|
|
||||||
player.view_distance = Some(
|
|
||||||
settings
|
|
||||||
.max_view_distance
|
|
||||||
.map(|max| view_distance.min(max))
|
|
||||||
.unwrap_or(view_distance),
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
//correct client if its VD is to high
|
|
||||||
if settings
|
|
||||||
.max_view_distance
|
|
||||||
.map(|max| view_distance > max)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
client.send_msg(ServerGeneral::SetViewDistance(
|
|
||||||
settings.max_view_distance.unwrap_or(0),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ClientGeneral::ControllerInputs(inputs) => {
|
|
||||||
if let Some(ClientInGame::Character) = client.in_game {
|
|
||||||
if let Some(controller) = controllers.get_mut(entity) {
|
|
||||||
controller.inputs.update_with_new(inputs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ClientGeneral::ControlEvent(event) => {
|
|
||||||
if let Some(ClientInGame::Character) = client.in_game {
|
|
||||||
// Skip respawn if client entity is alive
|
|
||||||
if let ControlEvent::Respawn = event {
|
|
||||||
if healths.get(entity).map_or(true, |h| !h.is_dead) {
|
|
||||||
//Todo: comment why return!
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(controller) = controllers.get_mut(entity) {
|
|
||||||
controller.events.push(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ClientGeneral::ControlAction(event) => {
|
|
||||||
if let Some(ClientInGame::Character) = client.in_game {
|
|
||||||
if let Some(controller) = controllers.get_mut(entity) {
|
|
||||||
controller.actions.push(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ClientGeneral::PlayerPhysics { pos, vel, ori } => {
|
|
||||||
if let Some(ClientInGame::Character) = client.in_game {
|
|
||||||
if force_updates.get(entity).is_none()
|
|
||||||
&& healths.get(entity).map_or(true, |h| !h.is_dead)
|
|
||||||
{
|
|
||||||
let _ = positions.insert(entity, pos);
|
|
||||||
let _ = velocities.insert(entity, vel);
|
|
||||||
let _ = orientations.insert(entity, ori);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ClientGeneral::BreakBlock(pos) => {
|
|
||||||
if let Some(block) = can_build.get(entity).and_then(|_| terrain.get(pos).ok()) {
|
|
||||||
block_changes.set(pos, block.into_vacant());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ClientGeneral::PlaceBlock(pos, block) => {
|
|
||||||
if can_build.get(entity).is_some() {
|
|
||||||
block_changes.try_set(pos, block);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ClientGeneral::TerrainChunkRequest { key } => {
|
|
||||||
let in_vd = if let (Some(view_distance), Some(pos)) = (
|
|
||||||
players.get(entity).and_then(|p| p.view_distance),
|
|
||||||
positions.get(entity),
|
|
||||||
) {
|
|
||||||
pos.0.xy().map(|e| e as f64).distance(
|
|
||||||
key.map(|e| e as f64 + 0.5) * TerrainChunkSize::RECT_SIZE.map(|e| e as f64),
|
|
||||||
) < (view_distance as f64 - 1.0 + 2.5 * 2.0_f64.sqrt())
|
|
||||||
* TerrainChunkSize::RECT_SIZE.x as f64
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
};
|
|
||||||
if in_vd {
|
|
||||||
match terrain.get_key(key) {
|
|
||||||
Some(chunk) => {
|
|
||||||
network_metrics.chunks_served_from_memory.inc();
|
|
||||||
client.send_msg(ServerGeneral::TerrainChunkUpdate {
|
|
||||||
key,
|
|
||||||
chunk: Ok(Box::new(chunk.clone())),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
network_metrics.chunks_generation_triggered.inc();
|
|
||||||
server_emitter.emit(ServerEvent::ChunkRequest(entity, key))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
network_metrics.chunks_request_dropped.inc();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ClientGeneral::UnlockSkill(skill) => {
|
|
||||||
stats
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|s| s.skill_set.unlock_skill(skill));
|
|
||||||
},
|
|
||||||
ClientGeneral::RefundSkill(skill) => {
|
|
||||||
stats
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|s| s.skill_set.refund_skill(skill));
|
|
||||||
},
|
|
||||||
ClientGeneral::UnlockSkillGroup(skill_group_type) => {
|
|
||||||
stats
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|s| s.skill_set.unlock_skill_group(skill_group_type));
|
|
||||||
},
|
|
||||||
_ => unreachable!("not a client_in_game msg"),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn handle_client_character_screen_msg(
|
|
||||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
|
||||||
new_chat_msgs: &mut Vec<(Option<specs::Entity>, UnresolvedChatMsg)>,
|
|
||||||
entity: specs::Entity,
|
|
||||||
client: &mut Client,
|
|
||||||
character_loader: &ReadExpect<'_, CharacterLoader>,
|
|
||||||
uids: &ReadStorage<'_, Uid>,
|
|
||||||
players: &mut WriteStorage<'_, Player>,
|
|
||||||
editable_settings: &ReadExpect<'_, EditableSettings>,
|
|
||||||
alias_validator: &ReadExpect<'_, AliasValidator>,
|
|
||||||
msg: ClientGeneral,
|
|
||||||
) -> Result<(), crate::error::Error> {
|
|
||||||
match msg {
|
|
||||||
// Request spectator state
|
|
||||||
ClientGeneral::Spectate if client.registered => {
|
|
||||||
client.in_game = Some(ClientInGame::Spectator)
|
|
||||||
},
|
|
||||||
ClientGeneral::Spectate => debug!("dropped Spectate msg from unregistered client"),
|
|
||||||
ClientGeneral::Character(character_id)
|
|
||||||
if client.registered && client.in_game.is_none() =>
|
|
||||||
{
|
|
||||||
if let Some(player) = players.get(entity) {
|
|
||||||
// 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
|
|
||||||
character_loader.load_character_data(
|
|
||||||
entity,
|
|
||||||
player.uuid().to_string(),
|
|
||||||
character_id,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Start inserting non-persisted/default components for the entity
|
|
||||||
// while we load the DB data
|
|
||||||
server_emitter.emit(ServerEvent::InitCharacterData {
|
|
||||||
entity,
|
|
||||||
character_id,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Give the player a welcome message
|
|
||||||
if !editable_settings.server_description.is_empty() {
|
|
||||||
client.send_msg(
|
|
||||||
ChatType::CommandInfo
|
|
||||||
.server_msg(String::from(&*editable_settings.server_description)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !client.login_msg_sent {
|
|
||||||
if let Some(player_uid) = uids.get(entity) {
|
|
||||||
new_chat_msgs.push((None, UnresolvedChatMsg {
|
|
||||||
chat_type: ChatType::Online(*player_uid),
|
|
||||||
message: "".to_string(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
client.login_msg_sent = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
client.send_msg(ServerGeneral::CharacterDataLoadError(String::from(
|
|
||||||
"Failed to fetch player entity",
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClientGeneral::Character(_) => {
|
|
||||||
let registered = client.registered;
|
|
||||||
let in_game = client.in_game;
|
|
||||||
debug!(?registered, ?in_game, "dropped Character msg from client");
|
|
||||||
},
|
|
||||||
ClientGeneral::RequestCharacterList => {
|
|
||||||
if let Some(player) = players.get(entity) {
|
|
||||||
character_loader.load_character_list(entity, player.uuid().to_string())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ClientGeneral::CreateCharacter { alias, tool, body } => {
|
|
||||||
if let Err(error) = alias_validator.validate(&alias) {
|
|
||||||
debug!(?error, ?alias, "denied alias as it contained a banned word");
|
|
||||||
client.send_msg(ServerGeneral::CharacterActionError(error.to_string()));
|
|
||||||
} else if let Some(player) = players.get(entity) {
|
|
||||||
character_creator::create_character(
|
|
||||||
entity,
|
|
||||||
player.uuid().to_string(),
|
|
||||||
alias,
|
|
||||||
tool,
|
|
||||||
body,
|
|
||||||
character_loader,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ClientGeneral::DeleteCharacter(character_id) => {
|
|
||||||
if let Some(player) = players.get(entity) {
|
|
||||||
character_loader.delete_character(
|
|
||||||
entity,
|
|
||||||
player.uuid().to_string(),
|
|
||||||
character_id,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => unreachable!("not a client_character_screen msg"),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn handle_ping_msg(client: &mut Client, msg: PingMsg) -> Result<(), crate::error::Error> {
|
|
||||||
match msg {
|
|
||||||
PingMsg::Ping => client.send_msg(PingMsg::Pong),
|
|
||||||
PingMsg::Pong => {},
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn handle_register_msg(
|
|
||||||
player_list: &HashMap<Uid, PlayerInfo>,
|
|
||||||
new_players: &mut Vec<specs::Entity>,
|
|
||||||
entity: specs::Entity,
|
|
||||||
client: &mut Client,
|
|
||||||
player_metrics: &ReadExpect<'_, PlayerMetrics>,
|
|
||||||
login_provider: &mut WriteExpect<'_, LoginProvider>,
|
|
||||||
admins: &mut WriteStorage<'_, Admin>,
|
|
||||||
players: &mut WriteStorage<'_, Player>,
|
|
||||||
editable_settings: &ReadExpect<'_, EditableSettings>,
|
|
||||||
msg: ClientRegister,
|
|
||||||
) -> Result<(), crate::error::Error> {
|
|
||||||
let (username, uuid) = match login_provider.try_login(
|
|
||||||
&msg.token_or_username,
|
|
||||||
&*editable_settings.admins,
|
|
||||||
&*editable_settings.whitelist,
|
|
||||||
&*editable_settings.banlist,
|
|
||||||
) {
|
|
||||||
Err(err) => {
|
|
||||||
client
|
|
||||||
.register_stream
|
|
||||||
.send(ServerRegisterAnswer::Err(err))?;
|
|
||||||
return Ok(());
|
|
||||||
},
|
|
||||||
Ok((username, uuid)) => (username, uuid),
|
|
||||||
};
|
|
||||||
|
|
||||||
const INITIAL_VD: Option<u32> = Some(5); //will be changed after login
|
|
||||||
let player = Player::new(username, None, INITIAL_VD, uuid);
|
|
||||||
let is_admin = editable_settings.admins.contains(&uuid);
|
|
||||||
|
|
||||||
if !player.is_valid() {
|
|
||||||
// Invalid player
|
|
||||||
client
|
|
||||||
.register_stream
|
|
||||||
.send(ServerRegisterAnswer::Err(RegisterError::InvalidCharacter))?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
if !client.registered && client.in_game.is_none() {
|
|
||||||
// Add Player component to this client
|
|
||||||
let _ = players.insert(entity, player);
|
|
||||||
player_metrics.players_connected.inc();
|
|
||||||
|
|
||||||
// Give the Admin component to the player if their name exists in
|
|
||||||
// admin list
|
|
||||||
if is_admin {
|
|
||||||
let _ = admins.insert(entity, Admin);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell the client its request was successful.
|
|
||||||
client.registered = true;
|
|
||||||
client.register_stream.send(ServerRegisterAnswer::Ok(()))?;
|
|
||||||
|
|
||||||
// Send initial player list
|
|
||||||
client.send_msg(ServerGeneral::PlayerListUpdate(PlayerListUpdate::Init(
|
|
||||||
player_list.clone(),
|
|
||||||
)));
|
|
||||||
|
|
||||||
// Add to list to notify all clients of the new player
|
|
||||||
new_players.push(entity);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
///We needed to move this to a async fn, if we would use a async closures
|
|
||||||
/// the compiler generates to much recursion and fails to compile this
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
async fn handle_messages(
|
|
||||||
server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
|
|
||||||
new_chat_msgs: &mut Vec<(Option<specs::Entity>, UnresolvedChatMsg)>,
|
|
||||||
player_list: &HashMap<Uid, PlayerInfo>,
|
|
||||||
new_players: &mut Vec<specs::Entity>,
|
|
||||||
entity: specs::Entity,
|
|
||||||
client: &mut Client,
|
|
||||||
cnt: &mut u64,
|
|
||||||
character_loader: &ReadExpect<'_, CharacterLoader>,
|
|
||||||
terrain: &ReadExpect<'_, TerrainGrid>,
|
|
||||||
network_metrics: &ReadExpect<'_, NetworkRequestMetrics>,
|
|
||||||
player_metrics: &ReadExpect<'_, PlayerMetrics>,
|
|
||||||
uids: &ReadStorage<'_, Uid>,
|
|
||||||
can_build: &ReadStorage<'_, CanBuild>,
|
|
||||||
force_updates: &ReadStorage<'_, ForceUpdate>,
|
|
||||||
stats: &mut WriteStorage<'_, Stats>,
|
|
||||||
healths: &mut WriteStorage<'_, Health>,
|
|
||||||
chat_modes: &ReadStorage<'_, ChatMode>,
|
|
||||||
login_provider: &mut WriteExpect<'_, LoginProvider>,
|
|
||||||
block_changes: &mut Write<'_, BlockChange>,
|
|
||||||
admins: &mut WriteStorage<'_, Admin>,
|
|
||||||
positions: &mut WriteStorage<'_, Pos>,
|
|
||||||
velocities: &mut WriteStorage<'_, Vel>,
|
|
||||||
orientations: &mut WriteStorage<'_, Ori>,
|
|
||||||
players: &mut WriteStorage<'_, Player>,
|
|
||||||
controllers: &mut WriteStorage<'_, Controller>,
|
|
||||||
settings: &Read<'_, Settings>,
|
|
||||||
editable_settings: &ReadExpect<'_, EditableSettings>,
|
|
||||||
alias_validator: &ReadExpect<'_, AliasValidator>,
|
|
||||||
) -> Result<(), crate::error::Error> {
|
|
||||||
let (mut b1, mut b2, mut b3, mut b4, mut b5) = (
|
|
||||||
client.network_error,
|
|
||||||
client.network_error,
|
|
||||||
client.network_error,
|
|
||||||
client.network_error,
|
|
||||||
client.network_error,
|
|
||||||
);
|
|
||||||
loop {
|
|
||||||
/*
|
|
||||||
waiting for 1 of the 5 streams to return a massage asynchronous.
|
|
||||||
If so, handle that msg type. This code will be refactored soon
|
|
||||||
*/
|
|
||||||
|
|
||||||
let q1 = Client::internal_recv(&mut b1, &mut client.general_stream);
|
|
||||||
let q2 = Client::internal_recv(&mut b2, &mut client.in_game_stream);
|
|
||||||
let q3 = Client::internal_recv(&mut b3, &mut client.character_screen_stream);
|
|
||||||
let q4 = Client::internal_recv(&mut b4, &mut client.ping_stream);
|
|
||||||
let q5 = Client::internal_recv(&mut b5, &mut client.register_stream);
|
|
||||||
|
|
||||||
let (m1, m2, m3, m4, m5) = select!(
|
|
||||||
msg = q1.fuse() => (Some(msg), None, None, None, None),
|
|
||||||
msg = q2.fuse() => (None, Some(msg), None, None, None),
|
|
||||||
msg = q3.fuse() => (None, None, Some(msg), None, None),
|
|
||||||
msg = q4.fuse() => (None, None, None, Some(msg), None),
|
|
||||||
msg = q5.fuse() => (None, None, None, None,Some(msg)),
|
|
||||||
);
|
|
||||||
*cnt += 1;
|
|
||||||
if let Some(msg) = m1 {
|
|
||||||
client.network_error |= b1;
|
|
||||||
Self::handle_client_msg(
|
|
||||||
server_emitter,
|
|
||||||
new_chat_msgs,
|
|
||||||
entity,
|
|
||||||
client,
|
|
||||||
player_metrics,
|
|
||||||
uids,
|
|
||||||
chat_modes,
|
|
||||||
msg?,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
if let Some(msg) = m2 {
|
|
||||||
client.network_error |= b2;
|
|
||||||
Self::handle_client_in_game_msg(
|
|
||||||
server_emitter,
|
|
||||||
entity,
|
|
||||||
client,
|
|
||||||
terrain,
|
|
||||||
network_metrics,
|
|
||||||
can_build,
|
|
||||||
force_updates,
|
|
||||||
stats,
|
|
||||||
healths,
|
|
||||||
block_changes,
|
|
||||||
positions,
|
|
||||||
velocities,
|
|
||||||
orientations,
|
|
||||||
players,
|
|
||||||
controllers,
|
|
||||||
settings,
|
|
||||||
msg?,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
if let Some(msg) = m3 {
|
|
||||||
client.network_error |= b3;
|
|
||||||
Self::handle_client_character_screen_msg(
|
|
||||||
server_emitter,
|
|
||||||
new_chat_msgs,
|
|
||||||
entity,
|
|
||||||
client,
|
|
||||||
character_loader,
|
|
||||||
uids,
|
|
||||||
players,
|
|
||||||
editable_settings,
|
|
||||||
alias_validator,
|
|
||||||
msg?,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
if let Some(msg) = m4 {
|
|
||||||
client.network_error |= b4;
|
|
||||||
Self::handle_ping_msg(client, msg?)?;
|
|
||||||
}
|
|
||||||
if let Some(msg) = m5 {
|
|
||||||
client.network_error |= b5;
|
|
||||||
Self::handle_register_msg(
|
|
||||||
player_list,
|
|
||||||
new_players,
|
|
||||||
entity,
|
|
||||||
client,
|
|
||||||
player_metrics,
|
|
||||||
login_provider,
|
|
||||||
admins,
|
|
||||||
players,
|
|
||||||
editable_settings,
|
|
||||||
msg?,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This system will handle new messages from clients
|
|
||||||
pub struct Sys;
|
|
||||||
impl<'a> System<'a> for Sys {
|
|
||||||
#[allow(clippy::type_complexity)] // TODO: Pending review in #587
|
|
||||||
type SystemData = (
|
|
||||||
Entities<'a>,
|
|
||||||
Read<'a, EventBus<ServerEvent>>,
|
|
||||||
Read<'a, Time>,
|
|
||||||
ReadExpect<'a, CharacterLoader>,
|
|
||||||
ReadExpect<'a, TerrainGrid>,
|
|
||||||
ReadExpect<'a, NetworkRequestMetrics>,
|
|
||||||
ReadExpect<'a, PlayerMetrics>,
|
|
||||||
Write<'a, SysTimer<Self>>,
|
|
||||||
ReadStorage<'a, Uid>,
|
|
||||||
ReadStorage<'a, CanBuild>,
|
|
||||||
ReadStorage<'a, ForceUpdate>,
|
|
||||||
WriteStorage<'a, Stats>,
|
|
||||||
WriteStorage<'a, Health>,
|
|
||||||
ReadStorage<'a, ChatMode>,
|
|
||||||
WriteExpect<'a, LoginProvider>,
|
|
||||||
Write<'a, BlockChange>,
|
|
||||||
WriteStorage<'a, Admin>,
|
|
||||||
WriteStorage<'a, Pos>,
|
|
||||||
WriteStorage<'a, Vel>,
|
|
||||||
WriteStorage<'a, Ori>,
|
|
||||||
WriteStorage<'a, Player>,
|
|
||||||
WriteStorage<'a, Client>,
|
|
||||||
WriteStorage<'a, Controller>,
|
|
||||||
Read<'a, Settings>,
|
|
||||||
ReadExpect<'a, EditableSettings>,
|
|
||||||
ReadExpect<'a, AliasValidator>,
|
|
||||||
);
|
|
||||||
|
|
||||||
#[allow(clippy::match_ref_pats)] // TODO: Pending review in #587
|
|
||||||
#[allow(clippy::single_char_pattern)] // TODO: Pending review in #587
|
|
||||||
#[allow(clippy::single_match)] // TODO: Pending review in #587
|
|
||||||
fn run(
|
|
||||||
&mut self,
|
|
||||||
(
|
|
||||||
entities,
|
|
||||||
server_event_bus,
|
|
||||||
time,
|
|
||||||
character_loader,
|
|
||||||
terrain,
|
|
||||||
network_metrics,
|
|
||||||
player_metrics,
|
|
||||||
mut timer,
|
|
||||||
uids,
|
|
||||||
can_build,
|
|
||||||
force_updates,
|
|
||||||
mut stats,
|
|
||||||
mut healths,
|
|
||||||
chat_modes,
|
|
||||||
mut accounts,
|
|
||||||
mut block_changes,
|
|
||||||
mut admins,
|
|
||||||
mut positions,
|
|
||||||
mut velocities,
|
|
||||||
mut orientations,
|
|
||||||
mut players,
|
|
||||||
mut clients,
|
|
||||||
mut controllers,
|
|
||||||
settings,
|
|
||||||
editable_settings,
|
|
||||||
alias_validator,
|
|
||||||
): Self::SystemData,
|
|
||||||
) {
|
|
||||||
span!(_guard, "run", "message::Sys::run");
|
|
||||||
timer.start();
|
|
||||||
|
|
||||||
let mut server_emitter = server_event_bus.emitter();
|
|
||||||
|
|
||||||
let mut new_chat_msgs = Vec::new();
|
|
||||||
|
|
||||||
// Player list to send new players.
|
|
||||||
let player_list = (&uids, &players, stats.maybe(), admins.maybe())
|
|
||||||
.join()
|
|
||||||
.map(|(uid, player, stats, admin)| {
|
|
||||||
(*uid, PlayerInfo {
|
|
||||||
is_online: true,
|
|
||||||
is_admin: admin.is_some(),
|
|
||||||
player_alias: player.alias.clone(),
|
|
||||||
character: stats.map(|stats| CharacterInfo {
|
|
||||||
name: stats.name.clone(),
|
|
||||||
level: stats.level.level(),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<HashMap<_, _>>();
|
|
||||||
// List of new players to update player lists of all clients.
|
|
||||||
let mut new_players = Vec::new();
|
|
||||||
|
|
||||||
for (entity, client) in (&entities, &mut clients).join() {
|
|
||||||
let mut cnt = 0;
|
|
||||||
|
|
||||||
let network_err: Result<(), crate::error::Error> = block_on(async {
|
|
||||||
//TIMEOUT 0.02 ms for msg handling
|
|
||||||
let work_future = Self::handle_messages(
|
|
||||||
&mut server_emitter,
|
|
||||||
&mut new_chat_msgs,
|
|
||||||
&player_list,
|
|
||||||
&mut new_players,
|
|
||||||
entity,
|
|
||||||
client,
|
|
||||||
&mut cnt,
|
|
||||||
&character_loader,
|
|
||||||
&terrain,
|
|
||||||
&network_metrics,
|
|
||||||
&player_metrics,
|
|
||||||
&uids,
|
|
||||||
&can_build,
|
|
||||||
&force_updates,
|
|
||||||
&mut stats,
|
|
||||||
&mut healths,
|
|
||||||
&chat_modes,
|
|
||||||
&mut accounts,
|
|
||||||
&mut block_changes,
|
|
||||||
&mut admins,
|
|
||||||
&mut positions,
|
|
||||||
&mut velocities,
|
|
||||||
&mut orientations,
|
|
||||||
&mut players,
|
|
||||||
&mut controllers,
|
|
||||||
&settings,
|
|
||||||
&editable_settings,
|
|
||||||
&alias_validator,
|
|
||||||
);
|
|
||||||
select!(
|
|
||||||
_ = Delay::new(std::time::Duration::from_micros(20)).fuse() => Ok(()),
|
|
||||||
err = work_future.fuse() => err,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
// Network error
|
|
||||||
if network_err.is_err() {
|
|
||||||
debug!(?entity, "postbox error with client, disconnecting");
|
|
||||||
player_metrics
|
|
||||||
.clients_disconnected
|
|
||||||
.with_label_values(&["network_error"])
|
|
||||||
.inc();
|
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
|
|
||||||
} else if cnt > 0 {
|
|
||||||
// Update client ping.
|
|
||||||
client.last_ping = time.0
|
|
||||||
} else if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64
|
|
||||||
// Timeout
|
|
||||||
{
|
|
||||||
info!(?entity, "timeout error with client, disconnecting");
|
|
||||||
player_metrics
|
|
||||||
.clients_disconnected
|
|
||||||
.with_label_values(&["timeout"])
|
|
||||||
.inc();
|
|
||||||
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
|
|
||||||
} else if time.0 - client.last_ping > settings.client_timeout.as_secs() as f64 * 0.5 {
|
|
||||||
// Try pinging the client if the timeout is nearing.
|
|
||||||
client.send_msg(PingMsg::Ping);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle new players.
|
|
||||||
// Tell all clients to add them to the player list.
|
|
||||||
for entity in new_players {
|
|
||||||
if let (Some(uid), Some(player)) = (uids.get(entity), players.get(entity)) {
|
|
||||||
let msg =
|
|
||||||
ServerGeneral::PlayerListUpdate(PlayerListUpdate::Add(*uid, PlayerInfo {
|
|
||||||
player_alias: player.alias.clone(),
|
|
||||||
is_online: true,
|
|
||||||
is_admin: admins.get(entity).is_some(),
|
|
||||||
character: None, // new players will be on character select.
|
|
||||||
}));
|
|
||||||
for client in (&mut clients).join().filter(|c| c.registered) {
|
|
||||||
client.send_msg(msg.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle new chat messages.
|
|
||||||
for (entity, msg) in new_chat_msgs {
|
|
||||||
// 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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
timer.end()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,10 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{HealthChange, HealthSource, Object, PhysicsState, Pos, Vel},
|
comp::{HealthSource, Object, PhysicsState, Pos, Vel},
|
||||||
effect::Effect,
|
effect::Effect,
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
span,
|
span,
|
||||||
state::DeltaTime,
|
state::DeltaTime,
|
||||||
Explosion, RadiusEffect,
|
Damage, DamageSource, Explosion, RadiusEffect,
|
||||||
};
|
};
|
||||||
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
|
||||||
|
|
||||||
@ -52,9 +52,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
effects: vec![
|
effects: vec![
|
||||||
RadiusEffect::Entity(
|
RadiusEffect::Entity(
|
||||||
None,
|
None,
|
||||||
Effect::Health(HealthChange {
|
Effect::Damage(Damage {
|
||||||
amount: -500,
|
source: DamageSource::Explosion,
|
||||||
cause: HealthSource::Explosion { owner: *owner },
|
value: 500.0,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
RadiusEffect::TerrainDestruction(4.0),
|
RadiusEffect::TerrainDestruction(4.0),
|
||||||
@ -79,9 +79,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
effects: vec![
|
effects: vec![
|
||||||
RadiusEffect::Entity(
|
RadiusEffect::Entity(
|
||||||
None,
|
None,
|
||||||
Effect::Health(HealthChange {
|
Effect::Damage(Damage {
|
||||||
amount: -50,
|
source: DamageSource::Explosion,
|
||||||
cause: HealthSource::Explosion { owner: *owner },
|
value: 50.0,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
RadiusEffect::TerrainDestruction(4.0),
|
RadiusEffect::TerrainDestruction(4.0),
|
||||||
|
@ -81,12 +81,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
// (maybe health changes could be sent to the client as a list
|
// (maybe health changes could be sent to the client as a list
|
||||||
// of events)
|
// of events)
|
||||||
if match health.last_change.1.cause {
|
if match health.last_change.1.cause {
|
||||||
HealthSource::Attack { by }
|
HealthSource::Damage { by: Some(by), .. }
|
||||||
| HealthSource::Projectile { owner: Some(by) }
|
| HealthSource::Heal { by: Some(by) } => {
|
||||||
| HealthSource::Energy { owner: Some(by) }
|
|
||||||
| HealthSource::Explosion { owner: Some(by) }
|
|
||||||
| HealthSource::Buff { owner: Some(by) }
|
|
||||||
| HealthSource::Healing { by: Some(by) } => {
|
|
||||||
let by_me = my_uid.map_or(false, |&uid| by == uid);
|
let by_me = my_uid.map_or(false, |&uid| by == uid);
|
||||||
// If the attack was by me also reset this timer
|
// If the attack was by me also reset this timer
|
||||||
if by_me {
|
if by_me {
|
||||||
|
@ -373,9 +373,9 @@ impl<'a> Widget for Chat<'a> {
|
|||||||
.localized_strings
|
.localized_strings
|
||||||
.get("hud.chat.pvp_energy_kill_msg")
|
.get("hud.chat.pvp_energy_kill_msg")
|
||||||
.to_string(),
|
.to_string(),
|
||||||
KillSource::Player(_, KillType::Buff) => self
|
KillSource::Player(_, KillType::Other) => self
|
||||||
.localized_strings
|
.localized_strings
|
||||||
.get("hud.chat.pvp_buff_kill_msg")
|
.get("hud.chat.pvp_other_kill_msg")
|
||||||
.to_string(),
|
.to_string(),
|
||||||
KillSource::NonPlayer(_, KillType::Melee) => self
|
KillSource::NonPlayer(_, KillType::Melee) => self
|
||||||
.localized_strings
|
.localized_strings
|
||||||
@ -393,9 +393,9 @@ impl<'a> Widget for Chat<'a> {
|
|||||||
.localized_strings
|
.localized_strings
|
||||||
.get("hud.chat.npc_energy_kill_msg")
|
.get("hud.chat.npc_energy_kill_msg")
|
||||||
.to_string(),
|
.to_string(),
|
||||||
KillSource::NonPlayer(_, KillType::Buff) => self
|
KillSource::NonPlayer(_, KillType::Other) => self
|
||||||
.localized_strings
|
.localized_strings
|
||||||
.get("hud.chat.npc_buff_kill_msg")
|
.get("hud.chat.npc_other_kill_msg")
|
||||||
.to_string(),
|
.to_string(),
|
||||||
KillSource::Environment(_) => self
|
KillSource::Environment(_) => self
|
||||||
.localized_strings
|
.localized_strings
|
||||||
|
Loading…
Reference in New Issue
Block a user