Impl enough backend to assign ids to players

This commit is contained in:
Stephen Seo 2022-03-18 19:29:38 +09:00
parent 495a11b87f
commit 5a0c0696d1
5 changed files with 231 additions and 23 deletions

74
back_end/Cargo.lock generated
View file

@ -2,6 +2,17 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "autocfg"
version = "1.1.0"
@ -104,6 +115,18 @@ dependencies = [
"crypto-common",
]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fastrand"
version = "1.7.0"
@ -134,6 +157,8 @@ name = "four_line_dropper_backend"
version = "0.1.0"
dependencies = [
"bytes",
"rand",
"rusqlite",
"serde_json",
"tokio",
"warp",
@ -226,6 +251,18 @@ name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
]
[[package]]
name = "hashlink"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
dependencies = [
"hashbrown",
]
[[package]]
name = "headers"
@ -367,6 +404,16 @@ version = "0.2.119"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
[[package]]
name = "libsqlite3-sys"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb644c388dfaefa18035c12614156d285364769e818893da0dda9030c80ad2ba"
dependencies = [
"pkg-config",
"vcpkg",
]
[[package]]
name = "lock_api"
version = "0.4.6"
@ -546,6 +593,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "pkg-config"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
[[package]]
name = "ppv-lite86"
version = "0.2.16"
@ -624,6 +677,21 @@ dependencies = [
"winapi",
]
[[package]]
name = "rusqlite"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"memchr",
"smallvec",
]
[[package]]
name = "ryu"
version = "1.0.9"
@ -976,6 +1044,12 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"

View file

@ -10,3 +10,5 @@ tokio = { version = "1", features = ["full"] }
warp = "0.3"
serde_json = "1.0"
bytes = "1.1"
rusqlite = "0.27.0"
rand = "0.8.4"

107
back_end/src/db_handler.rs Normal file
View file

@ -0,0 +1,107 @@
use std::sync::mpsc::{Receiver, SyncSender};
use std::thread;
use rand::{thread_rng, Rng};
use rusqlite::Connection;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
enum DBFirstRun {
FirstRun,
NotFirstRun,
}
fn init_conn(sqlite_path: &str, first_run: DBFirstRun) -> Result<Connection, String> {
if let Ok(conn) = Connection::open(sqlite_path) {
conn.execute("PRAGMA foreign_keys = ON;", [])
.expect("Should be able to enable \"foreign_keys\"");
let result = conn.execute(
"
CREATE TABLE players (id INTEGER PRIMARY KEY NOT NULL,
date_added TEXT NOT NULL,
game_id INTEGER,
FOREIGN KEY(game_id) REFERENCES games(id) ON DELETE CASCADE);
",
[],
);
if result.is_ok() {
if first_run == DBFirstRun::FirstRun {
println!("Created \"players\" table");
}
} else if first_run == DBFirstRun::FirstRun {
println!("\"players\" table exists");
}
let result = conn.execute(
"
CREATE TABLE games (id INTEGER PRIMARY KEY NOT NULL,
cyan_player INTEGER UNIQUE NOT NULL,
magenta_player INTEGER UNIQUE NOT NULL,
date_changed TEXT NOT NULL,
board TEXT NOT NULL,
status INTEGER NOT NULL,
FOREIGN KEY(cyan_player) REFERENCES players (id),
FOREIGN KEY(magenta_player) REFERENCES players (id));
",
[],
);
if result.is_ok() {
if first_run == DBFirstRun::FirstRun {
println!("Created \"games\" table");
}
} else if first_run == DBFirstRun::FirstRun {
println!("\"games\" table exists");
}
Ok(conn)
} else {
Err(String::from("Failed to get connection"))
}
}
pub fn start_db_handler_thread(rx: Receiver<SyncSender<u32>>, sqlite_path: String) {
thread::spawn(move || {
// temporarily get conn which should initialize on first setup of db
if let Ok(_conn) = init_conn(&sqlite_path, DBFirstRun::FirstRun) {
} else {
println!("ERROR: Failed init sqlite db connection");
return;
}
loop {
let result = rx.recv();
//println!("db_handler: Got result from rx");
if let Ok(player_tx) = result {
//println!("db_handler: Got player_tx from rx");
// got request to create new player, create new player
let mut player_id: u32 = thread_rng().gen();
let conn = init_conn(&sqlite_path, DBFirstRun::NotFirstRun)
.expect("DB connection should be available");
loop {
let mut stmt = conn
.prepare("SELECT id FROM players WHERE id = ?;")
.expect("Should be able to prepare DB statement");
match stmt.query_row([player_id], |_row| Ok(())) {
Ok(_) => {
player_id = thread_rng().gen();
}
Err(_) => break,
}
}
conn.execute(
"INSERT INTO players (id, date_added) VALUES (?, datetime());",
[player_id],
)
.unwrap_or_else(|_| {
panic!("Should be able to insert new player with id {}", player_id)
});
player_tx
.send(player_id)
.expect("Should be able to send back valid player id");
} else {
println!("db_handler: Failed to get player_tx");
}
// Pair up players
// TODO
} // loop end
});
}

View file

@ -1,14 +1,18 @@
use std::{
sync::mpsc::{sync_channel, Receiver, SyncSender},
time::Duration,
};
use serde_json::Value;
pub fn handle_json(root: Value) -> Result<String, String> {
pub fn handle_json(root: Value, tx: SyncSender<SyncSender<u32>>) -> Result<String, String> {
if let Some(Value::String(type_str)) = root.get("type") {
let (player_tx, player_rx) = sync_channel::<u32>(8);
match type_str.as_str() {
"pairing_request" => handle_pairing_request(root),
"pairing_request" => handle_pairing_request(tx, player_tx, player_rx),
"check_pairing" => handle_check_pairing(root),
"place_token" => handle_place_token(root),
"whose_turn" => handle_whose_turn(root),
"disconnect" => handle_disconnect(root),
"request_board_state" => handle_request_board_state(root),
"game_state" => handle_game_state(root),
_ => Err("{\"type\":\"invalid_type\"}".into()),
}
@ -17,8 +21,22 @@ pub fn handle_json(root: Value) -> Result<String, String> {
}
}
fn handle_pairing_request(root: Value) -> Result<String, String> {
Err("{\"type\":\"unimplemented\"}".into())
fn handle_pairing_request(
tx: SyncSender<SyncSender<u32>>,
player_tx: SyncSender<u32>,
player_rx: Receiver<u32>,
) -> Result<String, String> {
if tx.send(player_tx).is_err() {
return Err("{\"type\":\"pairing_response\", \"status\":\"internal_error\"}".into());
}
if let Ok(pid) = player_rx.recv_timeout(Duration::from_secs(5)) {
Ok(format!(
"{{\"type\":\"pairing_response\", \"id\": \"{}\", \"status\": \"waiting\"}}",
pid
))
} else {
Err("{\"type\":\"pairing_response\", \"status\":\"internal_error_timeout\"}".into())
}
}
fn handle_check_pairing(root: Value) -> Result<String, String> {
@ -29,18 +47,10 @@ fn handle_place_token(root: Value) -> Result<String, String> {
Err("{\"type\":\"unimplemented\"}".into())
}
fn handle_whose_turn(root: Value) -> Result<String, String> {
Err("{\"type\":\"unimplemented\"}".into())
}
fn handle_disconnect(root: Value) -> Result<String, String> {
Err("{\"type\":\"unimplemented\"}".into())
}
fn handle_request_board_state(root: Value) -> Result<String, String> {
Err("{\"type\":\"unimplemented\"}".into())
}
fn handle_game_state(root: Value) -> Result<String, String> {
Err("{\"type\":\"unimplemented\"}".into())
}

View file

@ -1,22 +1,37 @@
mod db_handler;
mod json_handlers;
const SQLITE_DB_PATH: &str = "./fourLineDropper.db";
use std::sync::mpsc::{sync_channel, SyncSender};
use db_handler::start_db_handler_thread;
use warp::{Filter, Rejection};
#[tokio::main]
async fn main() {
let (db_tx, db_rx) = sync_channel::<SyncSender<u32>>(32);
let db_tx_clone = db_tx.clone();
start_db_handler_thread(db_rx, SQLITE_DB_PATH.into());
let route = warp::body::content_length_limit(1024 * 32)
.and(warp::body::bytes())
.and_then(|bytes: bytes::Bytes| async move {
let body_str_result = std::str::from_utf8(bytes.as_ref());
if let Ok(body_str) = body_str_result {
let json_result = serde_json::from_str(body_str);
if let Ok(json_value) = json_result {
Ok(json_handlers::handle_json(json_value).unwrap_or_else(|e| e))
.and_then(move |bytes: bytes::Bytes| {
let db_tx_clone = db_tx_clone.clone();
async move {
let body_str_result = std::str::from_utf8(bytes.as_ref());
if let Ok(body_str) = body_str_result {
let json_result = serde_json::from_str(body_str);
if let Ok(json_value) = json_result {
Ok(json_handlers::handle_json(json_value, db_tx_clone)
.unwrap_or_else(|e| e))
} else {
Ok(String::from("{\"type\": \"invalid_syntax\"}"))
}
} else {
Ok(String::from("{\"type\": \"invalid_syntax\"}"))
Ok::<String, Rejection>(String::from("{\"type\": \"invalid_syntax\"}"))
}
} else {
Ok::<String, Rejection>(String::from("{\"type\": \"invalid_syntax\"}"))
}
});