use std::sync::{Arc, RwLock}; use std::time::SystemTime; use crate::common::serverstate::ClientId; // TODO: room passwords? // TODO: remove clients from areas (or upon insert, remove that id from anywhere else) pub const MAX_ROOMS: usize = 128; #[derive(Copy, Clone)] pub struct AreaClient { client_id: ClientId, time_join: SystemTime, } #[derive(Copy, Clone)] pub struct InnerClientArea { clients: [Option; N], } impl InnerClientArea<{N}> { pub fn new() -> InnerClientArea<{N}> { let mut clients: [std::mem::MaybeUninit>; N] = unsafe { std::mem::MaybeUninit::uninit().assume_init() }; for i in clients.iter_mut() { i.write(None); } InnerClientArea { clients: unsafe { (&clients as *const _ as *const [Option; N]).read()} } } fn add(&mut self, id: ClientId) -> Option { for (i, client) in self.clients.iter_mut().enumerate() { if client.is_none() { *client = Some(AreaClient{ client_id: id, time_join: SystemTime::now(), }); return Some(i); } } return None; } fn remove(&mut self, id: ClientId) -> bool { for areaclient in self.clients.iter_mut() { if let Some(client) = *areaclient { if client.client_id == id { *areaclient = None; return true; } } } false } fn contains(&self, id: ClientId) -> bool { self.clients.iter() .filter(|k| k.is_some()) .map(|k| k.unwrap() ) .fold(false, |acc, k| { if acc { acc } else if k.client_id == id { true } else { false } }) } } pub struct LobbyId(pub usize); pub struct RoomId(pub usize); pub type Lobby = InnerClientArea<12>; pub type Room = InnerClientArea<4>; trait ClientArea { fn clients(&self) -> std::slice::Iter<'_, Option>; fn remove(&mut self, id: ClientId) -> bool; } 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 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 { pub client_id: ClientId, pub index: usize, } pub struct Area { pub area_type: AreaType, area: Arc>, index: usize, } impl Area { fn new(area_type: AreaType, area: Arc>, index: usize) -> Area { Area { area_type: area_type, area: area, index: index, } } pub fn clients(&self) -> Vec { self.area.read().unwrap().clients() .enumerate() .filter(|(_i, k)| k.is_some()) .map(|(i, k)| (i, k.unwrap()) ) .map(|(i, k)| ClientAtLocation { client_id: k.client_id, index: i }).collect() } // TODO: Result in cases where no one is in the area? pub fn leader(&self) -> ClientAtLocation { self.area.read().unwrap().clients() .enumerate() .filter(|(_i, k)| k.is_some()) .map(|(i, k)| (i, k.unwrap()) ) .fold((ClientAtLocation { client_id: ClientId(0), index: 0 }, SystemTime::UNIX_EPOCH), |(acc, time), (i, k)| { if time > k.time_join { (ClientAtLocation { client_id: k.client_id, index: i, }, k.time_join) } else { (acc, time) } }).0 } pub fn remove(&mut self, id: ClientId) -> bool { self.area.write().unwrap().remove(id) } pub fn id(&self) -> usize { self.index } } #[derive(Debug)] pub enum CreateRoomError { NoOpenSlots, ClientInAreaAlready, } #[derive(Debug)] pub enum JoinRoomError { RoomDoesNotExist, RoomFull, ClientInAreaAlready, } #[derive(Debug)] pub enum JoinLobbyError { LobbyDoesNotExist, LobbyFull, ClientInAreaAlready, } pub struct ClientLocation { lobbies: [Arc>; 15], rooms: [Option>>; MAX_ROOMS], } impl ClientLocation { pub fn new() -> ClientLocation { ClientLocation { //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.read().unwrap().contains(id)); let in_room = self.rooms.iter() .filter(|k| k.is_some()) .map(|k| k.as_ref().unwrap()) .any(|k| k.read().unwrap().contains(id)); if in_lobby || in_room { Err(err) } else { Ok(()) } } pub fn add_to_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result { 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) } pub fn new_room(&mut self, id: ClientId) -> Result { let (room_id, empty_room) = self.rooms.iter_mut() .enumerate() .filter(|(_, k)| k.is_none()) .nth(0) .ok_or(CreateRoomError::NoOpenSlots)?; let mut new_room = Room::new(); new_room.add(id); *empty_room = Some(Arc::new(RwLock::new(new_room))); self.remove_from_location(id); Ok(RoomId(room_id)) } pub fn add_to_room(&mut self, id: ClientId, room: RoomId) -> Result { self.err_if_client_is_in_area(id, JoinRoomError::ClientInAreaAlready)?; self.rooms.get_mut(room.0) .ok_or(JoinRoomError::RoomDoesNotExist)? .as_mut() .ok_or(JoinRoomError::RoomDoesNotExist)? .write().unwrap() .add(id) .ok_or(JoinRoomError::RoomFull) } pub fn get_area_by_user(&mut self, id: ClientId) -> Area { for (i, lobby) in self.lobbies.iter().enumerate() { 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.read().unwrap().contains(id){ return Area::new(AreaType::Room, room.clone(), i); } } } panic!("client is not in a room/lobby") } pub fn remove_from_location(&mut self, id: ClientId) { let in_lobby = self.lobbies.iter_mut() .map(|lobby| lobby.write().unwrap().remove(id)) .any(|k| k); if in_lobby { return; } self.rooms.iter_mut() .filter(|lobby| lobby.is_some()) .map(|lobby| lobby.as_ref().unwrap()) .map(|lobby| lobby.write().unwrap().remove(id)) .any(|k| k); } pub fn get_client_count_in_room(&self, room_id: RoomId) -> u8 { self.rooms[room_id.0].as_ref() .unwrap() .read() .unwrap() .clients() .filter(|k| k.is_some()) .count() as u8 } }