diff --git a/server/src/db.rs b/server/src/db.rs index 956acc7..7bf6393 100644 --- a/server/src/db.rs +++ b/server/src/db.rs @@ -1,10 +1,11 @@ // use sqlx::{pool::PoolConnection, prelude::*, query, query_as, SqliteConnection, SqlitePool}; // use tokio::stream::StreamExt; -use tokio::task::spawn; -use rusqlite::{params, Connection, Error as SqliteError}; -use server_client::encapsulate; use fallible_iterator::FallibleIterator; +use rusqlite::{params, Connection, Error as SqliteError, ErrorCode}; +use server_client::encapsulate; +use tokio::task::spawn; +use tonic::Status; use uuid::Uuid; use std::convert::TryInto; @@ -22,6 +23,8 @@ pub async fn start() -> DbClient { client } +type Result = std::result::Result; + pub struct Db { conn: Connection, } @@ -33,37 +36,53 @@ impl Db { } // type LobbyId = (i32,); -// FIXME: return Results intead of crashing + #[encapsulate(pub async ordered)] impl Db { pub fn load_setup(&mut self) { self.conn.execute_batch(include_str!("setup.sql")).unwrap() } - pub fn add_user(&mut self, name: String) -> Uuid { - let uuid = Uuid::new_v4(); - // println!("{:?}", uuid.as_bytes().to_vec()); - self.conn - .execute( + pub fn add_user(&mut self, name: String) -> Result { + loop { + let uuid = Uuid::new_v4(); + // println!("{:?}", uuid.as_bytes().to_vec()); + let res = self.conn.execute( "INSERT INTO Users(uuid, name) VALUES(?, ?)", params![uuid.as_bytes().to_vec(), name.to_string()], - ) - .unwrap(); // Server crashes if uuids collide - uuid + ); // Server crashes if uuids collide + + if let Err(e) = res { + match e { + SqliteError::SqliteFailure( + rusqlite::ffi::Error { + code: ErrorCode::ConstraintViolation, + extended_code: _, + }, + _, + ) => { + log::error!("add_user db error (constraint violation): {}", e); + return Err(Status::internal(e.to_string())); + } + e => { + log::error!("add_user db error: {}", e); + return Err(Status::internal(e.to_string())); + } + } + } + + return Ok(uuid); + } } - - // pub async fn users(&mut self) -> Vec { - // query_as::<_, types::User>("SELECT UUID, Name FROM Users") - // .fetch_all(&mut self.conn) - // .await - // .unwrap() - // } - + // FIXME: return Results intead of crashing pub fn get_users_in_lobby_where_user_is(&mut self, uuid: Uuid) -> Vec { let mut prepared = self.conn.prepare_cached("SELECT Name 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(0)).collect()).unwrap() + prepared + .query(params![uuid.as_bytes().to_vec()]) + .and_then(|r| r.map(|r| r.get(0)).collect()) + .unwrap() } - + // FIXME: return Results intead of crashing pub fn create_lobby(&mut self, public: bool) -> u32 { let id = rand::random(); @@ -76,7 +95,7 @@ impl Db { .unwrap(); // Server crashes if ids collide id } - + // FIXME: return Results intead of crashing pub fn join_lobby(&mut self, user: Uuid, lobby: u32) { self.conn .execute( @@ -86,7 +105,7 @@ impl Db { .unwrap(); // Server crashes if ids collide log::info!("{} joined the lobby {}", user, lobby); } - + // FIXME: return Results intead of crashing pub fn leave_lobby(&mut self, user: Uuid) { log::info!("{} leaving the lobby", user); self.delete_vote(user); @@ -103,23 +122,25 @@ impl Db { log::info!("Deleted {} lobbies ({})", lines, id as u32) } } - + // FIXME: return Results intead of crashing pub fn disconnect(&mut self, user: Uuid) { // self.leave_lobby(user).await; log::info!("{} disconnecting", user); - self.conn.execute( - "DELETE FROM Users WHERE UUID = ?", - params![user.as_bytes().to_vec()], - ).unwrap(); + self.conn + .execute( + "DELETE FROM Users WHERE UUID = ?", + params![user.as_bytes().to_vec()], + ) + .unwrap(); // log::error!("Disconnect DB result{:?}", r); } - + // FIXME: return Results intead of crashing pub fn get_lobby_for_user(&mut self, user: Uuid) -> Option { match self.conn.query_row( "SELECT LobbyID from UsersInLobbies WHERE UserID = ?", params![user.as_bytes().to_vec()], - |r| r.get::<_ ,i32>(0).map(|x| x as u32), + |r| r.get::<_, i32>(0).map(|x| x as u32), ) { Ok(v) => Ok(Some(v)), Err(e) => match e { @@ -129,42 +150,61 @@ impl Db { } .unwrap() } - + // FIXME: return Results intead of crashing pub fn get_public_lobbies(&mut self) -> Vec { let mut prepared = self .conn - .prepare("SELECT ID FROM Lobbies WHERE Public = TRUE").unwrap(); - prepared.query(params![]).and_then(|r| r.map(|r| r.get::<_, i32>(0).map(|x| x as u32)).collect()).unwrap() + .prepare("SELECT ID FROM Lobbies WHERE Public = TRUE") + .unwrap(); + prepared + .query(params![]) + .and_then(|r| r.map(|r| r.get::<_, i32>(0).map(|x| x as u32)).collect()) + .unwrap() } - + // FIXME: return Results intead of crashing fn delete_vote(&mut self, user: Uuid) { - self.conn.execute( - "DELETE FROM Votes WHERE UserId = ?", - params![user.as_bytes().to_vec()], - ).unwrap(); + self.conn + .execute( + "DELETE FROM Votes WHERE UserId = ?", + params![user.as_bytes().to_vec()], + ) + .unwrap(); } - + // FIXME: return Results intead of crashing pub fn vote(&mut self, user: Uuid, game: u32) { self.delete_vote(user); - self.conn.execute( - "INSERT INTO Votes(UserID, GameID) VALUES(?, ?)", - params![user.as_bytes().to_vec(), game as i32], - ).unwrap(); + self.conn + .execute( + "INSERT INTO Votes(UserID, GameID) VALUES(?, ?)", + params![user.as_bytes().to_vec(), game as i32], + ) + .unwrap(); } - + // FIXME: return Results intead of crashing pub fn vote_ready(&mut self, user: Uuid) { self.delete_vote(user); - self.conn.execute( - "UPDATE Votes SET Ready = TRUE WHERE UserID = ?", - params![user.as_bytes().to_vec()], - ).unwrap(); + self.conn + .execute( + "UPDATE Votes SET Ready = TRUE WHERE UserID = ?", + params![user.as_bytes().to_vec()], + ) + .unwrap(); } - + // FIXME: return Results intead of crashing pub fn get_votes(&mut self, lobby: u32) -> Vec<(String, u32, bool)> { let mut prepared = self.conn.prepare_cached("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 = ?)").unwrap(); - prepared.query(params![lobby as i32]).and_then(|r| r.map(|r| r.try_into().map(|(s, n, b): (String, i32, bool)| (s, n as u32, b))).collect()).unwrap() - } - + prepared + .query(params![lobby as i32]) + .and_then(|r| { + r.map(|r| { + r.try_into() + .map(|(s, n, b): (String, i32, bool)| (s, n as u32, b)) + }) + .collect() + }) + .unwrap() + } + // FIXME: return Results intead of crashing pub fn is_poll_finished(&mut self, lobby: u32) -> bool { self.conn.query_row( "SELECT (SELECT COUNT(*) FROM UsersInLobbies where LobbyID = ?) = (SELECT COUNT(*) FROM Votes WHERE UserID IN (SELECT UserID FROM UsersInLobbies WHERE LobbyID = ?) AND Ready = TRUE)", @@ -172,7 +212,7 @@ impl Db { |r| r.get(0) ).unwrap() } - + // FIXME: return Results intead of crashing pub fn delete_votes(&mut self, lobby: u32) { self.conn .execute("DELETE FROM Votes WHERE UserId IN (SELECT UserID FROM UsersInLobbies WHERE LobbyID = ?)", params![lobby as i32]).unwrap(); diff --git a/server/src/logger.rs b/server/src/logger.rs index 1a3df1f..1d546ad 100644 --- a/server/src/logger.rs +++ b/server/src/logger.rs @@ -170,7 +170,24 @@ where } } cleanup(); + }); + std::panic::set_hook(Box::new(|info| { + let thread = std::thread::current(); + let name = thread.name().unwrap_or(""); + log::error!("thread '{}' {}", name, info); + log::info!("Press any key to exit"); + loop { + match crossterm::event::read() { + // Event::Resize(_cols, rows) => self.lines.size = rows as usize - 1, + Ok(Event::Key(_)) => {break} + Ok(_) => (), + Err(e) => {log::error!("Errored while panicking, this is unrecoverable, exiting: {}", e); break} + } + } + cleanup(); + std::process::exit(0) + })); (stdout_send, stdin_recv, close_send, join_handle) } @@ -381,3 +398,5 @@ impl LimitedVec { self.pos = 0; } } + + diff --git a/server/src/main.rs b/server/src/main.rs index ec24c1d..e554bdd 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -12,6 +12,7 @@ async fn main() { server_properties::setup(); let p = server_properties::ServerProperties::load(); let (close, _stdin) = logger::setup(&p).unwrap(); + info!(target: "setup", "Server name: `{}`", p.name); info!(target: "setup", "Serving on address `{}`", p.addr); // for i in 0..1000 { diff --git a/server/src/server/connection.rs b/server/src/server/connection.rs index 86200b8..3e7958b 100644 --- a/server/src/server/connection.rs +++ b/server/src/server/connection.rs @@ -27,7 +27,7 @@ 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 = self.conn.write().await.add_user(name.clone()).await; + let uuid = self.conn.write().await.add_user(name.clone()).await?; info!("Connected {}[{}]", name, uuid); Ok(Response::new(UserId { id: uuid.to_hyphenated().to_string(),