From 3989d711460600a7482e0faae2c4309627f35a7f Mon Sep 17 00:00:00 2001 From: ThePerkinrex Date: Tue, 24 Nov 2020 21:42:56 +0100 Subject: [PATCH] Start to add votes --- server/build.rs | 2 +- server/src/db.rs | 137 +++++++++---- server/src/grpc.rs | 223 --------------------- server/src/main.rs | 4 +- server/src/server.rs | 55 +++++ server/src/server/connection.rs | 113 +++++++++++ server/src/server/grpc.rs | 2 + server/src/{ => server}/grpc/game.rs | 0 server/src/server/lobby.rs | 94 +++++++++ server/src/server/lobby/votes.rs | 59 ++++++ server/src/setup.sql | 7 + unity/Assets/Scripts/MainMenuController.cs | 5 + 12 files changed, 435 insertions(+), 266 deletions(-) delete mode 100644 server/src/grpc.rs create mode 100644 server/src/server.rs create mode 100644 server/src/server/connection.rs create mode 100644 server/src/server/grpc.rs rename server/src/{ => server}/grpc/game.rs (100%) create mode 100644 server/src/server/lobby.rs create mode 100644 server/src/server/lobby/votes.rs diff --git a/server/build.rs b/server/build.rs index e52fffb..edcb99a 100644 --- a/server/build.rs +++ b/server/build.rs @@ -11,7 +11,7 @@ fn main() -> Result<(), Box> { } tonic_build::configure() .build_client(false) - .out_dir("src/grpc") + .out_dir("src/server/grpc") .compile(&files, &["../protobuf".into()])?; Ok(()) } diff --git a/server/src/db.rs b/server/src/db.rs index 62cb550..5ed2b81 100644 --- a/server/src/db.rs +++ b/server/src/db.rs @@ -1,5 +1,5 @@ use sqlx::{ - pool::PoolConnection, prelude::*, query_as, query_file_unchecked, query, FromRow, + pool::PoolConnection, prelude::*, query, query_as, query_file_unchecked, FromRow, SqliteConnection, SqlitePool, }; // use tokio::stream::StreamExt; @@ -52,11 +52,11 @@ impl DbConnection { let uuid = Uuid::new_v4(); // println!("{:?}", uuid.as_bytes().to_vec()); self.conn - .execute(query( - "INSERT INTO Users(uuid, name) VALUES(?, ?)").bind( - uuid.as_bytes().to_vec()).bind( - name.to_string() - )) + .execute( + query("INSERT INTO Users(uuid, name) VALUES(?, ?)") + .bind(uuid.as_bytes().to_vec()) + .bind(name.to_string()), + ) .await .unwrap(); // Server crashes if uuids collide uuid @@ -69,7 +69,7 @@ impl DbConnection { // .unwrap() // } - pub async fn getUsersInLobbyWhereUserIs(&mut self, uuid: Uuid) -> Vec { + pub async fn get_users_in_lobby_where_user_is(&mut self, uuid: Uuid) -> Vec { query_as::<_, (String, )>("SELECT Name FROM Users WHERE UUID in (SELECT UserId FROM UsersInLobbies WHERE LobbyId = (SELECT LobbyId FROM UsersInLobbies WHERE UserId = ?))").bind(uuid.as_bytes().to_vec()) .fetch_all(&mut self.conn) .await @@ -81,11 +81,11 @@ impl DbConnection { log::info!("Created the lobby {}", id); self.conn - .execute(query( - "INSERT INTO Lobbies(id, public) VALUES(?, ?)").bind( - id as i32).bind( - public - )) + .execute( + query("INSERT INTO Lobbies(id, public) VALUES(?, ?)") + .bind(id as i32) + .bind(public), + ) .await .unwrap(); // Server crashes if ids collide id @@ -94,53 +94,110 @@ impl DbConnection { pub async fn join_lobby(&mut self, user: Uuid, lobby: u32) { log::info!("{} joined the lobby {}", user, lobby); self.conn - .execute(query( - "INSERT INTO UsersInLobbies(UserId, LobbyId) VALUES(?, ?)").bind( - user.as_bytes().to_vec()).bind( - lobby as i32 - )) + .execute( + query("INSERT INTO UsersInLobbies(UserId, LobbyId) VALUES(?, ?)") + .bind(user.as_bytes().to_vec()) + .bind(lobby as i32), + ) .await .unwrap(); // Server crashes if ids collide } pub async fn leave_lobby(&mut self, user: &Uuid) { log::info!("{} leaving the lobby", user); - let v = query_as::<_, (i32,)>("SELECT LobbyId FROM UsersInLobbies WHERE UserId = ?").bind(user.as_bytes().to_vec()).fetch_one(&mut self.conn).await; - + self.delete_vote(user).await; + let v = query_as::<_, (i32,)>("SELECT LobbyId FROM UsersInLobbies WHERE UserId = ?") + .bind(user.as_bytes().to_vec()) + .fetch_one(&mut self.conn) + .await; + self.conn - .execute(query( - "DELETE FROM UsersInLobbies WHERE UserId = ?").bind( - user.as_bytes().to_vec(), - )) - .await - .unwrap(); - if let Ok((id, )) = v { - let lines = self.conn.execute(query("DELETE FROM Lobbies WHERE 0 = (SELECT count(*) FROM UsersInLobbies WHERE LobbyID = ?)").bind( id)).await.unwrap(); + .execute( + query("DELETE FROM UsersInLobbies WHERE UserId = ?").bind(user.as_bytes().to_vec()), + ) + .await + .unwrap(); + if let Ok((id,)) = v { + let lines = self.conn.execute(query("DELETE FROM Lobbies WHERE 0 = (SELECT count(*) FROM UsersInLobbies WHERE LobbyID = ?)").bind(id)).await.unwrap(); log::info!("Deleted {} lobbies ({})", lines, id as u32) } } pub async fn disconnect(&mut self, user: &Uuid) { log::info!("{} disconnecting", user); + self.leave_lobby(user).await; self.conn - .execute(query( - "DELETE FROM UsersInLobbies WHERE UserId = ?").bind( - user.as_bytes().to_vec(), - )); - self.conn - .execute(query( - "DELETE FROM Users WHERE UUID = ?").bind( - user.as_bytes().to_vec(), - )) - .await - .unwrap(); + .execute(query("DELETE FROM Users WHERE UUID = ?").bind(user.as_bytes().to_vec())) + .await + .unwrap(); } - pub async fn getPublicLobbies(&mut self) -> Vec { + pub async fn get_lobby_for_user(&mut self, user: &Uuid) -> Option { + match query_as::<_, (i32,)>("SELECT LobbyID from UsersInLobbies WHERE UserID = ?") + .bind(user.as_bytes().to_vec()) + .fetch_one(&mut self.conn) + .await + { + Ok((v,)) => Ok(Some(v as u32)), + Err(e) => match e { + sqlx::Error::RowNotFound => Ok(None), + e => Err(e), + }, + } + .unwrap() + } + + pub async fn get_public_lobbies(&mut self) -> Vec { query_as::<_, (i32,)>("SELECT ID FROM Lobbies WHERE Public = TRUE") .fetch_all(&mut self.conn) .await - .unwrap().into_iter().map(|(x,)| x as u32).collect() + .unwrap() + .into_iter() + .map(|(x,)| x as u32) + .collect() + } + + async fn delete_vote(&mut self, user: &Uuid) { + self.conn + .execute(query("DELETE FROM Votes WHERE UserId = ?").bind(user.as_bytes().to_vec())); + } + + pub async fn vote(&mut self, user: Uuid, game: u32) { + self.delete_vote(&user).await; + self.conn.execute( + query("INSERT INTO Votes(UserID, GameID) VALUES(?, ?)") + .bind(user.as_bytes().to_vec()) + .bind(game as i32), + ); + } + + pub async fn vote_ready(&mut self, user: Uuid) { + self.delete_vote(&user).await; + self.conn.execute( + query("UPDATE Votes SET Ready = TRUE WHERE UserID = ?").bind(user.as_bytes().to_vec()), + ); + } + + pub async fn get_votes(&mut self, lobby: u32) -> Vec<(String, u32, bool)> { + query_as::<_, (String, i32, bool)>("SELECT Users.Name, Votes.GameID, Votes.Ready FROM Votes JOIN Users ON Users.UUID = Votes.UserID WHERE Votes.UserID IN (SELECT UserID FROM UsersInLobbies WHERE LobbyID = ?)").bind(lobby as i32) + .fetch_all(&mut self.conn) + .await + .unwrap() + .into_iter() + .map(|(user, game, ready)| (user, game as u32, ready)) + .collect() + } + + pub async fn is_poll_finished(&mut self, lobby: u32) -> bool { + query_as::<_, (bool,)>("SELECT (SELECT COUNT(*) FROM UsersInLobbies where LobbyID = ?) = (SELECT COUNT(*) FROM Votes WHERE UserID IN (SELECT UserID FROM UsersInLobbies WHERE LobbyID = ?) AND Ready = TRUE)") + .bind(lobby as i32).bind(lobby as i32) + .fetch_one(&mut self.conn) + .await.unwrap().0 + } + + pub async fn delete_votes(&mut self, lobby: u32) { + self.conn + .execute(query("DELETE FROM Votes WHERE UserId IN (SELECT UserID FROM UsersInLobbies WHERE LobbyID = ?)").bind(lobby as i32)); } pub async fn close(self) { diff --git a/server/src/grpc.rs b/server/src/grpc.rs deleted file mode 100644 index faa380d..0000000 --- a/server/src/grpc.rs +++ /dev/null @@ -1,223 +0,0 @@ -use tonic::{transport::Server, Request, Response, Status}; - -use log::info; - -use std::sync::Arc; - -#[allow(non_camel_case_types)] -mod game; - -use game::connection_server::{Connection, ConnectionServer}; -use game::lobby_server::{Lobby, LobbyServer}; -use game::{CardId, Image, LobbyCode, LobbyConfig, Name, Null, UserId}; - -use crate::db; - -pub struct ConnectionService { - conn: Arc, - properties: Arc, - games: Arc>, -} - -#[tonic::async_trait] -impl Connection for ConnectionService { - async fn connect(&self, request: Request) -> Result, Status> { - let name = request.into_inner().name; - let mut conn = self.conn.acquire().await; - let uuid = conn.add_user(&name).await; - info!("Connected {}[{}]", name, uuid); - conn.close().await; - Ok(Response::new(UserId { - id: uuid.to_hyphenated().to_string(), - })) - } - - async fn join_lobby_with_code( - &self, - request: Request, - ) -> Result, Status> { - let uuid = client_id::get(request.metadata()).map_err(|x| match x { - client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), - client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), - })?; - let lobby = request.get_ref().code; - let mut conn = self.conn.acquire().await; - conn.join_lobby(uuid, lobby).await; - conn.close().await; - Ok(Response::new(Null {})) - } - - async fn create_lobby( - &self, - request: Request, - ) -> Result, Status> { - let uuid = client_id::get(request.metadata()).map_err(|x| match x { - client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), - client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), - })?; - let mut conn = self.conn.acquire().await; - let lobby = conn.create_lobby(request.into_inner().public).await; - conn.join_lobby(uuid, lobby).await; - conn.close().await; - Ok(Response::new(LobbyCode { code: lobby })) - } - - async fn name(&self, _request: Request) -> Result, Status> { - Ok(Response::new(game::Name { - name: self.properties.name.clone(), - })) - } - - type getGamesStream = mpsc::UnboundedReceiver>; - - async fn get_games(&self, _: Request) -> Result, Status> { - let (sender, receiver) = mpsc::unbounded_channel(); - for (id, game) in self.games.iter().enumerate() { - sender - .send(Ok(game::Game { - id: id as u32, - name: game.name.clone(), - version: game.version.clone(), - authors: game.authors.clone(), - })) - .unwrap(); - } - Ok(Response::new(receiver)) - } - - type getPublicLobbiesStream = mpsc::UnboundedReceiver>; - - async fn get_public_lobbies( - &self, - _request: Request, - ) -> Result, Status> { - let (sender, receiver) = mpsc::unbounded_channel(); - let mut conn = self.conn.acquire().await; - for id in conn.getPublicLobbies().await { - sender.send(Ok(LobbyCode { code: id })).unwrap(); - } - conn.close().await; - Ok(Response::new(receiver)) - } - - async fn disconnect(&self, request: Request) -> Result, Status> { - let uuid = client_id::get(request.metadata()).map_err(|x| match x { - client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), - client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), - })?; - let mut conn = self.conn.acquire().await; - conn.disconnect(&uuid).await; - conn.close().await; - Ok(Response::new(Null {})) - } -} - -use tokio::sync::mpsc; - -pub struct LobbyService { - conn: Arc, - games: Arc>, -} - -#[tonic::async_trait] -impl Lobby for LobbyService { - async fn get_card_image( - &self, - request: tonic::Request, - ) -> Result, tonic::Status> { - let id = request.into_inner(); - let game = &self.games.as_ref()[id.game_id as usize]; - let card = game.get_card_path(&id.card_id); - let mut buffer = Vec::new(); - image::open(&card) - .expect(&format!("Error loading the image in {:?}", card)) - .write_to(&mut buffer, image::ImageOutputFormat::Png) - .unwrap(); - Ok(Response::new(Image { content: buffer })) - } - - async fn vote(&self, _request: Request) -> Result, Status> { - todo!() - } - - async fn ready(&self, _request: Request) -> Result, Status> { - todo!() - } - - async fn status(&self, _request: Request) -> Result, Status> { - todo!() - } - - async fn leave( - &self, - request: tonic::Request, - ) -> Result, tonic::Status> { - let uuid = client_id::get(request.metadata()).map_err(|x| match x { - client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), - client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), - })?; - let mut conn = self.conn.acquire().await; - conn.leave_lobby(&uuid).await; - conn.close().await; - Ok(Response::new(Null {})) - } - - type usersStream = mpsc::UnboundedReceiver>; - - async fn users(&self, request: Request) -> Result, Status> { - let uuid = client_id::get(request.metadata()).map_err(|x| match x { - client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), - client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), - })?; - let (sender, receiver) = mpsc::unbounded_channel(); - let mut conn = self.conn.acquire().await; - for name in conn.getUsersInLobbyWhereUserIs(uuid).await { - sender.send(Ok(Name {name})).unwrap(); - } - conn.close().await; - Ok(Response::new(receiver)) - } -} - -pub async fn start( - pool: db::DbPool, - games: Vec, - properties: crate::server_properties::ServerProperties, -) { - let arc = Arc::new(pool); - let properties = Arc::new(properties); - let games = Arc::new(games); - let connection = ConnectionService { - conn: arc.clone(), - properties: properties.clone(), - games: games.clone(), - }; - let lobby = LobbyService { - conn: arc.clone(), - games, - }; - - Server::builder() - .add_service(ConnectionServer::new(connection)) - .add_service(LobbyServer::new(lobby)) - .serve(properties.addr) - .await - .unwrap(); -} - -mod client_id { - pub fn get(metadata: &tonic::metadata::MetadataMap) -> Result { - metadata - .get("client_id") - .ok_or(Error::NotSet) - .and_then(|x| { - uuid::Uuid::parse_str(x.to_str().map_err(|_| Error::MalformedUuid)?) - .map_err(|_| Error::MalformedUuid) - }) - } - - pub enum Error { - MalformedUuid, - NotSet, - } -} diff --git a/server/src/main.rs b/server/src/main.rs index 12f6d10..3e45b85 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -1,4 +1,4 @@ -mod grpc; +mod server; mod db; mod games; mod logger; @@ -27,6 +27,6 @@ async fn main() { info!(target: "setup", "Loading database"); let pool = db::DbPool::new().await; info!(target: "setup", "Starting server"); - grpc::start(pool, games, p).await; + server::start(pool, games, p).await; close.close(); } diff --git a/server/src/server.rs b/server/src/server.rs new file mode 100644 index 0000000..93b6765 --- /dev/null +++ b/server/src/server.rs @@ -0,0 +1,55 @@ +use tonic::transport::Server; + +use std::sync::Arc; + +use crate::db; + +mod connection; +mod grpc; +mod lobby; + +use grpc::game::connection_server::ConnectionServer; +use grpc::game::lobby_server::LobbyServer; + +use connection::ConnectionService; +use lobby::LobbyService; + +pub async fn start( + pool: db::DbPool, + games: Vec, + properties: crate::server_properties::ServerProperties, +) { + let arc = Arc::new(pool); + let properties = Arc::new(properties); + let games = Arc::new(games); + let connection = ConnectionService { + conn: arc.clone(), + properties: properties.clone(), + games: games.clone(), + }; + let lobby = LobbyService::new(arc.clone(), games); + + Server::builder() + .add_service(ConnectionServer::new(connection)) + .add_service(LobbyServer::new(lobby)) + .serve(properties.addr) + .await + .unwrap(); +} + +mod client_id { + pub fn get(metadata: &tonic::metadata::MetadataMap) -> Result { + metadata + .get("client_id") + .ok_or(Error::NotSet) + .and_then(|x| { + uuid::Uuid::parse_str(x.to_str().map_err(|_| Error::MalformedUuid)?) + .map_err(|_| Error::MalformedUuid) + }) + } + + pub enum Error { + MalformedUuid, + NotSet, + } +} diff --git a/server/src/server/connection.rs b/server/src/server/connection.rs new file mode 100644 index 0000000..6803231 --- /dev/null +++ b/server/src/server/connection.rs @@ -0,0 +1,113 @@ +use tonic::{Request, Response, Status}; + +use log::info; + +use tokio::sync::mpsc; + +use std::sync::Arc; + +use super::grpc::game::connection_server::Connection; +use super::grpc::game::{LobbyCode, LobbyConfig, Name, Null, UserId, Game}; + +use super::client_id; + +use crate::db; + +pub struct ConnectionService { + pub conn: Arc, + pub properties: Arc, + pub games: Arc>, +} + +#[tonic::async_trait] +impl Connection for ConnectionService { + async fn connect(&self, request: Request) -> Result, Status> { + let name = request.into_inner().name; + let mut conn = self.conn.acquire().await; + let uuid = conn.add_user(&name).await; + info!("Connected {}[{}]", name, uuid); + conn.close().await; + Ok(Response::new(UserId { + id: uuid.to_hyphenated().to_string(), + })) + } + + async fn join_lobby_with_code( + &self, + request: Request, + ) -> Result, Status> { + let uuid = client_id::get(request.metadata()).map_err(|x| match x { + client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), + client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), + })?; + let lobby = request.get_ref().code; + let mut conn = self.conn.acquire().await; + conn.join_lobby(uuid, lobby).await; + conn.close().await; + Ok(Response::new(Null {})) + } + + async fn create_lobby( + &self, + request: Request, + ) -> Result, Status> { + let uuid = client_id::get(request.metadata()).map_err(|x| match x { + client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), + client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), + })?; + let mut conn = self.conn.acquire().await; + let lobby = conn.create_lobby(request.into_inner().public).await; + conn.join_lobby(uuid, lobby).await; + conn.close().await; + Ok(Response::new(LobbyCode { code: lobby })) + } + + async fn name(&self, _request: Request) -> Result, Status> { + Ok(Response::new(Name { + name: self.properties.name.clone(), + })) + } + + type getGamesStream = mpsc::UnboundedReceiver>; + + async fn get_games(&self, _: Request) -> Result, Status> { + let (sender, receiver) = mpsc::unbounded_channel(); + for (id, game) in self.games.iter().enumerate() { + sender + .send(Ok(Game { + id: id as u32, + name: game.name.clone(), + version: game.version.clone(), + authors: game.authors.clone(), + })) + .unwrap(); + } + Ok(Response::new(receiver)) + } + + type getPublicLobbiesStream = mpsc::UnboundedReceiver>; + + async fn get_public_lobbies( + &self, + _request: Request, + ) -> Result, Status> { + let (sender, receiver) = mpsc::unbounded_channel(); + let mut conn = self.conn.acquire().await; + for id in conn.get_public_lobbies().await { + sender.send(Ok(LobbyCode { code: id })).unwrap(); + } + conn.close().await; + Ok(Response::new(receiver)) + } + + async fn disconnect(&self, request: Request) -> Result, Status> { + let uuid = client_id::get(request.metadata()).map_err(|x| match x { + client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), + client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), + })?; + let mut conn = self.conn.acquire().await; + conn.disconnect(&uuid).await; + conn.close().await; + Ok(Response::new(Null {})) + } +} \ No newline at end of file diff --git a/server/src/server/grpc.rs b/server/src/server/grpc.rs new file mode 100644 index 0000000..491f7cc --- /dev/null +++ b/server/src/server/grpc.rs @@ -0,0 +1,2 @@ +#[allow(non_camel_case_types)] +pub mod game; \ No newline at end of file diff --git a/server/src/grpc/game.rs b/server/src/server/grpc/game.rs similarity index 100% rename from server/src/grpc/game.rs rename to server/src/server/grpc/game.rs diff --git a/server/src/server/lobby.rs b/server/src/server/lobby.rs new file mode 100644 index 0000000..ef2c0f4 --- /dev/null +++ b/server/src/server/lobby.rs @@ -0,0 +1,94 @@ +mod votes; + +use tonic::{Request, Response, Status}; + +use log::info; + +use tokio::sync::mpsc; + +use std::sync::Arc; + +use super::grpc::game::lobby_server::Lobby; +use super::grpc::game::{CardId, Image, LobbyStatus, Name, Null, Vote}; + +use super::client_id; + +use crate::db; + +pub struct LobbyService { + conn: Arc, + games: Arc>, + voting: votes::VotingSystem, +} + +impl LobbyService { + pub fn new(conn: Arc, games: Arc>) -> Self { + Self { games, conn: conn.clone(), voting: votes::VotingSystem::new(conn).0 } + } +} + +#[tonic::async_trait] +impl Lobby for LobbyService { + async fn get_card_image( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let id = request.into_inner(); + let game = &self.games.as_ref()[id.game_id as usize]; + let card = game.get_card_path(&id.card_id); + let mut buffer = Vec::new(); + image::open(&card) + .expect(&format!("Error loading the image in {:?}", card)) + .write_to(&mut buffer, image::ImageOutputFormat::Png) + .unwrap(); + Ok(Response::new(Image { content: buffer })) + } + + async fn vote(&self, request: Request) -> Result, Status> { + let uuid = client_id::get(request.metadata()).map_err(|x| match x { + client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), + client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), + })?; + // self.votes.vote(uuid, request.into_inner().id).await; + // log::info!("Votes: {:?}", self.votes.get_votes().await); + Ok(Response::new(Null {})) + } + + async fn ready(&self, _request: Request) -> Result, Status> { + todo!() + } + + async fn status(&self, _request: Request) -> Result, Status> { + todo!() + } + + async fn leave( + &self, + request: tonic::Request, + ) -> Result, tonic::Status> { + let uuid = client_id::get(request.metadata()).map_err(|x| match x { + client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), + client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), + })?; + let mut conn = self.conn.acquire().await; + conn.leave_lobby(&uuid).await; + conn.close().await; + Ok(Response::new(Null {})) + } + + type usersStream = mpsc::UnboundedReceiver>; + + async fn users(&self, request: Request) -> Result, Status> { + let uuid = client_id::get(request.metadata()).map_err(|x| match x { + client_id::Error::NotSet => Status::failed_precondition("client_id must be set"), + client_id::Error::MalformedUuid => Status::failed_precondition("malformed client_id"), + })?; + let (sender, receiver) = mpsc::unbounded_channel(); + let mut conn = self.conn.acquire().await; + for name in conn.get_users_in_lobby_where_user_is(uuid).await { + sender.send(Ok(Name { name })).unwrap(); + } + conn.close().await; + Ok(Response::new(receiver)) + } +} diff --git a/server/src/server/lobby/votes.rs b/server/src/server/lobby/votes.rs new file mode 100644 index 0000000..09fe95d --- /dev/null +++ b/server/src/server/lobby/votes.rs @@ -0,0 +1,59 @@ +use tokio::sync::broadcast; +use uuid::Uuid; + +use std::sync::Arc; + +use crate::db; + +type Message = Vec<(String, u32, bool)>; + +pub struct VotingSystem { + conn: Arc, + // games: Arc>, + broadcast: broadcast::Sender, +} + +impl VotingSystem { + pub fn new( + conn: Arc, + // games: Arc>, + ) -> (Self, broadcast::Receiver) { + let (tx, rx) = broadcast::channel(1); + ( + Self { + conn, + // games, + broadcast: tx, + }, + rx, + ) + } + + pub fn subscribe(&self) -> broadcast::Receiver { + self.broadcast.subscribe() + } + + pub async fn vote(&self, user: Uuid, game: u32) { + let mut conn = self.conn.acquire().await; + if let Some(lobby) = conn.get_lobby_for_user(&user).await { + conn.vote(user, game).await; + let status = conn.get_votes(lobby).await; + self.broadcast.send(status).unwrap(); + + } + conn.close().await; + + } + + pub async fn ready(&self, user: Uuid) { + let mut conn = self.conn.acquire().await; + if let Some(lobby) = conn.get_lobby_for_user(&user).await { + conn.vote_ready(user).await; + let status = conn.get_votes(lobby).await; + self.broadcast.send(status).unwrap(); + + } + + conn.close().await; + } +} \ No newline at end of file diff --git a/server/src/setup.sql b/server/src/setup.sql index 90db7ab..76fac8e 100644 --- a/server/src/setup.sql +++ b/server/src/setup.sql @@ -20,4 +20,11 @@ CREATE TABLE UsersInLobbies ( UserID CHAR(16) NOT NULL UNIQUE, FOREIGN KEY (LobbyID) REFERENCES Lobbies(ID), FOREIGN KEY (UserID) REFERENCES Users(UUID) +); + +CREATE TABLE Votes ( + UserID CHAR(16) NOT NULL UNIQUE, + GameID INT NOT NULL, + Ready BOOLEAN DEFAULT FALSE, + FOREIGN KEY (UserID) REFERENCES UsersInLobbies(UserID) ); \ No newline at end of file diff --git a/unity/Assets/Scripts/MainMenuController.cs b/unity/Assets/Scripts/MainMenuController.cs index 3e20231..118fff4 100644 --- a/unity/Assets/Scripts/MainMenuController.cs +++ b/unity/Assets/Scripts/MainMenuController.cs @@ -61,6 +61,11 @@ public class MainMenuController : MonoBehaviour { } } + public void OnApplicationQuit() { + var conn = Client.GetConnection(); + if (conn != null) conn.Close(); + } + void Update() {} public void ConnectServer() {