diff --git a/src/common/mod.rs b/src/common/mod.rs index 055ace5..dbee73e 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -5,3 +5,17 @@ pub mod client; pub mod clientpool; pub mod mainloop; pub mod leveltable; + +// https://www.reddit.com/r/rust/comments/33xhhu/how_to_create_an_array_of_structs_that_havent/ +#[macro_export] +macro_rules! init_array( + ($ty:ty, $len:expr, $val:expr) => ( + { + let mut array: [$ty; $len] = unsafe { std::mem::uninitialized() }; + for i in array.iter_mut() { + unsafe { ::std::ptr::write(i, $val); } + } + array + } + ) +); diff --git a/src/ship/location.rs b/src/ship/location.rs index 35a60a4..d848b35 100644 --- a/src/ship/location.rs +++ b/src/ship/location.rs @@ -1,3 +1,5 @@ +use std::sync::{Arc, RwLock}; + use std::time::SystemTime; use crate::common::serverstate::ClientId; // TODO: room passwords? @@ -11,7 +13,6 @@ pub struct AreaClient { time_join: SystemTime, } - #[derive(Copy, Clone)] pub struct InnerClientArea { clients: [Option; N], @@ -80,23 +81,36 @@ pub struct RoomId(pub usize); pub type Lobby = InnerClientArea<12>; pub type Room = InnerClientArea<4>; -trait ClientArea<'a> { - fn clients(&'a self) -> std::slice::Iter<'_, Option>; +trait ClientArea { + fn clients(&self) -> std::slice::Iter<'_, Option>; + fn remove(&mut self, id: ClientId) -> bool; } -impl<'a> ClientArea<'a> for Lobby { - fn clients(&'a self) -> std::slice::Iter<'_, Option> { +impl ClientArea for Lobby { + fn clients(& self) -> std::slice::Iter<'_, Option> { self.clients.iter() } + + fn remove(&mut self, id: ClientId) -> bool { + self.remove(id) + } } -impl<'a> ClientArea<'a> for Room { - fn clients(&'a self) -> std::slice::Iter<'_, Option> { +impl<'a> ClientArea for Room { + fn clients(& self) -> std::slice::Iter<'_, Option> { self.clients.iter() } + + fn remove(&mut self, id: ClientId) -> bool { + self.remove(id) + } } +pub enum AreaType { + Lobby, + Room, +} #[derive(Debug)] pub struct ClientAtLocation { @@ -104,21 +118,22 @@ pub struct ClientAtLocation { pub index: usize, } - -pub struct Area<'a> { - area: &'a dyn ClientArea<'a>, +pub struct Area { + pub area_type: AreaType, + area: Arc>, index: usize, } -impl<'a> Area<'a> { - fn new(area: &'a dyn ClientArea<'a>, index: usize) -> Area<'a> { +impl Area { + fn new(area_type: AreaType, area: Arc>, index: usize) -> Area { Area { + area_type: area_type, area: area, index: index, } } - pub fn clients(&'a self) -> Vec { - self.area.clients() + pub fn clients(&self) -> Vec { + self.area.read().unwrap().clients() .enumerate() .filter(|(_i, k)| k.is_some()) .map(|(i, k)| (i, k.unwrap()) ) @@ -129,9 +144,9 @@ impl<'a> Area<'a> { } // TODO: Result in cases where no one is in the area? pub fn leader(&self) -> ClientAtLocation { - self.area.clients() + self.area.read().unwrap().clients() .enumerate() - .filter(|(i, k)| k.is_some()) + .filter(|(_i, k)| k.is_some()) .map(|(i, k)| (i, k.unwrap()) ) .fold((ClientAtLocation { client_id: ClientId(0), @@ -150,6 +165,10 @@ impl<'a> Area<'a> { }).0 } + pub fn remove(&mut self, id: ClientId) -> bool { + self.area.write().unwrap().remove(id) + } + pub fn id(&self) -> usize { self.index } @@ -176,25 +195,26 @@ pub enum JoinLobbyError { } pub struct ClientLocation { - lobbies: [Lobby; 15], - rooms: [Option; MAX_ROOMS], + lobbies: [Arc>; 15], + rooms: [Option>>; MAX_ROOMS], } impl ClientLocation { pub fn new() -> ClientLocation { ClientLocation { - lobbies: [Lobby::new(); 15], + //lobbies: [Arc::new(RwLock::new(Lobby::new())); 15], + lobbies: crate::init_array!(Arc>, 15, Arc::new(RwLock::new(Lobby::new()))), rooms: [None; MAX_ROOMS], } } fn err_if_client_is_in_area(&mut self, id: ClientId, err: E) -> Result<(), E> { let in_lobby = self.lobbies.iter() - .any(|k| k.contains(id)); + .any(|k| k.read().unwrap().contains(id)); let in_room = self.rooms.iter() .filter(|k| k.is_some()) - .map(|k| k.unwrap()) - .any(|k| k.contains(id)); + .map(|k| k.as_ref().unwrap()) + .any(|k| k.read().unwrap().contains(id)); if in_lobby || in_room { Err(err) @@ -208,6 +228,7 @@ impl ClientLocation { self.err_if_client_is_in_area(id, JoinLobbyError::ClientInAreaAlready)?; self.lobbies.get_mut(lobby.0) .ok_or(JoinLobbyError::LobbyDoesNotExist)? + .write().unwrap() .add(id) .ok_or(JoinLobbyError::LobbyFull) } @@ -222,7 +243,7 @@ impl ClientLocation { let mut new_room = Room::new(); new_room.add(id); - *empty_room = Some(new_room); + *empty_room = Some(Arc::new(RwLock::new(new_room))); Ok(RoomId(room_id)) } @@ -233,21 +254,22 @@ impl ClientLocation { .ok_or(JoinRoomError::RoomDoesNotExist)? .as_mut() .ok_or(JoinRoomError::RoomDoesNotExist)? + .write().unwrap() .add(id) .ok_or(JoinRoomError::RoomFull) } - pub fn get_area_by_user(&self, id: ClientId) -> Area { + pub fn get_area_by_user(&mut self, id: ClientId) -> Area { for (i, lobby) in self.lobbies.iter().enumerate() { - if lobby.contains(id) { - return Area::new(lobby, i); + if lobby.read().unwrap().contains(id) { + return Area::new(AreaType::Lobby, lobby.clone(), i); } } for (i, room) in self.rooms.iter().enumerate() { if let Some(room) = room { - if room.contains(id){ - return Area::new(room, i); + if room.read().unwrap().contains(id){ + return Area::new(AreaType::Room, room.clone(), i); } } } @@ -257,7 +279,7 @@ impl ClientLocation { pub fn remove_from_location(&mut self, id: ClientId) { let in_lobby = self.lobbies.iter_mut() - .map(|lobby| lobby.remove(id)) + .map(|lobby| lobby.write().unwrap().remove(id)) .any(|k| k); if in_lobby { @@ -266,8 +288,8 @@ impl ClientLocation { self.rooms.iter_mut() .filter(|lobby| lobby.is_some()) - .map(|lobby| lobby.unwrap()) - .map(|mut lobby| lobby.remove(id)) + .map(|lobby| lobby.as_ref().unwrap()) + .map(|mut lobby| lobby.write().unwrap().remove(id)) .any(|k| k); } } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 46d807d..58386ca 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -20,7 +20,7 @@ use crate::entity::account::{UserAccount, UserSettings, USERFLAG_NEWCHAR, USERFL use crate::entity::character::Character; use crate::entity::item::ItemLocation; use crate::login::login::get_login_status; -use crate::ship::location::{ClientLocation, LobbyId, RoomId}; +use crate::ship::location::{ClientLocation, LobbyId, RoomId, AreaType}; use crate::ship::character::{CharacterBuilder, FullCharacterBuilder}; use crate::ship::room; @@ -59,7 +59,7 @@ impl RecvServerPacket for RecvShipPacket { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum SendShipPacket { ShipWelcome(ShipWelcome), LoginResponse(LoginResponse), @@ -74,6 +74,8 @@ pub enum SendShipPacket { SmallDialog(SmallDialog), JoinRoom(JoinRoom), AddToRoom(AddToRoom), + LeaveLobby(LeaveLobby), + LeaveRoom(LeaveRoom), } impl SendServerPacket for SendShipPacket { @@ -92,6 +94,8 @@ impl SendServerPacket for SendShipPacket { SendShipPacket::SmallDialog(pkt) => pkt.as_bytes(), SendShipPacket::JoinRoom(pkt) => pkt.as_bytes(), SendShipPacket::AddToRoom(pkt) => pkt.as_bytes(), + SendShipPacket::LeaveLobby(pkt) => pkt.as_bytes(), + SendShipPacket::LeaveRoom(pkt) => pkt.as_bytes(), } } } @@ -430,7 +434,25 @@ impl ServerState for ShipServerState { } fn on_disconnect(&mut self, id: ClientId) -> Vec<(ClientId, SendShipPacket)> { - warn!("disconnected!"); - Vec::new() + let mut area = self.client_location.get_area_by_user(id); + let client = area.clients().into_iter().filter(|client| { + client.client_id == id + //}).collect::>()[0]; + }).next().unwrap(); + let other_clients = area.clients().into_iter().filter(|client| { + client.client_id != id + }); + //self.client_location.remove_from_location(id); + area.remove(id); + let leader = area.leader(); + + let pkt = match area.area_type { + AreaType::Lobby => SendShipPacket::LeaveLobby(LeaveLobby::new(client.index as u8, leader.index as u8)), + AreaType::Room => SendShipPacket::LeaveRoom(LeaveRoom::new(client.index as u8, leader.index as u8)), + }; + + other_clients.map(|client| { + (client.client_id, pkt.clone()) + }).collect() } }