Browse Source

Add better panic & error handling

new_protocol
ThePerkinrex 5 years ago
parent
commit
4bf1f9c0f7
No known key found for this signature in database GPG Key ID: 1F45A7C4BFB41607
  1. 124
      server/src/db.rs
  2. 19
      server/src/logger.rs
  3. 1
      server/src/main.rs
  4. 2
      server/src/server/connection.rs

124
server/src/db.rs

@ -1,10 +1,11 @@
// use sqlx::{pool::PoolConnection, prelude::*, query, query_as, SqliteConnection, SqlitePool}; // use sqlx::{pool::PoolConnection, prelude::*, query, query_as, SqliteConnection, SqlitePool};
// use tokio::stream::StreamExt; // use tokio::stream::StreamExt;
use tokio::task::spawn;
use rusqlite::{params, Connection, Error as SqliteError};
use server_client::encapsulate;
use fallible_iterator::FallibleIterator; 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 uuid::Uuid;
use std::convert::TryInto; use std::convert::TryInto;
@ -22,6 +23,8 @@ pub async fn start() -> DbClient {
client client
} }
type Result<T> = std::result::Result<T, Status>;
pub struct Db { pub struct Db {
conn: Connection, conn: Connection,
} }
@ -33,37 +36,53 @@ impl Db {
} }
// type LobbyId = (i32,); // type LobbyId = (i32,);
// FIXME: return Results intead of crashing
#[encapsulate(pub async ordered)] #[encapsulate(pub async ordered)]
impl Db { impl Db {
pub fn load_setup(&mut self) { pub fn load_setup(&mut self) {
self.conn.execute_batch(include_str!("setup.sql")).unwrap() self.conn.execute_batch(include_str!("setup.sql")).unwrap()
} }
pub fn add_user(&mut self, name: String) -> Uuid { pub fn add_user(&mut self, name: String) -> Result<Uuid> {
loop {
let uuid = Uuid::new_v4(); let uuid = Uuid::new_v4();
// println!("{:?}", uuid.as_bytes().to_vec()); // println!("{:?}", uuid.as_bytes().to_vec());
self.conn let res = self.conn.execute(
.execute(
"INSERT INTO Users(uuid, name) VALUES(?, ?)", "INSERT INTO Users(uuid, name) VALUES(?, ?)",
params![uuid.as_bytes().to_vec(), name.to_string()], params![uuid.as_bytes().to_vec(), name.to_string()],
) ); // Server crashes if uuids collide
.unwrap(); // Server crashes if uuids collide
uuid 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()));
}
}
} }
// pub async fn users(&mut self) -> Vec<types::User> { return Ok(uuid);
// query_as::<_, types::User>("SELECT UUID, Name FROM Users") }
// .fetch_all(&mut self.conn) }
// .await // FIXME: return Results intead of crashing
// .unwrap()
// }
pub fn get_users_in_lobby_where_user_is(&mut self, uuid: Uuid) -> Vec<String> { pub fn get_users_in_lobby_where_user_is(&mut self, uuid: Uuid) -> Vec<String> {
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(); 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 { pub fn create_lobby(&mut self, public: bool) -> u32 {
let id = rand::random(); let id = rand::random();
@ -76,7 +95,7 @@ impl Db {
.unwrap(); // Server crashes if ids collide .unwrap(); // Server crashes if ids collide
id id
} }
// FIXME: return Results intead of crashing
pub fn join_lobby(&mut self, user: Uuid, lobby: u32) { pub fn join_lobby(&mut self, user: Uuid, lobby: u32) {
self.conn self.conn
.execute( .execute(
@ -86,7 +105,7 @@ impl Db {
.unwrap(); // Server crashes if ids collide .unwrap(); // Server crashes if ids collide
log::info!("{} joined the lobby {}", user, lobby); log::info!("{} joined the lobby {}", user, lobby);
} }
// FIXME: return Results intead of crashing
pub fn leave_lobby(&mut self, user: Uuid) { pub fn leave_lobby(&mut self, user: Uuid) {
log::info!("{} leaving the lobby", user); log::info!("{} leaving the lobby", user);
self.delete_vote(user); self.delete_vote(user);
@ -103,23 +122,25 @@ impl Db {
log::info!("Deleted {} lobbies ({})", lines, id as u32) log::info!("Deleted {} lobbies ({})", lines, id as u32)
} }
} }
// FIXME: return Results intead of crashing
pub fn disconnect(&mut self, user: Uuid) { pub fn disconnect(&mut self, user: Uuid) {
// self.leave_lobby(user).await; // self.leave_lobby(user).await;
log::info!("{} disconnecting", user); log::info!("{} disconnecting", user);
self.conn.execute( self.conn
.execute(
"DELETE FROM Users WHERE UUID = ?", "DELETE FROM Users WHERE UUID = ?",
params![user.as_bytes().to_vec()], params![user.as_bytes().to_vec()],
).unwrap(); )
.unwrap();
// log::error!("Disconnect DB result{:?}", r); // log::error!("Disconnect DB result{:?}", r);
} }
// FIXME: return Results intead of crashing
pub fn get_lobby_for_user(&mut self, user: Uuid) -> Option<u32> { pub fn get_lobby_for_user(&mut self, user: Uuid) -> Option<u32> {
match self.conn.query_row( match self.conn.query_row(
"SELECT LobbyID from UsersInLobbies WHERE UserID = ?", "SELECT LobbyID from UsersInLobbies WHERE UserID = ?",
params![user.as_bytes().to_vec()], 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)), Ok(v) => Ok(Some(v)),
Err(e) => match e { Err(e) => match e {
@ -129,42 +150,61 @@ impl Db {
} }
.unwrap() .unwrap()
} }
// FIXME: return Results intead of crashing
pub fn get_public_lobbies(&mut self) -> Vec<u32> { pub fn get_public_lobbies(&mut self) -> Vec<u32> {
let mut prepared = self let mut prepared = self
.conn .conn
.prepare("SELECT ID FROM Lobbies WHERE Public = TRUE").unwrap(); .prepare("SELECT ID FROM Lobbies WHERE Public = TRUE")
prepared.query(params![]).and_then(|r| r.map(|r| r.get::<_, i32>(0).map(|x| x as u32)).collect()).unwrap() .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) { fn delete_vote(&mut self, user: Uuid) {
self.conn.execute( self.conn
.execute(
"DELETE FROM Votes WHERE UserId = ?", "DELETE FROM Votes WHERE UserId = ?",
params![user.as_bytes().to_vec()], params![user.as_bytes().to_vec()],
).unwrap(); )
.unwrap();
} }
// FIXME: return Results intead of crashing
pub fn vote(&mut self, user: Uuid, game: u32) { pub fn vote(&mut self, user: Uuid, game: u32) {
self.delete_vote(user); self.delete_vote(user);
self.conn.execute( self.conn
.execute(
"INSERT INTO Votes(UserID, GameID) VALUES(?, ?)", "INSERT INTO Votes(UserID, GameID) VALUES(?, ?)",
params![user.as_bytes().to_vec(), game as i32], params![user.as_bytes().to_vec(), game as i32],
).unwrap(); )
.unwrap();
} }
// FIXME: return Results intead of crashing
pub fn vote_ready(&mut self, user: Uuid) { pub fn vote_ready(&mut self, user: Uuid) {
self.delete_vote(user); self.delete_vote(user);
self.conn.execute( self.conn
.execute(
"UPDATE Votes SET Ready = TRUE WHERE UserID = ?", "UPDATE Votes SET Ready = TRUE WHERE UserID = ?",
params![user.as_bytes().to_vec()], params![user.as_bytes().to_vec()],
).unwrap(); )
.unwrap();
} }
// FIXME: return Results intead of crashing
pub fn get_votes(&mut self, lobby: u32) -> Vec<(String, u32, bool)> { 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(); 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 { pub fn is_poll_finished(&mut self, lobby: u32) -> bool {
self.conn.query_row( 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)", "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) |r| r.get(0)
).unwrap() ).unwrap()
} }
// FIXME: return Results intead of crashing
pub fn delete_votes(&mut self, lobby: u32) { pub fn delete_votes(&mut self, lobby: u32) {
self.conn self.conn
.execute("DELETE FROM Votes WHERE UserId IN (SELECT UserID FROM UsersInLobbies WHERE LobbyID = ?)", params![lobby as i32]).unwrap(); .execute("DELETE FROM Votes WHERE UserId IN (SELECT UserID FROM UsersInLobbies WHERE LobbyID = ?)", params![lobby as i32]).unwrap();

19
server/src/logger.rs

@ -170,7 +170,24 @@ where
} }
} }
cleanup(); cleanup();
}); });
std::panic::set_hook(Box::new(|info| {
let thread = std::thread::current();
let name = thread.name().unwrap_or("<unnamed>");
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) (stdout_send, stdin_recv, close_send, join_handle)
} }
@ -381,3 +398,5 @@ impl<T> LimitedVec<T> {
self.pos = 0; self.pos = 0;
} }
} }

1
server/src/main.rs

@ -12,6 +12,7 @@ async fn main() {
server_properties::setup(); server_properties::setup();
let p = server_properties::ServerProperties::load(); let p = server_properties::ServerProperties::load();
let (close, _stdin) = logger::setup(&p).unwrap(); let (close, _stdin) = logger::setup(&p).unwrap();
info!(target: "setup", "Server name: `{}`", p.name); info!(target: "setup", "Server name: `{}`", p.name);
info!(target: "setup", "Serving on address `{}`", p.addr); info!(target: "setup", "Serving on address `{}`", p.addr);
// for i in 0..1000 { // for i in 0..1000 {

2
server/src/server/connection.rs

@ -27,7 +27,7 @@ impl Connection for ConnectionService {
async fn connect(&self, request: Request<Name>) -> Result<Response<UserId>, Status> { async fn connect(&self, request: Request<Name>) -> Result<Response<UserId>, Status> {
let name = request.into_inner().name; let name = request.into_inner().name;
// let mut conn = self.conn.acquire().await; // 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); info!("Connected {}[{}]", name, uuid);
Ok(Response::new(UserId { Ok(Response::new(UserId {
id: uuid.to_hyphenated().to_string(), id: uuid.to_hyphenated().to_string(),

Loading…
Cancel
Save