# 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"
"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"
version = "0.1.0"
dependencies = [
"bytes",
+ "rand",
+ "rusqlite",
"serde_json",
"tokio",
"warp",
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"
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"
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"
"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"
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"
warp = "0.3"
serde_json = "1.0"
bytes = "1.1"
+rusqlite = "0.27.0"
+rand = "0.8.4"
--- /dev/null
+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
+ });
+}
+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()),
}
}
}
-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> {
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())
}
+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\"}"))
}
});