From 234f101a65993d17d9660b16066314b5b4abde59 Mon Sep 17 00:00:00 2001 From: ThePerkinrex Date: Sun, 13 Dec 2020 19:47:37 +0100 Subject: [PATCH] Add more helper features for the game server --- server/src/db.rs | 14 ++++++++++++++ server/src/games/mod.rs | 3 ++- server/src/games/run.rs | 25 +++++++++++++++++++++---- server/src/games/run/functions.rs | 1 + server/src/server.rs | 5 +++-- server/src/server/connection.rs | 10 ++++++++-- server/src/server/lobby.rs | 18 ++++++++++-------- server/src/server/votes.rs | 27 +++++++++++++++++++++++++-- 8 files changed, 84 insertions(+), 19 deletions(-) diff --git a/server/src/db.rs b/server/src/db.rs index c561292..5278f4c 100644 --- a/server/src/db.rs +++ b/server/src/db.rs @@ -82,6 +82,20 @@ impl Db { .and_then(|r| r.map(|r| r.get(0)).collect()) .unwrap() } + + // FIXME: return Results intead of crashing + pub fn get_uuids_in_lobby_where_user_is(&mut self, uuid: Uuid) -> Vec { + let mut prepared = self.conn.prepare_cached("SELECT UUID FROM Users WHERE UUID in (SELECT UserId FROM UsersInLobbies WHERE LobbyId = (SELECT LobbyId FROM UsersInLobbies WHERE UserId = ?))").unwrap(); + prepared + .query(params![uuid.as_bytes().to_vec()]) + .and_then(|r| { + r.map(|r| r.get::<_, Vec>(0)) + .map(|x| Ok(Uuid::from_slice(&x).unwrap())) + .collect() + }) + .unwrap() + } + // FIXME: return Results intead of crashing pub fn create_lobby(&mut self, public: bool) -> u32 { let id = rand::random(); diff --git a/server/src/games/mod.rs b/server/src/games/mod.rs index 617cdbb..f4daf9e 100644 --- a/server/src/games/mod.rs +++ b/server/src/games/mod.rs @@ -4,6 +4,7 @@ mod config; mod run; pub use run::{CardId, CardIdx, PileKind, RunningGame}; +use uuid::Uuid; #[derive(Clone)] pub struct Game { @@ -44,7 +45,7 @@ impl Game { // self.authors.clone() // } - pub fn run(&self, players: u32) -> run::RunningGame { + pub fn run(&self, players: &[Uuid]) -> run::RunningGame { // let ast = rhai::Engine::new().compile_file(self.folder.join(&self.conf.script)).unwrap(); run::RunningGame::new( self.name.clone(), diff --git a/server/src/games/run.rs b/server/src/games/run.rs index 90f604e..4dd8982 100644 --- a/server/src/games/run.rs +++ b/server/src/games/run.rs @@ -2,6 +2,7 @@ use rhai::{ serde::{from_dynamic, to_dynamic}, Dynamic, Func, AST, }; +use uuid::Uuid; use std::collections::HashMap; @@ -22,6 +23,7 @@ pub struct RunningGame { functions: Functions, current_player: Player, data: HashMap, + players: HashMap, } // TODO add errors @@ -40,30 +42,45 @@ impl RunningGame { Ok((ast, fns)) } - pub fn new(name: String, ast: AST, fns: &[String], conf: &Config, players: u32) -> Self { + pub fn new( + name: String, + ast: AST, + fns: &[String], + conf: &Config, + current_players: &[Uuid], + ) -> Self { // log::info!("Fns: {:?}", fns); let functions = Functions::new(fns, ast.clone(), &name); let engine = setup_engine(); let setup = Func::<(Dynamic,), Dynamic>::create_from_ast(engine, ast, "setup"); let piles = conf.piles.clone(); - let player_piles = vec![conf.player_piles.clone(); players as usize]; + let player_piles = vec![conf.player_piles.clone(); current_players.len()]; let Data { piles, player_piles, players: _, other, } = from_dynamic( - &setup(to_dynamic(Data::new(piles, player_piles, players)).unwrap()).unwrap(), + &setup( + to_dynamic(Data::new(piles, player_piles, current_players.len() as u32)).unwrap(), + ) + .unwrap(), ) .unwrap(); + let mut players = HashMap::new(); + for (i, player) in current_players.iter().enumerate() { + players.insert(player.clone(), i as u32); + } + log::info!("PLayers in game {}: {:?}", name, players); Self { name, piles, player_piles, functions, - current_player: Player::new(0, players), + current_player: Player::new(0, current_players.len() as u32), data: other, + players, } // Self {setup: Box::new(Func::<(Vec, Vec>), ()>::create_from_ast(engine, ast, "setup"))} } diff --git a/server/src/games/run/functions.rs b/server/src/games/run/functions.rs index 595ae04..42cbe7d 100644 --- a/server/src/games/run/functions.rs +++ b/server/src/games/run/functions.rs @@ -52,6 +52,7 @@ impl Functions { pub fn turn_end(&self, d: Dynamic, p: Player) -> RhaiResult { self.engine .call_fn(&mut Scope::new(), &self.ast, "turn_end", (d, p)) + // TODO Check if the game has ended (With variable in the scope) } pub fn on_click( diff --git a/server/src/server.rs b/server/src/server.rs index be0e52c..1237951 100644 --- a/server/src/server.rs +++ b/server/src/server.rs @@ -24,14 +24,15 @@ pub async fn start( let properties = Arc::new(properties); let games = Arc::new(games); let voting = votes::VotingSystem::new(pool.clone().await); + let running_games: Arc>>> = Default::default(); let connection = ConnectionService { conn: RwLock::new(pool.clone().await), properties: properties.clone(), games: games.clone(), voting: voting.clone(), + running_games: running_games.clone(), }; - let running_games: Arc>>> = Default::default(); - let lobby = LobbyService::new(pool.clone().await, voting, running_games.clone()); + let lobby = LobbyService::new(pool.clone().await, voting, games, running_games.clone()); let game = GameService::new(pool, running_games); Server::builder() diff --git a/server/src/server/connection.rs b/server/src/server/connection.rs index c2e08bb..203b3ac 100644 --- a/server/src/server/connection.rs +++ b/server/src/server/connection.rs @@ -4,7 +4,7 @@ use log::info; use tokio::sync::{mpsc, RwLock}; -use std::sync::Arc; +use std::{collections::HashMap, sync::Arc}; use super::grpc::common::Name; use super::grpc::connection_service::connection_server::Connection; @@ -16,12 +16,13 @@ use super::votes; use super::client_id; -use crate::db; +use crate::{db, games::RunningGame}; pub struct ConnectionService { pub conn: RwLock, pub properties: Arc, pub games: Arc>, + pub running_games: Arc>>>, pub voting: votes::VotingSystem, } @@ -46,6 +47,11 @@ impl Connection for ConnectionService { client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), })?; let lobby = request.get_ref().code; + if self.running_games.read().await.contains_key(&lobby) { + return Err(Status::permission_denied( + "Can't join a lobby where a game is running", + )); + } let mut conn = self.conn.write().await; conn.join_lobby(uuid, lobby).await; self.voting.updated_users(&lobby).await; diff --git a/server/src/server/lobby.rs b/server/src/server/lobby.rs index 4c3f365..04c20cf 100644 --- a/server/src/server/lobby.rs +++ b/server/src/server/lobby.rs @@ -18,7 +18,7 @@ use crate::{db, games::RunningGame}; pub struct LobbyService { conn: RwLock, - // games: Arc>, + games: Arc>, voting: votes::VotingSystem, running_games: Arc>>>, } @@ -27,12 +27,14 @@ impl LobbyService { pub fn new( conn: db::DbClient, voting: votes::VotingSystem, + games: Arc>, running_games: Arc>>>, ) -> Self { Self { conn: RwLock::new(conn), voting, running_games, + games, } } } @@ -54,15 +56,15 @@ impl Lobby for LobbyService { client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), })?; - let finished = self.voting.ready(uuid).await; - if let Some(finished) = finished { - let user_count = self - .conn + if let Some(Some(winner)) = self.voting.ready(uuid).await { + let mut conn = self.conn.write().await; + let lobby = conn.get_lobby_for_user(uuid).await.unwrap(); + let user_count = conn.get_uuids_in_lobby_where_user_is(uuid).await; + let game = self.games[winner as usize].run(&user_count); + self.running_games .write() .await - .get_users_in_lobby_where_user_is(uuid) - .await - .len(); + .insert(lobby, RwLock::new((winner, game))); } Ok(Response::new(())) } diff --git a/server/src/server/votes.rs b/server/src/server/votes.rs index 5c1d5b3..e1e466d 100644 --- a/server/src/server/votes.rs +++ b/server/src/server/votes.rs @@ -150,7 +150,7 @@ impl VotingSystem { } } - pub async fn ready(&self, user: Uuid) -> Option { + pub async fn ready(&self, user: Uuid) -> Option> { let mut conn = self.conn.write().await; if let Some(lobby) = conn.get_lobby_for_user(user).await { conn.vote_ready(user).await; @@ -158,14 +158,37 @@ impl VotingSystem { let status = conn.get_votes(lobby).await; if finished { conn.delete_votes(lobby).await; + let winner = get_winner(status.iter().map(|(_, g, _)| g)); self.change_status_with_timeout(lobby, (status, finished), Some(SystemTime::now())) .await; + Some(Some(winner)) } else { self.change_status(lobby, (status, finished)).await; + Some(None) } - Some(finished) } else { None } } } + +fn get_winner<'a, I: Iterator>(iter: I) -> u32 { + let mut count: HashMap = Default::default(); + for item in iter { + if let Some(e) = count.get_mut(item) { + *e += 1; + } else { + count.insert(*item, 1); + } + } + count + .into_iter() + .fold((0, 0), |(max_game, max_num), (game, num)| { + if num > max_num { + (game, num) + } else { + (max_game, max_num) + } + }) + .0 +}