aboutsummaryrefslogtreecommitdiff
path: root/games/rstnode/rst-core/src/lobby.rs
diff options
context:
space:
mode:
Diffstat (limited to 'games/rstnode/rst-core/src/lobby.rs')
-rw-r--r--games/rstnode/rst-core/src/lobby.rs191
1 files changed, 191 insertions, 0 deletions
diff --git a/games/rstnode/rst-core/src/lobby.rs b/games/rstnode/rst-core/src/lobby.rs
new file mode 100644
index 000000000000..496f98bd7b6f
--- /dev/null
+++ b/games/rstnode/rst-core/src/lobby.rs
@@ -0,0 +1,191 @@
+//! 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(()),
+ }
+ }
+}