#![allow(dead_code, unused_must_use)] use std::collections::HashMap; use std::time::SystemTime; use thiserror::Error; use crate::common::serverstate::ClientId; pub const MAX_ROOMS: usize = 128; pub enum AreaType { Room, Lobby, } #[derive(Debug, Copy, Clone, PartialEq)] pub struct LobbyId(pub usize); #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] pub struct RoomId(pub usize); impl LobbyId { pub fn id(&self) -> u8 { self.0 as u8 } } #[derive(Error, Debug, PartialEq)] #[error("")] pub enum CreateRoomError { NoOpenSlots, ClientInAreaAlready, JoinError, } #[derive(Error, Debug, PartialEq)] #[error("")] pub enum JoinRoomError { RoomDoesNotExist, RoomFull, ClientInAreaAlready, } #[derive(Error, Debug, PartialEq)] #[error("")] pub enum JoinLobbyError { LobbyDoesNotExist, LobbyFull, ClientInAreaAlready, } #[derive(Error, Debug, PartialEq)] #[error("")] pub enum GetAreaError { NotInRoom, NotInLobby, InvalidClient, } #[derive(Error, Debug, PartialEq)] #[error("")] pub enum ClientRemovalError { ClientNotInArea, InvalidArea, } #[derive(Error, Debug, PartialEq)] #[error("")] pub enum GetClientsError { InvalidClient, InvalidArea, } #[derive(Error, Debug, PartialEq)] #[error("")] pub enum GetNeighborError { InvalidClient, InvalidArea, } #[derive(Error, Debug, PartialEq)] #[error("")] pub enum GetLeaderError { InvalidClient, InvalidArea, NoClientInArea, } #[derive(Error, Debug, PartialEq)] #[error("")] pub enum ClientLocationError { CreateRoomError(#[from] CreateRoomError), JoinRoomError(#[from] JoinRoomError), JoinLobbyError(#[from] JoinLobbyError), GetAreaError(#[from] GetAreaError), ClientRemovalError(#[from] ClientRemovalError), GetClientsError(#[from] GetClientsError), GetNeighborError(#[from] GetNeighborError), GetLeaderError(#[from] GetLeaderError) } #[derive(Debug, Copy, Clone, PartialEq)] pub struct LocalClientId(usize); impl LocalClientId { pub fn id(&self) -> u8 { self.0 as u8 } } #[derive(Debug, Copy, Clone, PartialEq)] pub struct AreaClient { pub client: ClientId, pub local_client: LocalClientId, time_join: SystemTime, } #[derive(Debug, Copy, Clone, PartialEq)] struct Lobby([Option; 12]); #[derive(Debug, Copy, Clone, PartialEq)] struct Room([Option; 4]); #[derive(Debug, Copy, Clone, PartialEq)] pub enum RoomLobby { Room(RoomId), Lobby(LobbyId), } pub struct ClientLocation { lobbies: [Lobby; 15], rooms: [Option; MAX_ROOMS], client_location: HashMap, } impl Default for ClientLocation { fn default() -> ClientLocation { ClientLocation { lobbies: [Lobby([None; 12]); 15], rooms: [None; MAX_ROOMS], client_location: HashMap::new(), } } } impl ClientLocation { pub fn add_client_to_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result<(), JoinLobbyError> { let l = self.lobbies.get_mut(lobby.0).ok_or(JoinLobbyError::LobbyDoesNotExist)?; let (index, empty_slot) = l.0.iter_mut() .enumerate() .filter(|(_, k)| k.is_none()) .nth(0) .ok_or(JoinLobbyError::LobbyFull)?; *empty_slot = Some(AreaClient { client: id, local_client: LocalClientId(index), time_join: SystemTime::now(), }); self.remove_client_from_area(id); self.client_location.insert(id, RoomLobby::Lobby(lobby)); Ok(()) } pub fn add_client_to_next_available_lobby(&mut self, id: ClientId, lobby: LobbyId) -> Result { let l = (0..15) .map(|lobby_index| { let new_lobby = LobbyId((lobby.0 + lobby_index) % 15); (new_lobby, self.add_client_to_lobby(id, new_lobby)) }) .filter(|(_, lobby_option)| { lobby_option.is_ok() }) .nth(0) .ok_or(JoinLobbyError::LobbyFull)?; Ok(l.0) } pub fn create_new_room(&mut self, id: ClientId) -> Result { let (index, empty_slot) = self.rooms.iter_mut() .enumerate() .filter(|(_, r)| r.is_none()) .nth(0) .ok_or(CreateRoomError::NoOpenSlots)?; *empty_slot = Some(Room([None; 4])); self.add_client_to_room(id, RoomId(index)).map_err(|_err| CreateRoomError::JoinError)?; Ok(RoomId(index)) } pub fn add_client_to_room(&mut self, id: ClientId, room: RoomId) -> Result<(), JoinRoomError> { let r = self.rooms.get_mut(room.0) .ok_or(JoinRoomError::RoomDoesNotExist)? .as_mut() .ok_or(JoinRoomError::RoomDoesNotExist)?; let (index, empty_slot) = r.0.iter_mut() .enumerate() .filter(|(_, k)| k.is_none()) .nth(0) .ok_or(JoinRoomError::RoomFull)?; *empty_slot = Some(AreaClient { client: id, local_client: LocalClientId(index), time_join: SystemTime::now(), }); self.remove_client_from_area(id); self.client_location.insert(id, RoomLobby::Room(room)); Ok(()) } pub fn get_all_clients_by_client(&self, id: ClientId) -> Result, GetNeighborError> { let area = self.client_location.get(&id).ok_or(GetNeighborError::InvalidClient)?; match area { RoomLobby::Room(room) => { Ok(self.get_clients_in_room(*room).map_err(|_| GetNeighborError::InvalidArea)? .into_iter() .collect()) }, RoomLobby::Lobby(lobby) => { Ok(self.get_clients_in_lobby(*lobby).map_err(|_| GetNeighborError::InvalidArea)? .into_iter() .collect()) } } } pub fn get_client_neighbors(&self, id: ClientId) -> Result, GetNeighborError> { let area = self.client_location.get(&id).ok_or(GetNeighborError::InvalidClient)?; match area { RoomLobby::Room(room) => { Ok(self.get_clients_in_room(*room).map_err(|_| GetNeighborError::InvalidArea)? .into_iter() .filter(|c| c.client != id) .collect()) }, RoomLobby::Lobby(lobby) => { Ok(self.get_clients_in_lobby(*lobby).map_err(|_| GetNeighborError::InvalidArea)? .into_iter() .filter(|c| c.client != id) .collect()) } } } pub fn get_room_leader(&self, room: RoomId) -> Result { let mut r = self.rooms[room.0] .as_ref() .ok_or(GetLeaderError::InvalidArea)? .0.iter().flat_map(|k| k) .collect::>(); r.sort_by_key(|k| k.time_join); let c = r.get(0).ok_or(GetLeaderError::NoClientInArea)?; Ok(**c) } pub fn get_lobby_leader(&self, lobby: LobbyId) -> Result { let mut l = self.lobbies[lobby.0] .0.iter().flat_map(|k| k) .collect::>(); l.sort_by_key(|k| k.time_join); let c = l.get(0).ok_or(GetLeaderError::NoClientInArea)?; Ok(**c) } pub fn get_area_leader(&self, roomlobby: RoomLobby) -> Result { match roomlobby { RoomLobby::Room(room) => { self.get_room_leader(room) }, RoomLobby::Lobby(lobby) => { self.get_lobby_leader(lobby) } } } pub fn get_leader_by_client(&self, id: ClientId) -> Result { let area = self.client_location.get(&id).ok_or(GetLeaderError::InvalidClient)?; match area { RoomLobby::Room(room) => { self.get_room_leader(*room) }, RoomLobby::Lobby(lobby) => { self.get_lobby_leader(*lobby) } } } pub fn get_clients_in_lobby(&self, lobby: LobbyId) -> Result, GetClientsError> { Ok(self.lobbies.get(lobby.0).ok_or(GetClientsError::InvalidArea)?.0 .iter() .filter_map(|client| { client.map(|c| { c }) }).collect()) } pub fn get_clients_in_room(&self, room: RoomId) -> Result, GetClientsError> { Ok(self.rooms.get(room.0) .ok_or(GetClientsError::InvalidArea)? .ok_or(GetClientsError::InvalidArea)?.0 .iter() .filter_map(|client| { client.map(|c| { c }) }).collect()) } pub fn get_local_client(&self, id: ClientId) -> Result { let area = self.client_location.get(&id).ok_or(GetClientsError::InvalidClient)?; match area { RoomLobby::Room(room) => { self.get_clients_in_room(*room).map_err(|_| GetClientsError::InvalidArea)? .into_iter() .filter(|c| c.client == id) .nth(0) .ok_or(GetClientsError::InvalidClient) }, RoomLobby::Lobby(lobby) => { self.get_clients_in_lobby(*lobby).map_err(|_| GetClientsError::InvalidArea)? .into_iter() .filter(|c| c.client == id) .nth(0) .ok_or(GetClientsError::InvalidClient) } } } pub fn get_area(&self, id: ClientId) -> Result { self.client_location.get(&id) .ok_or(GetAreaError::InvalidClient) .map(Clone::clone) } pub fn get_room(&self, id: ClientId) -> Result { if let RoomLobby::Room(room) = self.client_location.get(&id).ok_or(GetAreaError::InvalidClient)? { Ok(*room) } else { Err(GetAreaError::NotInRoom) } } pub fn get_lobby(&self, id: ClientId) -> Result { if let RoomLobby::Lobby(lobby) = self.client_location.get(&id).ok_or(GetAreaError::InvalidClient)? { Ok(*lobby) } else { Err(GetAreaError::NotInLobby) } } pub fn remove_client_from_area(&mut self, id: ClientId) -> Result<(), ClientRemovalError> { let area = self.client_location.get_mut(&id).ok_or(ClientRemovalError::ClientNotInArea)?; let client_list = match area { RoomLobby::Room(room) => { self.rooms[room.0].as_mut().map_or(None, |r| { Some(r.0.iter_mut()) }) }, RoomLobby::Lobby(lobby) => { Some(self.lobbies[lobby.0].0.iter_mut()) } }; client_list .ok_or(ClientRemovalError::InvalidArea)? .filter(|client| { client.map_or(false, |c| { c.client == id }) }) .for_each(|client| { *client = None }); Ok(()) } } #[cfg(test)] mod test { use super::*; #[test] fn test_add_client_to_lobby() { let mut cl = ClientLocation::default(); cl.add_client_to_lobby(ClientId(12), LobbyId(0)); cl.add_client_to_lobby(ClientId(13), LobbyId(1)); cl.add_client_to_lobby(ClientId(14), LobbyId(0)); assert!(cl.get_clients_in_lobby(LobbyId(0)).into_iter().flatten().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(12), LocalClientId(0)), (ClientId(14), LocalClientId(1)), ]); } #[test] fn test_add_client_to_full_lobby() { let mut cl = ClientLocation::default(); (0..12).for_each(|i| { cl.add_client_to_lobby(ClientId(i), LobbyId(0)); }); assert!(cl.add_client_to_lobby(ClientId(99), LobbyId(0)) == Err(JoinLobbyError::LobbyFull)); } #[test] fn test_add_client_to_next_available_lobby() { let mut cl = ClientLocation::default(); (1..4).for_each(|lobby| { (0..12).for_each(|i| { cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)); }); }); assert!(cl.add_client_to_next_available_lobby(ClientId(99), LobbyId(1)) == Ok(LobbyId(4))); } #[test] fn test_add_to_lobby_when_all_are_full() { let mut cl = ClientLocation::default(); (0..15).for_each(|lobby| { (0..12).for_each(|i| { cl.add_client_to_lobby(ClientId(lobby*12+i), LobbyId(lobby)); }); }); assert!(cl.add_client_to_next_available_lobby(ClientId(99), LobbyId(1)) == Err(JoinLobbyError::LobbyFull)); } #[test] fn test_new_room() { let mut cl = ClientLocation::default(); assert!(cl.create_new_room(ClientId(12)) == Ok(RoomId(0))); } #[test] fn test_add_client_to_room() { let mut cl = ClientLocation::default(); let room = cl.create_new_room(ClientId(12)).unwrap(); assert!(cl.add_client_to_room(ClientId(234), room) == Ok(())); assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(12), LocalClientId(0)), (ClientId(234), LocalClientId(1)), ]); } #[test] fn test_no_new_room_slots() { let mut cl = ClientLocation::default(); for i in 0..128 { cl.create_new_room(ClientId(i)); } assert!(cl.create_new_room(ClientId(234)) == Err(CreateRoomError::NoOpenSlots)); } #[test] fn test_joining_full_room() { let mut cl = ClientLocation::default(); let room = cl.create_new_room(ClientId(0)).unwrap(); assert!(cl.add_client_to_room(ClientId(1), room) == Ok(())); assert!(cl.add_client_to_room(ClientId(2), room) == Ok(())); assert!(cl.add_client_to_room(ClientId(3), room) == Ok(())); assert!(cl.add_client_to_room(ClientId(234), room) == Err(JoinRoomError::RoomFull)); } #[test] fn test_adding_client_to_room_removes_from_lobby() { let mut cl = ClientLocation::default(); cl.add_client_to_lobby(ClientId(93), LobbyId(0)); cl.add_client_to_lobby(ClientId(23), LobbyId(0)); cl.add_client_to_lobby(ClientId(51), LobbyId(0)); cl.add_client_to_lobby(ClientId(12), LobbyId(0)); let room = cl.create_new_room(ClientId(51)).unwrap(); assert!(cl.add_client_to_room(ClientId(93), room) == Ok(())); assert!(cl.get_clients_in_lobby(LobbyId(0)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(23), LocalClientId(1)), (ClientId(12), LocalClientId(3)), ]); assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(51), LocalClientId(0)), (ClientId(93), LocalClientId(1)), ]); } #[test] fn test_getting_neighbors() { let mut cl = ClientLocation::default(); cl.add_client_to_lobby(ClientId(93), LobbyId(0)); cl.add_client_to_lobby(ClientId(23), LobbyId(0)); cl.add_client_to_lobby(ClientId(51), LobbyId(0)); cl.add_client_to_lobby(ClientId(12), LobbyId(0)); assert!(cl.get_client_neighbors(ClientId(23)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(93), LocalClientId(0)), (ClientId(51), LocalClientId(2)), (ClientId(12), LocalClientId(3)), ]); } #[test] fn test_failing_to_join_lobby_does_not_remove_from_current_area() { let mut cl = ClientLocation::default(); (0..12).for_each(|i| { cl.add_client_to_lobby(ClientId(i), LobbyId(0)); }); cl.add_client_to_lobby(ClientId(99), LobbyId(1)); cl.add_client_to_lobby(ClientId(99), LobbyId(0)); assert!(cl.get_clients_in_lobby(LobbyId(0)).unwrap().len() == 12); assert!(cl.get_clients_in_lobby(LobbyId(1)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(99), LocalClientId(0)), ]); } #[test] fn test_get_leader() { let mut cl = ClientLocation::default(); cl.add_client_to_lobby(ClientId(93), LobbyId(0)); cl.add_client_to_lobby(ClientId(23), LobbyId(0)); cl.add_client_to_lobby(ClientId(51), LobbyId(0)); cl.add_client_to_lobby(ClientId(12), LobbyId(0)); assert!(cl.get_leader_by_client(ClientId(51)).map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(0)))); } #[test] fn test_remove_client_from_room() { let mut cl = ClientLocation::default(); let room = cl.create_new_room(ClientId(51)).unwrap(); cl.add_client_to_room(ClientId(93), room); cl.add_client_to_room(ClientId(23), room); cl.remove_client_from_area(ClientId(51)); cl.add_client_to_room(ClientId(12), room); assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::>() == vec![ (ClientId(12), LocalClientId(0)), (ClientId(93), LocalClientId(1)), (ClientId(23), LocalClientId(2)), ]); } #[test] fn test_leader_changes_on_leader_leaving() { let mut cl = ClientLocation::default(); let room = cl.create_new_room(ClientId(51)).unwrap(); cl.add_client_to_room(ClientId(93), room); cl.add_client_to_room(ClientId(23), room); cl.remove_client_from_area(ClientId(51)); cl.add_client_to_room(ClientId(12), room); cl.remove_client_from_area(ClientId(23)); cl.add_client_to_room(ClientId(99), room); assert!(cl.get_leader_by_client(ClientId(12)).map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(1)))); } }