diff --git a/client/src/lib.rs b/client/src/lib.rs index 3f1b611f55..b3c49ebfb5 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -30,14 +30,19 @@ use std::{ use uvth::{ThreadPool, ThreadPoolBuilder}; use vek::*; +// The duration of network inactivity until the player is kicked to the main menu const SERVER_TIMEOUT: Duration = Duration::from_secs(20); +// After this duration has elapsed, the user will begin getting kick warnings in their chat window +const SERVER_TIMEOUT_GRACE_PERIOD: Duration = Duration::from_secs(14); + pub enum Event { Chat { chat_type: ChatType, message: String, }, Disconnect, + DisconnectionNotification(u64), } pub struct Client { @@ -49,6 +54,7 @@ pub struct Client { postbox: PostBox, last_server_ping: Instant, + last_server_pong: Instant, last_ping_delta: f64, tick: u64, @@ -134,6 +140,7 @@ impl Client { postbox, last_server_ping: Instant::now(), + last_server_pong: Instant::now(), last_ping_delta: 0.0, tick: 0, @@ -494,6 +501,19 @@ impl Client { fn handle_new_messages(&mut self) -> Result, Error> { let mut frontend_events = Vec::new(); + // Check that we have an valid connection. + // Limit this to the ping time (1s), since that's the max resolution we have + if Instant::now().duration_since(self.last_server_ping) > Duration::from_secs(1) { + let duration_since_last_pong = Instant::now().duration_since(self.last_server_pong); + + // Dispatch a notification to the HUD warning they will be kicked in {n} seconds + if duration_since_last_pong.as_secs_f64() > SERVER_TIMEOUT_GRACE_PERIOD.as_secs_f64() { + frontend_events.push(Event::DisconnectionNotification( + SERVER_TIMEOUT.as_secs() - duration_since_last_pong.as_secs(), + )); + } + } + let new_msgs = self.postbox.new_messages(); if new_msgs.len() > 0 { @@ -508,6 +528,8 @@ impl Client { ServerMsg::InitialSync { .. } => return Err(Error::ServerWentMad), ServerMsg::Ping => self.postbox.send_message(ClientMsg::Pong), ServerMsg::Pong => { + self.last_server_pong = Instant::now(); + self.last_ping_delta = Instant::now() .duration_since(self.last_server_ping) .as_secs_f64() @@ -591,7 +613,7 @@ impl Client { } else if let Some(err) = self.postbox.error() { return Err(err.into()); // We regularily ping in the tick method - } else if Instant::now().duration_since(self.last_server_ping) > SERVER_TIMEOUT { + } else if Instant::now().duration_since(self.last_server_pong) > SERVER_TIMEOUT { return Err(Error::ServerTimeout); } Ok(frontend_events) diff --git a/voxygen/src/session.rs b/voxygen/src/session.rs index 0e89fd2907..1b1d560b9f 100644 --- a/voxygen/src/session.rs +++ b/voxygen/src/session.rs @@ -6,8 +6,9 @@ use crate::{ window::{Event, GameInput}, Direction, Error, GlobalState, PlayState, PlayStateResult, }; -use client::{self, Client}; +use client::{self, Client, Event::Chat}; use common::{ + ChatType, clock::Clock, comp, comp::{Pos, Vel}, @@ -55,13 +56,21 @@ impl SessionState { fn tick(&mut self, dt: Duration) -> Result<(), Error> { for event in self.client.borrow_mut().tick(self.inputs.clone(), dt)? { match event { - client::Event::Chat { + Chat { chat_type: _, message: _, } => { self.hud.new_message(event); } client::Event::Disconnect => {} // TODO + client::Event::DisconnectionNotification(time) => { + log::warn!("{}", format!("{:#?}", time)); + + self.hud.new_message(Chat { + chat_type: ChatType::Meta, + message: format!("Connection lost. Kicking in {} seconds", time), + }); + } } }