Sync client disconnects

This commit is contained in:
griffi-gh 2024-04-25 12:30:25 +02:00
parent ec17768842
commit 043bb873c4
5 changed files with 429 additions and 338 deletions

View file

@ -1,7 +1,7 @@
use glam::Mat4; use glam::Mat4;
use shipyard::{Component, EntityId, Unique, AllStoragesView, UniqueView, NonSendSync, View, ViewMut, Get, IntoIter}; use shipyard::{AllStoragesView, AllStoragesViewMut, Component, EntitiesViewMut, EntityId, Get, IntoIter, NonSendSync, Remove, Unique, UniqueView, UniqueViewMut, View, ViewMut};
use hashbrown::HashMap; use hashbrown::HashMap;
use uflow::SendMode; use uflow::{server::Event, SendMode};
use std::net::SocketAddr; use std::net::SocketAddr;
use kubi_shared::{ use kubi_shared::{
networking::{ networking::{
@ -13,7 +13,7 @@ use kubi_shared::{
}; };
use crate::{ use crate::{
server::{ServerEvents, UdpServer}, server::{ServerEvents, UdpServer},
util::check_message_auth util::check_message_auth, world::ChunkManager
}; };
#[derive(Component, Clone, Copy)] #[derive(Component, Clone, Copy)]
@ -79,3 +79,56 @@ pub fn sync_client_positions(
} }
} }
} }
pub fn on_client_disconnect(
mut all_storages: AllStoragesViewMut,
) {
let mut to_delete = Vec::new();
{
let server = all_storages.borrow::<NonSendSync<UniqueView<UdpServer>>>().unwrap();
let events = all_storages.borrow::<UniqueView<ServerEvents>>().unwrap();
let mut addr_map = all_storages.borrow::<UniqueViewMut<ClientAddressMap>>().unwrap();
let mut id_map = all_storages.borrow::<UniqueViewMut<ClientIdMap>>().unwrap();
let clients = all_storages.borrow::<View<Client>>().unwrap();
let mut chunk_manager = all_storages.borrow::<UniqueViewMut<ChunkManager>>().unwrap();
let addrs = all_storages.borrow::<View<ClientAddress>>().unwrap();
for event in &events.0 {
if let Event::Disconnect(addr) = event {
let net_client = server.0.client(addr).unwrap();
let Some(&entity_id) = addr_map.0.get(addr) else {
log::error!("Disconnected client not authenticated, moving on");
continue;
};
let client_id = clients.get(entity_id).unwrap().0;
log::info!("Client disconnected: ID {}", client_id);
addr_map.0.remove(addr);
id_map.0.remove(&client_id);
to_delete.push(entity_id);
//unsubscribe from chunks
chunk_manager.unsubscribe_all(client_id);
//send disconnect message to other clients
for (_, other_client_address) in (&clients, &addrs).iter() {
let Some(client) = server.0.client(&other_client_address.0) else {
log::error!("Client with address not found");
continue
};
client.borrow_mut().send(
postcard::to_allocvec(
&ServerToClientMessage::PlayerDisconnected { id: client_id }
).unwrap().into_boxed_slice(),
Channel::SysEvt as usize,
SendMode::Reliable
);
}
}
}
}
for entity_id in to_delete {
all_storages.delete_entity(entity_id);
}
}

View file

@ -10,7 +10,7 @@ mod auth;
use config::read_config; use config::read_config;
use server::{bind_server, update_server, log_server_errors}; use server::{bind_server, update_server, log_server_errors};
use client::{init_client_maps, sync_client_positions}; use client::{init_client_maps, on_client_disconnect, sync_client_positions};
use auth::authenticate_players; use auth::authenticate_players;
use world::{update_world, init_world}; use world::{update_world, init_world};
@ -31,6 +31,7 @@ fn update() -> Workload {
authenticate_players, authenticate_players,
update_world, update_world,
sync_client_positions, sync_client_positions,
on_client_disconnect,
).into_workload() ).into_workload()
).into_sequential_workload() ).into_sequential_workload()
} }

View file

@ -2,9 +2,7 @@ use shipyard::{Unique, UniqueView, UniqueViewMut, Workload, IntoWorkload, AllSto
use glam::IVec3; use glam::IVec3;
use hashbrown::HashMap; use hashbrown::HashMap;
use kubi_shared::networking::{ use kubi_shared::networking::{
messages::{ClientToServerMessage, ServerToClientMessage, ClientToServerMessageType}, channels::Channel, client::{Client, ClientId}, messages::{ClientToServerMessage, ClientToServerMessageType, ServerToClientMessage}
channels::Channel,
client::Client,
}; };
use uflow::{server::RemoteClient, SendMode}; use uflow::{server::RemoteClient, SendMode};
use lz4_flex::compress_prepend_size as lz4_compress; use lz4_flex::compress_prepend_size as lz4_compress;
@ -30,6 +28,11 @@ pub struct ChunkManager {
pub chunks: HashMap<IVec3, Chunk> pub chunks: HashMap<IVec3, Chunk>
} }
impl ChunkManager { impl ChunkManager {
pub fn unsubscribe_all(&mut self, client_id: ClientId) {
for chunk in self.chunks.values_mut() {
chunk.subscriptions.remove(&client_id);
}
}
pub fn new() -> Self { pub fn new() -> Self {
Self::default() Self::default()
} }

View file

@ -38,7 +38,8 @@ use player::{
init_client_map, init_client_map,
send_player_movement_events, send_player_movement_events,
receive_player_movement_events, receive_player_movement_events,
receive_player_connect_events receive_player_connect_events,
receive_player_disconnect_events,
}; };
const NET_TICKRATE: u16 = 33; const NET_TICKRATE: u16 = 33;
@ -137,6 +138,7 @@ pub fn update_networking() -> Workload {
( (
( (
receive_player_connect_events, receive_player_connect_events,
receive_player_disconnect_events,
).into_workload(), ).into_workload(),
( (
recv_block_place_events, recv_block_place_events,

View file

@ -99,3 +99,35 @@ pub fn receive_player_connect_events(
spawn_remote_player_multiplayer(&mut storages, init); spawn_remote_player_multiplayer(&mut storages, init);
} }
} }
pub fn receive_player_disconnect_events(
mut storages: AllStoragesViewMut,
) {
let messages: Vec<ServerToClientMessage> = storages.borrow::<View<NetworkEvent>>().unwrap().iter().filter_map(|event| {
let ClientEvent::Receive(data) = &event.0 else {
return None
};
if !event.is_message_of_type::<{ServerToClientMessageType::PlayerDisconnected as u8}>() {
return None
};
let Ok(parsed_message) = postcard::from_bytes(data) else {
log::error!("Malformed message");
return None
};
Some(parsed_message)
}).collect();
for message in messages {
let ServerToClientMessage::PlayerDisconnected { id } = message else { unreachable!() };
log::info!("player disconnected: {}", id);
let mut id_map = storages.borrow::<UniqueViewMut<ClientIdMap>>().unwrap();
let Some(ent_id) = id_map.0.remove(&id) else {
log::warn!("Disconnected player entity not found in client-id map");
continue
};
drop(id_map);
if !storages.delete_entity(ent_id) {
log::warn!("Disconnected player entity not found in storage");
}
}
}