mirror of
https://github.com/redstrate/Kawari.git
synced 2025-05-20 01:37:45 +00:00
Begin adding disconnection handling
This doesn't work 100% reliably yet, but the world server should now try to commit your player back to the database if it detects you disconnect. I also fixed a mistake where the global server state never removed clients when they errored out. There's now code to remove your actor from the instance when disconnecting, but this doesn't work reliably yet either.
This commit is contained in:
parent
f37aa53011
commit
a88b9037d4
4 changed files with 702 additions and 643 deletions
|
@ -1,6 +1,7 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use kawari::RECEIVE_BUFFER_SIZE;
|
use kawari::RECEIVE_BUFFER_SIZE;
|
||||||
use kawari::common::Position;
|
use kawari::common::Position;
|
||||||
|
@ -137,8 +138,21 @@ async fn client_loop(
|
||||||
loop {
|
loop {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
biased; // client data should always be prioritized
|
biased; // client data should always be prioritized
|
||||||
Ok(n) = connection.socket.read(&mut buf) => {
|
n = connection.socket.read(&mut buf) => {
|
||||||
|
match n {
|
||||||
|
Ok(n) => {
|
||||||
|
// if the last response was over >5 seconds, the client is probably gone
|
||||||
|
if n == 0 {
|
||||||
|
let now = Instant::now();
|
||||||
|
if now.duration_since(connection.last_keep_alive) > Duration::from_secs(5) {
|
||||||
|
tracing::info!("Connection was killed because of timeout");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
|
connection.last_keep_alive = Instant::now();
|
||||||
|
|
||||||
let (segments, connection_type) = connection.parse_packet(&buf[..n]).await;
|
let (segments, connection_type) = connection.parse_packet(&buf[..n]).await;
|
||||||
for segment in &segments {
|
for segment in &segments {
|
||||||
match &segment.data {
|
match &segment.data {
|
||||||
|
@ -472,6 +486,8 @@ async fn client_loop(
|
||||||
tracing::info!("Client disconnected!");
|
tracing::info!("Client disconnected!");
|
||||||
|
|
||||||
connection.handle.send(ToServer::Disconnected(connection.id)).await;
|
connection.handle.send(ToServer::Disconnected(connection.id)).await;
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
ClientZoneIpcData::ChatMessage(chat_message) => {
|
ClientZoneIpcData::ChatMessage(chat_message) => {
|
||||||
connection.handle.send(ToServer::Message(connection.id, chat_message.message.clone())).await;
|
connection.handle.send(ToServer::Message(connection.id, chat_message.message.clone())).await;
|
||||||
|
@ -725,8 +741,8 @@ async fn client_loop(
|
||||||
tracing::info!("Despawning {} because they died!", actor.id.0);
|
tracing::info!("Despawning {} because they died!", actor.id.0);
|
||||||
// if the actor died, despawn them
|
// if the actor died, despawn them
|
||||||
/*connection.handle
|
/*connection.handle
|
||||||
.send(ToServer::ActorDespawned(connection.id, actor.id.0))
|
* .send(ToServer::ActorDespawned(connection.id, actor.id.0))
|
||||||
.await;*/
|
* .await;*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -859,6 +875,12 @@ async fn client_loop(
|
||||||
lua_player.player_data = connection.player_data.clone();
|
lua_player.player_data = connection.player_data.clone();
|
||||||
lua_player.status_effects = connection.status_effects.clone();
|
lua_player.status_effects = connection.status_effects.clone();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
Err(_) => {
|
||||||
|
tracing::info!("Connection was killed because of a network error!");
|
||||||
|
break;
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
msg = internal_recv.recv() => match msg {
|
msg = internal_recv.recv() => match msg {
|
||||||
Some(msg) => match msg {
|
Some(msg) => match msg {
|
||||||
|
@ -875,6 +897,16 @@ async fn client_loop(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// forcefully log out the player if they weren't logging out but force D/C'd
|
||||||
|
if connection.player_data.actor_id != 0 && !connection.gracefully_logged_out {
|
||||||
|
tracing::info!("Forcefully logging out player...");
|
||||||
|
connection.begin_log_out().await;
|
||||||
|
connection
|
||||||
|
.handle
|
||||||
|
.send(ToServer::Disconnected(connection.id))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_rcon(listener: &Option<TcpListener>) -> Option<(TcpStream, SocketAddr)> {
|
async fn handle_rcon(listener: &Option<TcpListener>) -> Option<(TcpStream, SocketAddr)> {
|
||||||
|
@ -996,6 +1028,8 @@ async fn main() {
|
||||||
gamedata: game_data.clone(),
|
gamedata: game_data.clone(),
|
||||||
exit_position: None,
|
exit_position: None,
|
||||||
exit_rotation: None,
|
exit_rotation: None,
|
||||||
|
last_keep_alive: Instant::now(),
|
||||||
|
gracefully_logged_out: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Some((mut socket, _)) = handle_rcon(&rcon_listener) => {
|
Some((mut socket, _)) = handle_rcon(&rcon_listener) => {
|
||||||
|
|
|
@ -207,7 +207,9 @@ pub async fn send_packet<T: ReadWriteIpcSegment>(
|
||||||
|
|
||||||
let buffer = cursor.into_inner();
|
let buffer = cursor.into_inner();
|
||||||
|
|
||||||
socket.write_all(&buffer).await.unwrap();
|
if let Err(e) = socket.write_all(&buffer).await {
|
||||||
|
tracing::warn!("Failed to send packet: {e}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporary
|
// temporary
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::{
|
use std::{
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
sync::{Arc, Mutex},
|
sync::{Arc, Mutex},
|
||||||
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use tokio::net::TcpStream;
|
use tokio::net::TcpStream;
|
||||||
|
@ -87,6 +88,11 @@ pub struct ZoneConnection {
|
||||||
|
|
||||||
pub exit_position: Option<Position>,
|
pub exit_position: Option<Position>,
|
||||||
pub exit_rotation: Option<f32>,
|
pub exit_rotation: Option<f32>,
|
||||||
|
|
||||||
|
pub last_keep_alive: Instant,
|
||||||
|
|
||||||
|
/// Whether the player was gracefully logged out
|
||||||
|
pub gracefully_logged_out: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ZoneConnection {
|
impl ZoneConnection {
|
||||||
|
@ -612,6 +618,8 @@ impl ZoneConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn begin_log_out(&mut self) {
|
pub async fn begin_log_out(&mut self) {
|
||||||
|
self.gracefully_logged_out = true;
|
||||||
|
|
||||||
// write the player back to the database
|
// write the player back to the database
|
||||||
self.database.commit_player_data(&self.player_data);
|
self.database.commit_player_data(&self.player_data);
|
||||||
|
|
||||||
|
|
|
@ -313,10 +313,25 @@ pub async fn server_main_loop(mut recv: Receiver<ToServer>) -> Result<(), std::i
|
||||||
}
|
}
|
||||||
ToServer::FatalError(err) => return Err(err),
|
ToServer::FatalError(err) => return Err(err),
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// Remove any clients that errored out
|
// Remove any clients that errored out
|
||||||
for id in to_remove {
|
for remove_id in &to_remove {
|
||||||
data.clients.remove(&id);
|
// remove any actors they had
|
||||||
|
let mut actor_id = None;
|
||||||
|
for (id, (handle, _)) in &mut data.clients {
|
||||||
|
if *id == *remove_id {
|
||||||
|
actor_id = Some(handle.actor_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(actor_id) = actor_id {
|
||||||
|
// remove them from the instance
|
||||||
|
let current_instance = data.find_actor_instance_mut(actor_id).unwrap();
|
||||||
|
current_instance.actors.remove(&ObjectId(actor_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
data.clients.remove(&remove_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue