instead of manually trying to sync disconnect metrics we now pass a Reason to the handle_disconnect.

There we make sure that the disconnect only happens once and decrease the respective Metrics.
Also added more reasons
This commit is contained in:
Marcel Märtens 2021-05-06 11:43:10 +02:00
parent b1142a05bf
commit 8196fd8b35
11 changed files with 73 additions and 57 deletions

View File

@ -84,6 +84,7 @@ pub use self::{
Collider, Density, ForceUpdate, Mass, PhysicsState, Pos, PosVelDefer, PreviousPhysCache, Collider, Density, ForceUpdate, Mass, PhysicsState, Pos, PosVelDefer, PreviousPhysCache,
Scale, Sticky, Vel, Scale, Sticky, Vel,
}, },
player::DisconnectReason,
player::Player, player::Player,
poise::{Poise, PoiseChange, PoiseSource, PoiseState}, poise::{Poise, PoiseChange, PoiseSource, PoiseState},
projectile::{Projectile, ProjectileConstructor}, projectile::{Projectile, ProjectileConstructor},

View File

@ -5,6 +5,15 @@ use uuid::Uuid;
const MAX_ALIAS_LEN: usize = 32; const MAX_ALIAS_LEN: usize = 32;
#[derive(Debug)]
pub enum DisconnectReason {
Kicked,
NewerLogin,
NetworkError,
Timeout,
ClientRequested,
}
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Player { pub struct Player {
pub alias: String, pub alias: String,

View File

@ -4,7 +4,7 @@ use crate::{
self, self,
invite::{InviteKind, InviteResponse}, invite::{InviteKind, InviteResponse},
item::Item, item::Item,
Ori, Pos, DisconnectReason, Ori, Pos,
}, },
outcome::Outcome, outcome::Outcome,
rtsim::RtSimEntity, rtsim::RtSimEntity,
@ -135,7 +135,7 @@ pub enum ServerEvent {
rtsim_entity: Option<RtSimEntity>, rtsim_entity: Option<RtSimEntity>,
}, },
CreateWaypoint(Vec3<f32>), CreateWaypoint(Vec3<f32>),
ClientDisconnect(EcsEntity), ClientDisconnect(EcsEntity, DisconnectReason),
ClientDisconnectWithoutPersistence(EcsEntity), ClientDisconnectWithoutPersistence(EcsEntity),
ChunkRequest(EcsEntity, Vec2<i32>), ChunkRequest(EcsEntity, Vec2<i32>),
ChatCmd(EcsEntity, String), ChatCmd(EcsEntity, String),

View File

@ -16,7 +16,6 @@ pub struct Client {
pub participant: Option<Participant>, pub participant: Option<Participant>,
pub last_ping: Mutex<f64>, pub last_ping: Mutex<f64>,
pub login_msg_sent: AtomicBool, pub login_msg_sent: AtomicBool,
pub terminate_msg_recv: AtomicBool,
//TODO: improve network crate so that `send` is no longer `&mut self` and we can get rid of //TODO: improve network crate so that `send` is no longer `&mut self` and we can get rid of
// this Mutex. This Mutex is just to please the compiler as we do not get into contention // this Mutex. This Mutex is just to please the compiler as we do not get into contention
@ -68,7 +67,6 @@ impl Client {
participant: Some(participant), participant: Some(participant),
last_ping: Mutex::new(last_ping), last_ping: Mutex::new(last_ping),
login_msg_sent: AtomicBool::new(false), login_msg_sent: AtomicBool::new(false),
terminate_msg_recv: AtomicBool::new(false),
general_stream: Mutex::new(general_stream), general_stream: Mutex::new(general_stream),
ping_stream: Mutex::new(ping_stream), ping_stream: Mutex::new(ping_stream),
register_stream: Mutex::new(register_stream), register_stream: Mutex::new(register_stream),

View File

@ -2477,7 +2477,10 @@ fn kick_player(
server server
.state .state
.mut_resource::<EventBus<ServerEvent>>() .mut_resource::<EventBus<ServerEvent>>()
.emit_now(ServerEvent::ClientDisconnect(target_player)); .emit_now(ServerEvent::ClientDisconnect(
target_player,
common::comp::DisconnectReason::Kicked,
));
Ok(()) Ok(())
} }

View File

@ -172,11 +172,16 @@ impl Server {
rtsim_entity, rtsim_entity,
} => handle_create_ship(self, pos, ship, mountable, agent, rtsim_entity), } => handle_create_ship(self, pos, ship, mountable, agent, rtsim_entity),
ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos), ServerEvent::CreateWaypoint(pos) => handle_create_waypoint(self, pos),
ServerEvent::ClientDisconnect(entity) => { ServerEvent::ClientDisconnect(entity, reason) => {
frontend_events.push(handle_client_disconnect(self, entity, false)) frontend_events.push(handle_client_disconnect(self, entity, reason, false))
}, },
ServerEvent::ClientDisconnectWithoutPersistence(entity) => { ServerEvent::ClientDisconnectWithoutPersistence(entity) => {
frontend_events.push(handle_client_disconnect(self, entity, true)) frontend_events.push(handle_client_disconnect(
self,
entity,
common::comp::DisconnectReason::Kicked,
true,
))
}, },
ServerEvent::ChunkRequest(entity, key) => { ServerEvent::ChunkRequest(entity, key) => {

View File

@ -1,7 +1,7 @@
use super::Event; use super::Event;
use crate::{ use crate::{
client::Client, persistence::character_updater::CharacterUpdater, presence::Presence, client::Client, metrics::PlayerMetrics, persistence::character_updater::CharacterUpdater,
state_ext::StateExt, Server, presence::Presence, state_ext::StateExt, Server,
}; };
use common::{ use common::{
comp, comp,
@ -96,9 +96,20 @@ pub fn handle_exit_ingame(server: &mut Server, entity: EcsEntity) {
} }
} }
fn get_reason_str(reason: &comp::DisconnectReason) -> &str {
match reason {
comp::DisconnectReason::Timeout => "timeout",
comp::DisconnectReason::NetworkError => "network_error",
comp::DisconnectReason::NewerLogin => "newer_login",
comp::DisconnectReason::Kicked => "kicked",
comp::DisconnectReason::ClientRequested => "client_requested",
}
}
pub fn handle_client_disconnect( pub fn handle_client_disconnect(
server: &mut Server, server: &mut Server,
mut entity: EcsEntity, mut entity: EcsEntity,
reason: comp::DisconnectReason,
skip_persistence: bool, skip_persistence: bool,
) -> Event { ) -> Event {
span!(_guard, "handle_client_disconnect"); span!(_guard, "handle_client_disconnect");
@ -112,6 +123,14 @@ pub fn handle_client_disconnect(
// receiving multiple `ServerEvent::ClientDisconnect` messages in a tick // receiving multiple `ServerEvent::ClientDisconnect` messages in a tick
// intended for the same player, so the `None` case here is *not* a bug // intended for the same player, so the `None` case here is *not* a bug
// and we should not log it as a potential issue. // and we should not log it as a potential issue.
server
.state()
.ecs()
.read_resource::<PlayerMetrics>()
.clients_disconnected
.with_label_values(&[get_reason_str(&reason)])
.inc();
if let Some(participant) = client.participant.take() { if let Some(participant) = client.participant.take() {
let pid = participant.remote_pid(); let pid = participant.remote_pid();
server.runtime.spawn( server.runtime.spawn(
@ -135,7 +154,8 @@ pub fn handle_client_disconnect(
.instrument(tracing::debug_span!( .instrument(tracing::debug_span!(
"client_disconnect", "client_disconnect",
?pid, ?pid,
?entity ?entity,
?reason,
)), )),
); );
} }

View File

@ -861,7 +861,7 @@ impl Server {
for (_, entity) in (&clients, &entities).join() { for (_, entity) in (&clients, &entities).join() {
info!("Emitting client disconnect event for entity: {:?}", entity); info!("Emitting client disconnect event for entity: {:?}", entity);
let event = if with_persistence { let event = if with_persistence {
ServerEvent::ClientDisconnect(entity) ServerEvent::ClientDisconnect(entity, comp::DisconnectReason::Kicked)
} else { } else {
ServerEvent::ClientDisconnectWithoutPersistence(entity) ServerEvent::ClientDisconnectWithoutPersistence(entity)
}; };

View File

@ -1,4 +1,4 @@
use crate::{client::Client, metrics::PlayerMetrics}; use crate::client::Client;
use common::{ use common::{
comp::{ChatMode, Player}, comp::{ChatMode, Player},
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
@ -9,8 +9,7 @@ use common_ecs::{Job, Origin, Phase, System};
use common_net::msg::{ use common_net::msg::{
validate_chat_msg, ChatMsgValidationError, ClientGeneral, MAX_BYTES_CHAT_MSG, validate_chat_msg, ChatMsgValidationError, ClientGeneral, MAX_BYTES_CHAT_MSG,
}; };
use specs::{Entities, Join, Read, ReadExpect, ReadStorage}; use specs::{Entities, Join, Read, ReadStorage};
use std::sync::atomic::Ordering;
use tracing::{debug, error, warn}; use tracing::{debug, error, warn};
impl Sys { impl Sys {
@ -19,9 +18,8 @@ impl Sys {
fn handle_general_msg( fn handle_general_msg(
server_emitter: &mut common::event::Emitter<'_, ServerEvent>, server_emitter: &mut common::event::Emitter<'_, ServerEvent>,
entity: specs::Entity, entity: specs::Entity,
client: &Client, _client: &Client,
player: Option<&Player>, player: Option<&Player>,
player_metrics: &ReadExpect<'_, PlayerMetrics>,
uids: &ReadStorage<'_, Uid>, uids: &ReadStorage<'_, Uid>,
chat_modes: &ReadStorage<'_, ChatMode>, chat_modes: &ReadStorage<'_, ChatMode>,
msg: ClientGeneral, msg: ClientGeneral,
@ -56,12 +54,10 @@ impl Sys {
}, },
ClientGeneral::Terminate => { ClientGeneral::Terminate => {
debug!(?entity, "Client send message to terminate session"); debug!(?entity, "Client send message to terminate session");
player_metrics server_emitter.emit(ServerEvent::ClientDisconnect(
.clients_disconnected entity,
.with_label_values(&["gracefully"]) common::comp::DisconnectReason::ClientRequested,
.inc(); ));
client.terminate_msg_recv.store(true, Ordering::Relaxed);
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
}, },
_ => unreachable!("not a client_general msg"), _ => unreachable!("not a client_general msg"),
} }
@ -78,7 +74,6 @@ impl<'a> System<'a> for Sys {
Entities<'a>, Entities<'a>,
Read<'a, EventBus<ServerEvent>>, Read<'a, EventBus<ServerEvent>>,
Read<'a, Time>, Read<'a, Time>,
ReadExpect<'a, PlayerMetrics>,
ReadStorage<'a, Uid>, ReadStorage<'a, Uid>,
ReadStorage<'a, ChatMode>, ReadStorage<'a, ChatMode>,
ReadStorage<'a, Player>, ReadStorage<'a, Player>,
@ -91,16 +86,7 @@ impl<'a> System<'a> for Sys {
fn run( fn run(
_job: &mut Job<Self>, _job: &mut Job<Self>,
( (entities, server_event_bus, time, uids, chat_modes, players, clients): Self::SystemData,
entities,
server_event_bus,
time,
player_metrics,
uids,
chat_modes,
players,
clients,
): Self::SystemData,
) { ) {
let mut server_emitter = server_event_bus.emitter(); let mut server_emitter = server_event_bus.emitter();
@ -111,7 +97,6 @@ impl<'a> System<'a> for Sys {
entity, entity,
client, client,
player, player,
&player_metrics,
&uids, &uids,
&chat_modes, &chat_modes,
msg, msg,

View File

@ -1,12 +1,11 @@
use crate::{client::Client, metrics::PlayerMetrics, Settings}; use crate::{client::Client, Settings};
use common::{ use common::{
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
resources::Time, resources::Time,
}; };
use common_ecs::{Job, Origin, Phase, System}; use common_ecs::{Job, Origin, Phase, System};
use common_net::msg::PingMsg; use common_net::msg::PingMsg;
use specs::{Entities, Join, Read, ReadExpect, ReadStorage}; use specs::{Entities, Join, Read, ReadStorage};
use std::sync::atomic::Ordering;
use tracing::{debug, info}; use tracing::{debug, info};
impl Sys { impl Sys {
@ -28,7 +27,6 @@ impl<'a> System<'a> for Sys {
Entities<'a>, Entities<'a>,
Read<'a, EventBus<ServerEvent>>, Read<'a, EventBus<ServerEvent>>,
Read<'a, Time>, Read<'a, Time>,
ReadExpect<'a, PlayerMetrics>,
ReadStorage<'a, Client>, ReadStorage<'a, Client>,
Read<'a, Settings>, Read<'a, Settings>,
); );
@ -39,7 +37,7 @@ impl<'a> System<'a> for Sys {
fn run( fn run(
_job: &mut Job<Self>, _job: &mut Job<Self>,
(entities, server_event_bus, time, player_metrics, clients, settings): Self::SystemData, (entities, server_event_bus, time, clients, settings): Self::SystemData,
) { ) {
let mut server_emitter = server_event_bus.emitter(); let mut server_emitter = server_event_bus.emitter();
@ -48,14 +46,11 @@ impl<'a> System<'a> for Sys {
match res { match res {
Err(e) => { Err(e) => {
if !client.terminate_msg_recv.load(Ordering::Relaxed) { debug!(?entity, ?e, "network error with client, disconnecting");
debug!(?entity, ?e, "network error with client, disconnecting"); server_emitter.emit(ServerEvent::ClientDisconnect(
player_metrics entity,
.clients_disconnected common::comp::DisconnectReason::NetworkError,
.with_label_values(&["network_error"]) ));
.inc();
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
}
}, },
Ok(1_u64..=u64::MAX) => { Ok(1_u64..=u64::MAX) => {
// Update client ping. // Update client ping.
@ -66,14 +61,11 @@ impl<'a> System<'a> for Sys {
if time.0 - last_ping > settings.client_timeout.as_secs() as f64 if time.0 - last_ping > settings.client_timeout.as_secs() as f64
// Timeout // Timeout
{ {
if !client.terminate_msg_recv.load(Ordering::Relaxed) { info!(?entity, "timeout error with client, disconnecting");
info!(?entity, "timeout error with client, disconnecting"); server_emitter.emit(ServerEvent::ClientDisconnect(
player_metrics entity,
.clients_disconnected common::comp::DisconnectReason::Timeout,
.with_label_values(&["timeout"]) ));
.inc();
server_emitter.emit(ServerEvent::ClientDisconnect(entity));
}
} else if time.0 - last_ping > settings.client_timeout.as_secs() as f64 * 0.5 { } else if time.0 - last_ping > settings.client_timeout.as_secs() as f64 * 0.5 {
// Try pinging the client if the timeout is nearing. // Try pinging the client if the timeout is nearing.
client.send_fallible(PingMsg::Ping); client.send_fallible(PingMsg::Ping);

View File

@ -145,7 +145,10 @@ impl<'a> System<'a> for Sys {
.find(|(_, _, old_player)| old_player.uuid() == uuid) .find(|(_, _, old_player)| old_player.uuid() == uuid)
{ {
// Remove old client // Remove old client
server_event_bus.emit_now(ServerEvent::ClientDisconnect(old_entity)); server_event_bus.emit_now(ServerEvent::ClientDisconnect(
old_entity,
common::comp::DisconnectReason::NewerLogin,
));
let _ = old_client.send(ServerGeneral::Disconnect(DisconnectReason::Kicked( let _ = old_client.send(ServerGeneral::Disconnect(DisconnectReason::Kicked(
String::from("You have logged in from another location."), String::from("You have logged in from another location."),
))); )));