Browse Source

Start to add votes

new_protocol
ThePerkinrex 5 years ago
parent
commit
3989d71146
No known key found for this signature in database GPG Key ID: 1F45A7C4BFB41607
  1. 2
      server/build.rs
  2. 135
      server/src/db.rs
  3. 223
      server/src/grpc.rs
  4. 4
      server/src/main.rs
  5. 55
      server/src/server.rs
  6. 113
      server/src/server/connection.rs
  7. 2
      server/src/server/grpc.rs
  8. 0
      server/src/server/grpc/game.rs
  9. 94
      server/src/server/lobby.rs
  10. 59
      server/src/server/lobby/votes.rs
  11. 7
      server/src/setup.sql
  12. 5
      unity/Assets/Scripts/MainMenuController.cs

2
server/build.rs

@ -11,7 +11,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
tonic_build::configure()
.build_client(false)
.out_dir("src/grpc")
.out_dir("src/server/grpc")
.compile(&files, &["../protobuf".into()])?;
Ok(())
}

135
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<String> {
pub async fn get_users_in_lobby_where_user_is(&mut self, uuid: Uuid) -> Vec<String> {
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<u32> {
pub async fn get_lobby_for_user(&mut self, user: &Uuid) -> Option<u32> {
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<u32> {
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) {

223
server/src/grpc.rs

@ -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<db::DbPool>,
properties: Arc<crate::server_properties::ServerProperties>,
games: Arc<Vec<crate::games::Game>>,
}
#[tonic::async_trait]
impl Connection for ConnectionService {
async fn connect(&self, request: Request<Name>) -> Result<Response<UserId>, 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<LobbyCode>,
) -> Result<Response<Null>, 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<LobbyConfig>,
) -> Result<Response<LobbyCode>, 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<Null>) -> Result<Response<game::Name>, Status> {
Ok(Response::new(game::Name {
name: self.properties.name.clone(),
}))
}
type getGamesStream = mpsc::UnboundedReceiver<Result<game::Game, tonic::Status>>;
async fn get_games(&self, _: Request<Null>) -> Result<Response<Self::getGamesStream>, 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<Result<LobbyCode, tonic::Status>>;
async fn get_public_lobbies(
&self,
_request: Request<Null>,
) -> Result<Response<Self::getPublicLobbiesStream>, 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<Null>) -> Result<Response<Null>, 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<db::DbPool>,
games: Arc<Vec<crate::games::Game>>,
}
#[tonic::async_trait]
impl Lobby for LobbyService {
async fn get_card_image(
&self,
request: tonic::Request<CardId>,
) -> Result<tonic::Response<Image>, 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<game::Vote>) -> Result<Response<Null>, Status> {
todo!()
}
async fn ready(&self, _request: Request<Null>) -> Result<Response<Null>, Status> {
todo!()
}
async fn status(&self, _request: Request<Null>) -> Result<Response<game::LobbyStatus>, Status> {
todo!()
}
async fn leave(
&self,
request: tonic::Request<Null>,
) -> Result<tonic::Response<Null>, 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<Result<Name, tonic::Status>>;
async fn users(&self, request: Request<Null>) -> Result<Response<Self::usersStream>, 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<crate::games::Game>,
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<uuid::Uuid, Error> {
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,
}
}

4
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();
}

55
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<crate::games::Game>,
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<uuid::Uuid, Error> {
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,
}
}

113
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<db::DbPool>,
pub properties: Arc<crate::server_properties::ServerProperties>,
pub games: Arc<Vec<crate::games::Game>>,
}
#[tonic::async_trait]
impl Connection for ConnectionService {
async fn connect(&self, request: Request<Name>) -> Result<Response<UserId>, 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<LobbyCode>,
) -> Result<Response<Null>, 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<LobbyConfig>,
) -> Result<Response<LobbyCode>, 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<Null>) -> Result<Response<Name>, Status> {
Ok(Response::new(Name {
name: self.properties.name.clone(),
}))
}
type getGamesStream = mpsc::UnboundedReceiver<Result<Game, tonic::Status>>;
async fn get_games(&self, _: Request<Null>) -> Result<Response<Self::getGamesStream>, 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<Result<LobbyCode, tonic::Status>>;
async fn get_public_lobbies(
&self,
_request: Request<Null>,
) -> Result<Response<Self::getPublicLobbiesStream>, 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<Null>) -> Result<Response<Null>, 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 {}))
}
}

2
server/src/server/grpc.rs

@ -0,0 +1,2 @@
#[allow(non_camel_case_types)]
pub mod game;

0
server/src/grpc/game.rs → server/src/server/grpc/game.rs

94
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<db::DbPool>,
games: Arc<Vec<crate::games::Game>>,
voting: votes::VotingSystem,
}
impl LobbyService {
pub fn new(conn: Arc<db::DbPool>, games: Arc<Vec<crate::games::Game>>) -> 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<CardId>,
) -> Result<tonic::Response<Image>, 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<Vote>) -> Result<Response<Null>, 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<Null>) -> Result<Response<Null>, Status> {
todo!()
}
async fn status(&self, _request: Request<Null>) -> Result<Response<LobbyStatus>, Status> {
todo!()
}
async fn leave(
&self,
request: tonic::Request<Null>,
) -> Result<tonic::Response<Null>, 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<Result<Name, tonic::Status>>;
async fn users(&self, request: Request<Null>) -> Result<Response<Self::usersStream>, 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))
}
}

59
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<db::DbPool>,
// games: Arc<Vec<crate::games::Game>>,
broadcast: broadcast::Sender<Message>,
}
impl VotingSystem {
pub fn new(
conn: Arc<db::DbPool>,
// games: Arc<Vec<crate::games::Game>>,
) -> (Self, broadcast::Receiver<Message>) {
let (tx, rx) = broadcast::channel(1);
(
Self {
conn,
// games,
broadcast: tx,
},
rx,
)
}
pub fn subscribe(&self) -> broadcast::Receiver<Message> {
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;
}
}

7
server/src/setup.sql

@ -21,3 +21,10 @@ CREATE TABLE UsersInLobbies (
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)
);

5
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() {

Loading…
Cancel
Save