elseware/src/ship/location.rs

520 lines
17 KiB
Rust
Raw Normal View History

2020-04-05 22:56:48 -07:00
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
2020-04-06 23:40:39 -07:00
use std::convert::Into;
2019-12-15 23:18:21 -08:00
use std::time::SystemTime;
2019-11-23 19:01:03 -08:00
use crate::common::serverstate::ClientId;
2020-01-02 20:24:17 -08:00
pub const MAX_ROOMS: usize = 128;
2020-04-06 23:40:39 -07:00
pub enum AreaType {
Room,
Lobby,
2019-12-15 23:18:21 -08:00
}
2019-11-23 19:01:03 -08:00
2020-04-05 22:56:48 -07:00
#[derive(Debug, Copy, Clone, PartialEq)]
2020-01-02 20:24:17 -08:00
pub struct LobbyId(pub usize);
2020-04-05 22:56:48 -07:00
#[derive(Debug, Copy, Clone, PartialEq)]
2020-01-02 20:24:17 -08:00
pub struct RoomId(pub usize);
2019-11-23 19:01:03 -08:00
2020-04-06 23:40:39 -07:00
impl LobbyId {
pub fn id(&self) -> u8 {
self.0 as u8
}
2019-12-15 23:18:21 -08:00
}
2020-04-05 22:56:48 -07:00
#[derive(Debug, PartialEq)]
2020-01-02 20:24:17 -08:00
pub enum CreateRoomError {
NoOpenSlots,
ClientInAreaAlready,
2020-04-05 22:56:48 -07:00
JoinError,
2020-01-02 20:24:17 -08:00
}
2020-04-05 22:56:48 -07:00
#[derive(Debug, PartialEq)]
pub enum JoinRoomError {
2019-11-23 19:01:03 -08:00
RoomDoesNotExist,
RoomFull,
2020-01-02 20:24:17 -08:00
ClientInAreaAlready,
2019-11-23 19:01:03 -08:00
}
2020-04-05 22:56:48 -07:00
#[derive(Debug, PartialEq)]
pub enum JoinLobbyError {
2019-11-23 19:01:03 -08:00
LobbyDoesNotExist,
LobbyFull,
2020-01-02 20:24:17 -08:00
ClientInAreaAlready,
}
2020-04-05 22:56:48 -07:00
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct LocalClientId(usize);
2020-04-06 23:40:39 -07:00
impl LocalClientId {
pub fn id(&self) -> u8 {
self.0 as u8
}
2020-04-05 22:56:48 -07:00
}
#[derive(Debug, Copy, Clone, PartialEq)]
2020-04-06 23:40:39 -07:00
pub struct AreaClient {
pub client: ClientId,
pub local_client: LocalClientId,
2020-04-05 22:56:48 -07:00
time_join: SystemTime,
}
#[derive(Debug, Copy, Clone, PartialEq)]
2020-04-06 23:40:39 -07:00
struct Lobby([Option<AreaClient>; 12]);
2020-04-05 22:56:48 -07:00
#[derive(Debug, Copy, Clone, PartialEq)]
2020-04-06 23:40:39 -07:00
struct Room([Option<AreaClient>; 4]);
2020-04-05 22:56:48 -07:00
#[derive(Debug, Copy, Clone, PartialEq)]
2020-04-06 23:40:39 -07:00
pub enum RoomLobby {
2020-04-05 22:56:48 -07:00
Room(RoomId),
Lobby(LobbyId),
}
2020-04-06 23:40:39 -07:00
#[derive(Debug, PartialEq)]
pub enum GetAreaError {
InvalidClient,
}
2020-04-05 22:56:48 -07:00
#[derive(Debug, PartialEq)]
pub enum ClientRemovalError {
ClientNotInArea,
InvalidArea,
}
#[derive(Debug, PartialEq)]
pub enum GetClientsError {
2020-04-06 23:40:39 -07:00
InvalidClient,
2020-04-05 22:56:48 -07:00
InvalidArea,
}
#[derive(Debug, PartialEq)]
pub enum GetNeighborError {
InvalidClient,
InvalidArea,
}
#[derive(Debug, PartialEq)]
pub enum GetLeaderError {
InvalidClient,
InvalidArea,
NoClientInArea,
}
2020-04-06 23:40:39 -07:00
pub struct ClientLocation {
lobbies: [Lobby; 15],
rooms: [Option<Room>; MAX_ROOMS],
2020-04-05 22:56:48 -07:00
client_location: HashMap<ClientId, RoomLobby>,
}
2020-04-06 23:40:39 -07:00
impl ClientLocation {
pub fn new() -> ClientLocation {
ClientLocation {
lobbies: [Lobby([None; 12]); 15],
2020-04-05 22:56:48 -07:00
rooms: [None; MAX_ROOMS],
client_location: HashMap::new(),
}
}
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)?;
2020-04-06 23:40:39 -07:00
*empty_slot = Some(AreaClient {
2020-04-05 22:56:48 -07:00
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<LobbyId, JoinLobbyError> {
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)
}
2020-04-06 23:40:39 -07:00
pub fn create_new_room(&mut self, id: ClientId) -> Result<RoomId, CreateRoomError> {
2020-04-05 22:56:48 -07:00
let (index, empty_slot) = self.rooms.iter_mut()
.enumerate()
.filter(|(_, r)| r.is_none())
.nth(0)
.ok_or(CreateRoomError::NoOpenSlots)?;
2020-04-06 23:40:39 -07:00
*empty_slot = Some(Room([None; 4]));
2020-04-05 22:56:48 -07:00
self.add_client_to_room(id, RoomId(index)).map_err(|err| CreateRoomError::JoinError)?;
Ok(RoomId(index))
}
2020-04-06 23:40:39 -07:00
pub fn add_client_to_room(&mut self, id: ClientId, room: RoomId) -> Result<(), JoinRoomError> {
2020-04-05 22:56:48 -07:00
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)?;
2020-04-06 23:40:39 -07:00
*empty_slot = Some(AreaClient {
2020-04-05 22:56:48 -07:00
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(())
}
2020-04-06 23:40:39 -07:00
pub fn get_all_clients_by_client(&self, id: ClientId) -> Result<Vec<AreaClient>, GetNeighborError> {
2020-04-05 22:56:48 -07:00
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)?
2020-04-06 23:40:39 -07:00
.into_iter()
.collect())
2020-04-05 22:56:48 -07:00
},
RoomLobby::Lobby(lobby) => {
Ok(self.get_clients_in_lobby(*lobby).map_err(|_| GetNeighborError::InvalidArea)?
2020-04-06 23:40:39 -07:00
.into_iter()
.collect())
2020-04-05 22:56:48 -07:00
}
}
}
2020-04-06 23:40:39 -07:00
pub fn get_client_neighbors(&self, id: ClientId) -> Result<Vec<AreaClient>, 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<AreaClient, GetLeaderError> {
let mut r = self.rooms[room.0]
.as_ref()
.ok_or(GetLeaderError::InvalidArea)?
.0.iter().flat_map(|k| k)
.collect::<Vec<_>>();
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<AreaClient, GetLeaderError> {
let mut l = self.lobbies[lobby.0]
.0.iter().flat_map(|k| k)
.collect::<Vec<_>>();
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<AreaClient, GetLeaderError> {
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<AreaClient, GetLeaderError> {
2020-04-05 22:56:48 -07:00
let area = self.client_location.get(&id).ok_or(GetLeaderError::InvalidClient)?;
match area {
RoomLobby::Room(room) => {
2020-04-06 23:40:39 -07:00
self.get_room_leader(*room)
2020-04-05 22:56:48 -07:00
},
RoomLobby::Lobby(lobby) => {
2020-04-06 23:40:39 -07:00
self.get_lobby_leader(*lobby)
2020-04-05 22:56:48 -07:00
}
}
}
2020-04-06 23:40:39 -07:00
pub fn get_clients_in_lobby(&self, lobby: LobbyId) -> Result<Vec<AreaClient>, GetClientsError> {
2020-04-05 22:56:48 -07:00
Ok(self.lobbies.get(lobby.0).ok_or(GetClientsError::InvalidArea)?.0
.iter()
2020-04-06 23:40:39 -07:00
.filter_map(|client| {
client.map(|c| {
c
})
}).collect())
2020-04-05 22:56:48 -07:00
}
2020-04-06 23:40:39 -07:00
pub fn get_clients_in_room(&self, room: RoomId) -> Result<Vec<AreaClient>, GetClientsError> {
2020-04-05 22:56:48 -07:00
Ok(self.rooms.get(room.0)
.ok_or(GetClientsError::InvalidArea)?
.ok_or(GetClientsError::InvalidArea)?.0
.iter()
2020-04-06 23:40:39 -07:00
.filter_map(|client| {
client.map(|c| {
c
})
}).collect())
}
pub fn get_local_client(&self, id: ClientId) -> Result<AreaClient, GetClientsError> {
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<RoomLobby, GetAreaError> {
self.client_location.get(&id)
.ok_or(GetAreaError::InvalidClient)
.map(Clone::clone)
2020-04-05 22:56:48 -07:00
}
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() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
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));
2020-04-06 23:40:39 -07:00
assert!(cl.get_clients_in_lobby(LobbyId(0)).into_iter().flatten().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
2020-04-05 22:56:48 -07:00
(ClientId(12), LocalClientId(0)),
(ClientId(14), LocalClientId(1)),
2020-04-06 23:40:39 -07:00
]);
2020-04-05 22:56:48 -07:00
}
#[test]
fn test_add_client_to_full_lobby() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
(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() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
(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() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
(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() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
assert!(cl.create_new_room(ClientId(12)) == Ok(RoomId(0)));
}
#[test]
fn test_add_client_to_room() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
let room = cl.create_new_room(ClientId(12)).unwrap();
assert!(cl.add_client_to_room(ClientId(234), room) == Ok(()));
2020-04-06 23:40:39 -07:00
assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
2020-04-05 22:56:48 -07:00
(ClientId(12), LocalClientId(0)),
(ClientId(234), LocalClientId(1)),
2020-04-06 23:40:39 -07:00
]);
2020-04-05 22:56:48 -07:00
}
#[test]
fn test_no_new_room_slots() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
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() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
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() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
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(()));
2020-04-06 23:40:39 -07:00
assert!(cl.get_clients_in_lobby(LobbyId(0)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
2020-04-05 22:56:48 -07:00
(ClientId(23), LocalClientId(1)),
(ClientId(12), LocalClientId(3)),
2020-04-06 23:40:39 -07:00
]);
assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
2020-04-05 22:56:48 -07:00
(ClientId(51), LocalClientId(0)),
(ClientId(93), LocalClientId(1)),
2020-04-06 23:40:39 -07:00
]);
2020-04-05 22:56:48 -07:00
}
#[test]
fn test_getting_neighbors() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
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));
2020-04-06 23:40:39 -07:00
assert!(cl.get_client_neighbors(ClientId(23)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
2020-04-05 22:56:48 -07:00
(ClientId(93), LocalClientId(0)),
(ClientId(51), LocalClientId(2)),
(ClientId(12), LocalClientId(3)),
2020-04-06 23:40:39 -07:00
]);
2020-04-05 22:56:48 -07:00
}
#[test]
fn test_failing_to_join_lobby_does_not_remove_from_current_area() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
(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);
2020-04-06 23:40:39 -07:00
assert!(cl.get_clients_in_lobby(LobbyId(1)).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
2020-04-05 22:56:48 -07:00
(ClientId(99), LocalClientId(0)),
2020-04-06 23:40:39 -07:00
]);
2020-04-05 22:56:48 -07:00
}
#[test]
2020-04-06 23:40:39 -07:00
fn test_get_leader() {
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
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));
2020-04-06 23:40:39 -07:00
assert!(cl.get_leader_by_client(ClientId(51)).map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(0))));
2020-04-05 22:56:48 -07:00
}
#[test]
fn test_remove_client_from_room() {
2020-04-06 23:40:39 -07:00
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
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);
2020-04-06 23:40:39 -07:00
assert!(cl.get_clients_in_room(room).unwrap().into_iter().map(|c| (c.client, c.local_client)).collect::<Vec<_>>() == vec![
2020-04-05 22:56:48 -07:00
(ClientId(12), LocalClientId(0)),
(ClientId(93), LocalClientId(1)),
(ClientId(23), LocalClientId(2)),
2020-04-06 23:40:39 -07:00
]);
2020-04-05 22:56:48 -07:00
}
#[test]
2020-04-06 23:40:39 -07:00
fn test_leader_changes_on_leader_leaving() {
let mut cl = ClientLocation::new();
2020-04-05 22:56:48 -07:00
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);
2020-04-06 23:40:39 -07:00
assert!(cl.get_leader_by_client(ClientId(12)).map(|c| (c.client, c.local_client)) == Ok((ClientId(93), LocalClientId(1))));
2020-04-05 22:56:48 -07:00
}
}