My personal project and infrastructure archive
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nomicon/games/rstnode/rst-core/src/lobby.rs

191 lines
4.8 KiB

//! The code that handles the lobby logic
use crate::{
data::{Color, ColorPalette},
users::MetaUser,
wire::{Lobby, LobbyErr, LobbyId, LobbyUpdate, LobbyUser, User, UserId},
};
use async_std::sync::{Arc, RwLock};
use std::{
collections::BTreeMap,
sync::atomic::{AtomicUsize, Ordering},
};
/// A list of all the lobbies on the server
pub struct LobbyList {
max: AtomicUsize,
lobbies: RwLock<BTreeMap<LobbyId, MetaLobby>>,
}
impl LobbyList {
pub fn new() -> Self {
Self {
max: 0.into(),
lobbies: Default::default(),
}
}
/// Create a new lobby
pub async fn create(&self, map: String) -> LobbyId {
let id = self.max.fetch_add(1, Ordering::Relaxed);
self.lobbies
.write()
.await
.insert(id, MetaLobby::create(id, map));
id
}
/// Remove a lobby by ID
pub async fn destroy(&self, id: LobbyId) -> Result<(), LobbyErr> {
self.consume(id).await.map(|_| ())
}
/// Remove and return the lobby
pub async fn consume(&self, id: LobbyId) -> Result<MetaLobby, LobbyErr> {
self.lobbies
.write()
.await
.remove(&id)
.map_or(Err(LobbyErr::NoSuchRoom), |l| Ok(l))
}
/// Get mutable access to a lobby
pub async fn get_mut<F, T>(&self, id: LobbyId, cb: F) -> Result<T, LobbyErr>
where
F: Fn(&mut MetaLobby) -> T,
{
self.lobbies
.write()
.await
.get_mut(&id)
.map_or(Err(LobbyErr::OtherError), |ref mut l| Ok(cb(l)))
}
}
/// Additional state held by the server
///
/// The meta lobby will also sync updates to all connected users, when updates are made to the lobby
pub struct MetaLobby {
pub palette: Vec<Color>,
pub inner: Lobby,
}
impl MetaLobby {
pub fn create(id: LobbyId, map: String) -> Self {
Self {
palette: Vec::palette(),
inner: Lobby {
id,
map,
players: vec![],
settings: vec![],
},
}
}
pub fn join(&mut self, user: &MetaUser) -> Lobby {
let color = if &user.name == "spacekookie" {
let color = Color::blue();
self.palette.without(&color);
if let Some(user) = self
.inner
.players
.iter_mut()
.find(|u| u.color == Color::blue())
{
user.color = self.palette.remove(0);
}
color
} else {
self.palette.remove(0)
};
self.inner.players.push(LobbyUser {
admin: false,
id: user.id,
name: user.name.clone(),
ready: false,
color,
});
self.inner.clone()
}
pub fn leave(&mut self, user: &MetaUser) {
let (pos, user) = self
.inner
.players
.iter()
.enumerate()
.find_map(|(num, u)| {
if u.id == user.id {
Some((num, u))
} else {
None
}
})
.unwrap();
self.palette.remix(user.color);
self.inner.players.remove(pos);
}
/// Check if a user is even present in a lobby
///
/// Perform this prerequisite check before making other user-specific changes to the lobby
pub fn in_lobby(&self, user: UserId) -> bool {
self.inner
.players
.iter()
.find(|u| u.id == user)
.map(|_| true)
.unwrap_or(false)
}
/// Set the ready state for a user
pub fn ready(&mut self, user: User, ready: bool) -> LobbyUpdate {
if let Some(user) = self
.inner
.players
.iter_mut()
.find(|u| u.id == user.id)
.as_mut()
{
user.ready = ready;
}
LobbyUpdate::Ready(
self.inner
.players
.iter()
.filter_map(|u| if u.ready { Some(u.id) } else { None })
.collect(),
)
}
/// Try to start a game, if the user can and everybody is ready
pub fn start(&mut self, user: UserId) -> Result<(), LobbyErr> {
if let Some(_) = self
.inner
.players
.iter()
.filter(|u| u.admin)
.find(|u| u.id == user)
{
return Err(LobbyErr::NotAuthorized);
};
match self
.inner
.players
.iter()
.filter(|u| !u.ready)
.collect::<Vec<_>>()
.len()
{
0 => Err(LobbyErr::NotAllReady),
_ => Ok(()),
}
}
}