Impl enough backend to assign ids to players
This commit is contained in:
parent
495a11b87f
commit
5a0c0696d1
5 changed files with 231 additions and 23 deletions
74
back_end/Cargo.lock
generated
74
back_end/Cargo.lock
generated
|
@ -2,6 +2,17 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
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]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -104,6 +115,18 @@ dependencies = [
|
||||||
"crypto-common",
|
"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]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
@ -134,6 +157,8 @@ name = "four_line_dropper_backend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
|
"rand",
|
||||||
|
"rusqlite",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"warp",
|
"warp",
|
||||||
|
@ -226,6 +251,18 @@ name = "hashbrown"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
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]]
|
[[package]]
|
||||||
name = "headers"
|
name = "headers"
|
||||||
|
@ -367,6 +404,16 @@ version = "0.2.119"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
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]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.6"
|
version = "0.4.6"
|
||||||
|
@ -546,6 +593,12 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
|
@ -624,6 +677,21 @@ dependencies = [
|
||||||
"winapi",
|
"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]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
|
@ -976,6 +1044,12 @@ version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
|
|
|
@ -10,3 +10,5 @@ tokio = { version = "1", features = ["full"] }
|
||||||
warp = "0.3"
|
warp = "0.3"
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
bytes = "1.1"
|
bytes = "1.1"
|
||||||
|
rusqlite = "0.27.0"
|
||||||
|
rand = "0.8.4"
|
||||||
|
|
107
back_end/src/db_handler.rs
Normal file
107
back_end/src/db_handler.rs
Normal 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
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,14 +1,18 @@
|
||||||
|
use std::{
|
||||||
|
sync::mpsc::{sync_channel, Receiver, SyncSender},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
use serde_json::Value;
|
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") {
|
if let Some(Value::String(type_str)) = root.get("type") {
|
||||||
|
let (player_tx, player_rx) = sync_channel::<u32>(8);
|
||||||
match type_str.as_str() {
|
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),
|
"check_pairing" => handle_check_pairing(root),
|
||||||
"place_token" => handle_place_token(root),
|
"place_token" => handle_place_token(root),
|
||||||
"whose_turn" => handle_whose_turn(root),
|
|
||||||
"disconnect" => handle_disconnect(root),
|
"disconnect" => handle_disconnect(root),
|
||||||
"request_board_state" => handle_request_board_state(root),
|
|
||||||
"game_state" => handle_game_state(root),
|
"game_state" => handle_game_state(root),
|
||||||
_ => Err("{\"type\":\"invalid_type\"}".into()),
|
_ => 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> {
|
fn handle_pairing_request(
|
||||||
Err("{\"type\":\"unimplemented\"}".into())
|
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> {
|
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())
|
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> {
|
fn handle_disconnect(root: Value) -> Result<String, String> {
|
||||||
Err("{\"type\":\"unimplemented\"}".into())
|
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> {
|
fn handle_game_state(root: Value) -> Result<String, String> {
|
||||||
Err("{\"type\":\"unimplemented\"}".into())
|
Err("{\"type\":\"unimplemented\"}".into())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,38 @@
|
||||||
|
mod db_handler;
|
||||||
mod json_handlers;
|
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};
|
use warp::{Filter, Rejection};
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn 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)
|
let route = warp::body::content_length_limit(1024 * 32)
|
||||||
.and(warp::body::bytes())
|
.and(warp::body::bytes())
|
||||||
.and_then(|bytes: bytes::Bytes| async move {
|
.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());
|
let body_str_result = std::str::from_utf8(bytes.as_ref());
|
||||||
if let Ok(body_str) = body_str_result {
|
if let Ok(body_str) = body_str_result {
|
||||||
let json_result = serde_json::from_str(body_str);
|
let json_result = serde_json::from_str(body_str);
|
||||||
if let Ok(json_value) = json_result {
|
if let Ok(json_value) = json_result {
|
||||||
Ok(json_handlers::handle_json(json_value).unwrap_or_else(|e| e))
|
Ok(json_handlers::handle_json(json_value, db_tx_clone)
|
||||||
|
.unwrap_or_else(|e| e))
|
||||||
} else {
|
} else {
|
||||||
Ok(String::from("{\"type\": \"invalid_syntax\"}"))
|
Ok(String::from("{\"type\": \"invalid_syntax\"}"))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok::<String, Rejection>(String::from("{\"type\": \"invalid_syntax\"}"))
|
Ok::<String, Rejection>(String::from("{\"type\": \"invalid_syntax\"}"))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
warp::serve(route).run(([0, 0, 0, 0], 1237)).await;
|
warp::serve(route).run(([0, 0, 0, 0], 1237)).await;
|
||||||
|
|
Loading…
Reference in a new issue