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) #[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) { for areaclient in self.clients.iter_mut() { if let Some(client) = *areaclient { if client.client_id == id { *areaclient = None; } } } } 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 type LobbyId = usize; pub type RoomId = usize; pub type Lobby = InnerClientArea<12>; pub type Room = InnerClientArea<4>; pub struct ClientLocation { lobbies: [Lobby; 15], rooms: [Option; 128], } trait ClientArea<'a> { fn clients(&'a self) -> std::slice::Iter<'_, Option>; } impl<'a> ClientArea<'a> for Lobby { fn clients(&'a self) -> std::slice::Iter<'_, Option> { self.clients.iter() } } impl<'a> ClientArea<'a> for Room { fn clients(&'a self) -> std::slice::Iter<'_, Option> { self.clients.iter() } } #[derive(Debug)] pub struct ClientAtLocation { pub client_id: ClientId, pub index: usize, } pub struct Area<'a> { area: &'a dyn ClientArea<'a>, index: usize, } impl<'a> Area<'a> { fn new(area: &'a dyn ClientArea<'a>, index: usize) -> Area<'a> { Area { area: area, index: index, } } pub fn clients(&'a self) -> Vec { self.area.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.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 id(&self) -> usize { self.index } } #[derive(Debug)] pub enum JoinRoomError { RoomDoesNotExist, RoomFull, } #[derive(Debug)] pub enum JoinLobbyError { LobbyDoesNotExist, LobbyFull, } impl ClientLocation { pub fn new() -> ClientLocation { ClientLocation { lobbies: [Lobby::new(); 15], rooms: [None; 128], } } pub fn add_to_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result { self.lobbies.get_mut(lobby) .ok_or(JoinLobbyError::LobbyDoesNotExist)? .add(id) .ok_or(JoinLobbyError::LobbyFull) } pub fn add_to_room(&mut self, id: ClientId, room: RoomId) -> Result { self.rooms.get_mut(room) .ok_or(JoinRoomError::RoomDoesNotExist)? .as_mut() .ok_or(JoinRoomError::RoomDoesNotExist)? .add(id) .ok_or(JoinRoomError::RoomFull) } pub fn get_area_by_user(&self, id: ClientId) -> Area { for (i, lobby) in self.lobbies.iter().enumerate() { if lobby.contains(id) { return Area::new(lobby, i); } } for (i, room) in self.rooms.iter().enumerate() { if let Some(room) = room { if room.contains(id){ return Area::new(room, i); } } } panic!("client is not in a room/lobby") } }