]> git.seodisparate.com - EN605.607.81.SP22_ASDM_Project/commitdiff
Impl enough backend to assign ids to players
authorStephen Seo <seo.disparate@gmail.com>
Fri, 18 Mar 2022 10:29:38 +0000 (19:29 +0900)
committerStephen Seo <seo.disparate@gmail.com>
Fri, 18 Mar 2022 10:29:38 +0000 (19:29 +0900)
back_end/Cargo.lock
back_end/Cargo.toml
back_end/src/db_handler.rs [new file with mode: 0644]
back_end/src/json_handlers.rs
back_end/src/main.rs

index 0ab459ce314adc1007a9a5d7ffc38807254216ff..200c91fd3391ec873a73b244a272eab8c7639fe1 100644 (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"
index 6f88a142d12dfaa73d214a107bd505e6c0d3cc7b..2149c40b735f03bbb23e8f1d332a607b92030d01 100644 (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"
diff --git a/back_end/src/db_handler.rs b/back_end/src/db_handler.rs
new file mode 100644 (file)
index 0000000..de42c2b
--- /dev/null
@@ -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
+    });
+}
index cbccb1bc02ec4209b8727e7211e8a48b9b4792b0..3d9b6d973afc8749c8e58e7adc8b9fa8de6bc627 100644 (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())
 }
index c4b39da7858b68739cea1b1b5d963a87122b89be..8b82821cf47004fd20f7d177033c5f80f6beb1cf 100644 (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\"}"))
             }
         });