backend: refactorings, impl "board_state" query
Implemented the fifth backend protocol request/response ("board_state"). Some refactorings involve improving readability from handling unwrapping Option of &Connection objects.
This commit is contained in:
parent
8706f8a90d
commit
fbf47027ef
3 changed files with 299 additions and 127 deletions
|
@ -1,8 +1,10 @@
|
||||||
|
use crate::constants::{COLS, ROWS};
|
||||||
|
|
||||||
use std::sync::mpsc::{Receiver, SyncSender};
|
use std::sync::mpsc::{Receiver, SyncSender};
|
||||||
use std::thread;
|
use std::{fmt, thread};
|
||||||
|
|
||||||
use rand::{thread_rng, Rng};
|
use rand::{thread_rng, Rng};
|
||||||
use rusqlite::{params, Connection};
|
use rusqlite::{params, Connection, Error as RusqliteError};
|
||||||
|
|
||||||
pub type GetIDSenderType = (u32, Option<bool>);
|
pub type GetIDSenderType = (u32, Option<bool>);
|
||||||
/// first bool is player exists,
|
/// first bool is player exists,
|
||||||
|
@ -10,6 +12,50 @@ pub type GetIDSenderType = (u32, Option<bool>);
|
||||||
/// third bool is if cyan player
|
/// third bool is if cyan player
|
||||||
pub type CheckPairingType = (bool, bool, bool);
|
pub type CheckPairingType = (bool, bool, bool);
|
||||||
|
|
||||||
|
pub type BoardStateType = (DBGameState, Option<String>);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum DBGameState {
|
||||||
|
CyanTurn,
|
||||||
|
MagentaTurn,
|
||||||
|
CyanWon,
|
||||||
|
MagentaWon,
|
||||||
|
Draw,
|
||||||
|
NotPaired,
|
||||||
|
OpponentDisconnected,
|
||||||
|
UnknownID,
|
||||||
|
InternalError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DBGameState {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
DBGameState::CyanTurn => write!(f, "cyan_turn"),
|
||||||
|
DBGameState::MagentaTurn => write!(f, "magenta_turn"),
|
||||||
|
DBGameState::CyanWon => write!(f, "cyan_won"),
|
||||||
|
DBGameState::MagentaWon => write!(f, "magenta_won"),
|
||||||
|
DBGameState::Draw => write!(f, "draw"),
|
||||||
|
DBGameState::NotPaired => write!(f, "not_paired"),
|
||||||
|
DBGameState::OpponentDisconnected => write!(f, "opponent_disconnected"),
|
||||||
|
DBGameState::UnknownID => write!(f, "unknown_id"),
|
||||||
|
DBGameState::InternalError => write!(f, "internal_error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<i64> for DBGameState {
|
||||||
|
fn from(value: i64) -> Self {
|
||||||
|
match value {
|
||||||
|
0 => DBGameState::CyanTurn,
|
||||||
|
1 => DBGameState::MagentaTurn,
|
||||||
|
2 => DBGameState::CyanWon,
|
||||||
|
3 => DBGameState::MagentaWon,
|
||||||
|
4 => DBGameState::Draw,
|
||||||
|
_ => DBGameState::InternalError,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum DBHandlerRequest {
|
pub enum DBHandlerRequest {
|
||||||
GetID(SyncSender<GetIDSenderType>),
|
GetID(SyncSender<GetIDSenderType>),
|
||||||
|
@ -17,6 +63,10 @@ pub enum DBHandlerRequest {
|
||||||
id: u32,
|
id: u32,
|
||||||
response_sender: SyncSender<CheckPairingType>,
|
response_sender: SyncSender<CheckPairingType>,
|
||||||
},
|
},
|
||||||
|
GetGameState {
|
||||||
|
id: u32,
|
||||||
|
response_sender: SyncSender<BoardStateType>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -98,30 +148,39 @@ impl DBHandler {
|
||||||
// not paired, can do nothing here
|
// not paired, can do nothing here
|
||||||
}
|
}
|
||||||
|
|
||||||
let send_result = player_tx.send((player_id, is_cyan_player_opt));
|
// don't stop server on send fail, may have timed out and
|
||||||
if let Err(e) = send_result {
|
// dropped the receiver
|
||||||
println!("Failed to send back player id: {:?}", e);
|
player_tx.send((player_id, is_cyan_player_opt)).ok();
|
||||||
self.shutdown_tx.send(()).ok();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
send_result.unwrap();
|
|
||||||
}
|
}
|
||||||
DBHandlerRequest::CheckPairing {
|
DBHandlerRequest::CheckPairing {
|
||||||
id,
|
id,
|
||||||
response_sender,
|
response_sender,
|
||||||
} => {
|
} => {
|
||||||
let check_result = self.check_if_player_is_paired(id);
|
if let Ok((exists, is_paired, is_cyan)) = self.check_if_player_is_paired(None, id) {
|
||||||
if let Ok((exists, is_paired, is_cyan)) = check_result {
|
// don't stop server on send fail, may have timed out and
|
||||||
let send_result = response_sender.send((exists, is_paired, is_cyan));
|
// dropped the receiver
|
||||||
if let Err(e) = send_result {
|
response_sender.send((exists, is_paired, is_cyan)).ok();
|
||||||
println!("Failed to send back check pairing status: {:?}", e);
|
|
||||||
self.shutdown_tx.send(()).ok();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
send_result.unwrap();
|
|
||||||
} else {
|
} else {
|
||||||
|
// On error, just respond that the given player_id doesn't
|
||||||
|
// exist
|
||||||
|
response_sender.send((false, false, true)).ok();
|
||||||
}
|
}
|
||||||
} // DBHandlerRequest::GetID(player_tx)
|
}
|
||||||
|
DBHandlerRequest::GetGameState {
|
||||||
|
id,
|
||||||
|
response_sender,
|
||||||
|
} => {
|
||||||
|
let get_board_result = self.get_board_state(None, id);
|
||||||
|
if get_board_result.is_err() {
|
||||||
|
// don't stop server on send fail, may have timed out and
|
||||||
|
// dropped the receiver
|
||||||
|
response_sender.send((DBGameState::UnknownID, None)).ok();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// don't stop server on send fail, may have timed out and
|
||||||
|
// dropped the receiver
|
||||||
|
response_sender.send(get_board_result.unwrap()).ok();
|
||||||
|
}
|
||||||
} // match db_request
|
} // match db_request
|
||||||
|
|
||||||
false
|
false
|
||||||
|
@ -175,73 +234,75 @@ impl DBHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pair_up_players(&self, conn: Option<&Connection>) -> Result<(), String> {
|
fn pair_up_players(&self, conn: Option<&Connection>) -> Result<(), String> {
|
||||||
if let Some(conn) = conn {
|
if conn.is_none() {
|
||||||
let mut to_pair: Option<u32> = None;
|
return self.pair_up_players(Some(&self.get_conn(DBFirstRun::NotFirstRun)?));
|
||||||
let mut unpaired_players_stmt = conn
|
|
||||||
.prepare("SELECT id FROM players WHERE game_id ISNULL ORDER BY date_added;")
|
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
|
||||||
let mut unpaired_players_rows = unpaired_players_stmt
|
|
||||||
.query([])
|
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
|
||||||
while let Some(row) = unpaired_players_rows
|
|
||||||
.next()
|
|
||||||
.map_err(|e| format!("{:?}", e))?
|
|
||||||
{
|
|
||||||
if to_pair.is_none() {
|
|
||||||
to_pair = Some(row.get(0).map_err(|e| format!("{:?}", e))?);
|
|
||||||
} else {
|
|
||||||
let players: [u32; 2] = [
|
|
||||||
to_pair.take().unwrap(),
|
|
||||||
row.get(0).map_err(|e| format!("{:?}", e))?,
|
|
||||||
];
|
|
||||||
self.create_game(Some(conn), &players)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
let conn = self.get_conn(DBFirstRun::NotFirstRun)?;
|
|
||||||
self.pair_up_players(Some(&conn))
|
|
||||||
}
|
}
|
||||||
|
let conn = conn.unwrap();
|
||||||
|
let mut to_pair: Option<u32> = None;
|
||||||
|
let mut unpaired_players_stmt = conn
|
||||||
|
.prepare("SELECT id FROM players WHERE game_id ISNULL ORDER BY date_added;")
|
||||||
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
|
let mut unpaired_players_rows = unpaired_players_stmt
|
||||||
|
.query([])
|
||||||
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
|
while let Some(row) = unpaired_players_rows
|
||||||
|
.next()
|
||||||
|
.map_err(|e| format!("{:?}", e))?
|
||||||
|
{
|
||||||
|
if to_pair.is_none() {
|
||||||
|
to_pair = Some(row.get(0).map_err(|e| format!("{:?}", e))?);
|
||||||
|
} else {
|
||||||
|
let players: [u32; 2] = [
|
||||||
|
to_pair.take().unwrap(),
|
||||||
|
row.get(0).map_err(|e| format!("{:?}", e))?,
|
||||||
|
];
|
||||||
|
self.create_game(Some(conn), &players)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_game(&self, conn: Option<&Connection>, players: &[u32; 2]) -> Result<u32, String> {
|
fn create_game(&self, conn: Option<&Connection>, players: &[u32; 2]) -> Result<u32, String> {
|
||||||
if let Some(conn) = conn {
|
if conn.is_none() {
|
||||||
let mut game_id: u32 = thread_rng().gen();
|
return self.create_game(Some(&self.get_conn(DBFirstRun::NotFirstRun)?), players);
|
||||||
{
|
|
||||||
let mut get_game_stmt = conn
|
|
||||||
.prepare("SELECT id FROM games WHERE id = ?;")
|
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
|
||||||
while get_game_stmt.query_row([game_id], |_row| Ok(())).is_ok() {
|
|
||||||
game_id = thread_rng().gen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO randomize players (or first-come-first-serve ok to do?)
|
|
||||||
conn.execute(
|
|
||||||
"INSERT INTO games (id, cyan_player, magenta_player, date_added, board, status) VALUES (?, ?, ?, datetime(), ?, 0);",
|
|
||||||
params![game_id, players[0], players[1], new_board()]
|
|
||||||
)
|
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
|
||||||
conn.execute(
|
|
||||||
"UPDATE players SET game_id = ? WHERE id = ?",
|
|
||||||
[game_id, players[0]],
|
|
||||||
)
|
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
|
||||||
conn.execute(
|
|
||||||
"UPDATE players SET game_id = ? WHERE id = ?",
|
|
||||||
[game_id, players[1]],
|
|
||||||
)
|
|
||||||
.map_err(|e| format!("{:?}", e))?;
|
|
||||||
|
|
||||||
Ok(game_id)
|
|
||||||
} else {
|
|
||||||
let conn = self.get_conn(DBFirstRun::NotFirstRun)?;
|
|
||||||
self.create_game(Some(&conn), players)
|
|
||||||
}
|
}
|
||||||
|
let conn = conn.unwrap();
|
||||||
|
let mut game_id: u32 = thread_rng().gen();
|
||||||
|
{
|
||||||
|
let mut get_game_stmt = conn
|
||||||
|
.prepare("SELECT id FROM games WHERE id = ?;")
|
||||||
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
|
while get_game_stmt.query_row([game_id], |_row| Ok(())).is_ok() {
|
||||||
|
game_id = thread_rng().gen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO randomize players (or first-come-first-serve ok to do?)
|
||||||
|
conn.execute(
|
||||||
|
"INSERT INTO games (id, cyan_player, magenta_player, date_added, board, status) VALUES (?, ?, ?, datetime(), ?, 0);",
|
||||||
|
params![game_id, players[0], players[1], new_board()]
|
||||||
|
)
|
||||||
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE players SET game_id = ? WHERE id = ?",
|
||||||
|
[game_id, players[0]],
|
||||||
|
)
|
||||||
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
|
conn.execute(
|
||||||
|
"UPDATE players SET game_id = ? WHERE id = ?",
|
||||||
|
[game_id, players[1]],
|
||||||
|
)
|
||||||
|
.map_err(|e| format!("{:?}", e))?;
|
||||||
|
|
||||||
|
Ok(game_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_if_player_is_paired(&self, player_id: u32) -> Result<CheckPairingType, String> {
|
fn check_if_player_is_paired(
|
||||||
|
&self,
|
||||||
|
conn: Option<&Connection>,
|
||||||
|
player_id: u32,
|
||||||
|
) -> Result<CheckPairingType, String> {
|
||||||
{
|
{
|
||||||
let player_exists_result = self.check_if_player_exists(None, player_id);
|
let player_exists_result = self.check_if_player_exists(None, player_id);
|
||||||
if player_exists_result.is_err() || !player_exists_result.unwrap() {
|
if player_exists_result.is_err() || !player_exists_result.unwrap() {
|
||||||
|
@ -250,7 +311,13 @@ impl DBHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let conn = self.get_conn(DBFirstRun::NotFirstRun)?;
|
if conn.is_none() {
|
||||||
|
return self.check_if_player_is_paired(
|
||||||
|
Some(&self.get_conn(DBFirstRun::NotFirstRun)?),
|
||||||
|
player_id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let conn = conn.unwrap();
|
||||||
|
|
||||||
let check_player_row = conn.query_row("SELECT games.cyan_player FROM players JOIN games where games.id = players.game_id AND players.id = ?;", [player_id], |row| row.get::<usize, u32>(0));
|
let check_player_row = conn.query_row("SELECT games.cyan_player FROM players JOIN games where games.id = players.game_id AND players.id = ?;", [player_id], |row| row.get::<usize, u32>(0));
|
||||||
if let Ok(cyan_player) = check_player_row {
|
if let Ok(cyan_player) = check_player_row {
|
||||||
|
@ -262,12 +329,23 @@ impl DBHandler {
|
||||||
Ok((true, true, false))
|
Ok((true, true, false))
|
||||||
}
|
}
|
||||||
} else if let Err(rusqlite::Error::QueryReturnedNoRows) = check_player_row {
|
} else if let Err(rusqlite::Error::QueryReturnedNoRows) = check_player_row {
|
||||||
// is not paired
|
// either does not exist or is not paired
|
||||||
Ok((true, false, true))
|
let exists_check_result = self.check_if_player_exists(Some(conn), player_id);
|
||||||
|
if let Ok(exists) = exists_check_result {
|
||||||
|
if exists {
|
||||||
|
Ok((true, false, true))
|
||||||
|
} else {
|
||||||
|
Ok((false, false, true))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// pass the error contained in result, making sure the Ok type
|
||||||
|
// is the expected type
|
||||||
|
exists_check_result.map(|_| (false, false, false))
|
||||||
|
}
|
||||||
} else if let Err(e) = check_player_row {
|
} else if let Err(e) = check_player_row {
|
||||||
Err(format!("check_if_player_is_paired: {:?}", e))
|
Err(format!("check_if_player_is_paired: {:?}", e))
|
||||||
} else {
|
} else {
|
||||||
unreachable!();
|
unreachable!("All possible Ok and Err results are already checked");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -276,19 +354,74 @@ impl DBHandler {
|
||||||
conn: Option<&Connection>,
|
conn: Option<&Connection>,
|
||||||
player_id: u32,
|
player_id: u32,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
if let Some(conn) = conn {
|
if conn.is_none() {
|
||||||
let check_player_row =
|
return self
|
||||||
conn.query_row("SELECT id FROM players WHERE id = ?;", [player_id], |row| {
|
.check_if_player_exists(Some(&self.get_conn(DBFirstRun::NotFirstRun)?), player_id);
|
||||||
row.get::<usize, u32>(0)
|
}
|
||||||
});
|
let conn = conn.unwrap();
|
||||||
if let Ok(_id) = check_player_row {
|
let check_player_row =
|
||||||
Ok(true)
|
conn.query_row("SELECT id FROM players WHERE id = ?;", [player_id], |row| {
|
||||||
|
row.get::<usize, u32>(0)
|
||||||
|
});
|
||||||
|
if let Ok(_id) = check_player_row {
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clippy lint allow required due to conn.query_row() needing to handle
|
||||||
|
// returning a tuple in a Result
|
||||||
|
#[allow(clippy::unnecessary_unwrap)]
|
||||||
|
fn get_board_state(
|
||||||
|
&self,
|
||||||
|
conn: Option<&Connection>,
|
||||||
|
player_id: u32,
|
||||||
|
) -> Result<BoardStateType, String> {
|
||||||
|
if conn.is_none() {
|
||||||
|
return self.get_board_state(Some(&self.get_conn(DBFirstRun::NotFirstRun)?), player_id);
|
||||||
|
}
|
||||||
|
let conn = conn.unwrap();
|
||||||
|
|
||||||
|
// TODO maybe handle "opponent_disconnected" case
|
||||||
|
let row_result: Result<(String, i64), RusqliteError> =
|
||||||
|
conn.query_row(
|
||||||
|
"SELECT games.board, games.status FROM games JOIN players WHERE players.id = ? AND games.id = players.game_id;",
|
||||||
|
[player_id],
|
||||||
|
|row| {
|
||||||
|
let board_result = row.get(0);
|
||||||
|
let status_result = row.get(1);
|
||||||
|
if board_result.is_ok() && status_result.is_ok() {
|
||||||
|
Ok((board_result.unwrap(), status_result.unwrap()))
|
||||||
|
} else if board_result.is_err() {
|
||||||
|
board_result
|
||||||
|
.map(|_| (String::from("this value should never be returned"), 0))
|
||||||
|
} else {
|
||||||
|
status_result
|
||||||
|
.map(|_| (String::from("this value should never be returned"), 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if let Ok((board, status)) = row_result {
|
||||||
|
if board.len() != (ROWS * COLS) as usize {
|
||||||
|
Ok((DBGameState::InternalError, None))
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok((DBGameState::from(status), Some(board)))
|
||||||
|
}
|
||||||
|
} else if let Err(RusqliteError::QueryReturnedNoRows) = row_result {
|
||||||
|
// No rows is either player doesn't exist or not paired
|
||||||
|
let (exists, is_paired, _is_cyan) =
|
||||||
|
self.check_if_player_is_paired(Some(conn), player_id)?;
|
||||||
|
if !exists {
|
||||||
|
Ok((DBGameState::UnknownID, None))
|
||||||
|
} else if !is_paired {
|
||||||
|
Ok((DBGameState::NotPaired, None))
|
||||||
|
} else {
|
||||||
|
unreachable!("either exists or is_paired must be false");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let conn = self.get_conn(DBFirstRun::NotFirstRun)?;
|
// TODO use internal error enum instead of string
|
||||||
self.check_if_player_exists(Some(&conn), player_id)
|
Err(String::from("internal_error"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub fn handle_json(
|
||||||
"check_pairing" => handle_check_pairing(root, tx),
|
"check_pairing" => handle_check_pairing(root, tx),
|
||||||
"place_token" => handle_place_token(root),
|
"place_token" => handle_place_token(root),
|
||||||
"disconnect" => handle_disconnect(root),
|
"disconnect" => handle_disconnect(root),
|
||||||
"game_state" => handle_game_state(root),
|
"game_state" => handle_game_state(root, tx),
|
||||||
_ => Err("{\"type\":\"invalid_type\"}".into()),
|
_ => Err("{\"type\":\"invalid_type\"}".into()),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,39 +52,40 @@ fn handle_pairing_request(tx: SyncSender<DBHandlerRequest>) -> Result<String, St
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_check_pairing(root: Value, tx: SyncSender<DBHandlerRequest>) -> Result<String, String> {
|
fn handle_check_pairing(root: Value, tx: SyncSender<DBHandlerRequest>) -> Result<String, String> {
|
||||||
if let Some(Value::Number(id)) = root.get("id") {
|
let id_option = root.get("id");
|
||||||
let (request_tx, request_rx) = sync_channel::<CheckPairingType>(1);
|
if id_option.is_none() {
|
||||||
let player_id = id
|
return Err("{\"type\":\"invalid_syntax\"}".into());
|
||||||
.as_u64()
|
}
|
||||||
.ok_or_else(|| String::from("{\"type\":\"invalid_syntax\"}"))?;
|
let player_id = id_option
|
||||||
let player_id: u32 = player_id
|
.unwrap()
|
||||||
.try_into()
|
.as_u64()
|
||||||
.map_err(|_| String::from("{\"type\":\"invalid_syntax\"}"))?;
|
.ok_or_else(|| String::from("{\"type\":\"invalid_syntax\"}"))?;
|
||||||
if tx
|
let player_id: u32 = player_id
|
||||||
.send(DBHandlerRequest::CheckPairing {
|
.try_into()
|
||||||
id: player_id,
|
.map_err(|_| String::from("{\"type\":\"invalid_syntax\"}"))?;
|
||||||
response_sender: request_tx,
|
let (request_tx, request_rx) = sync_channel::<CheckPairingType>(1);
|
||||||
})
|
if tx
|
||||||
.is_err()
|
.send(DBHandlerRequest::CheckPairing {
|
||||||
{
|
id: player_id,
|
||||||
return Err("{\"type\":\"pairing_response\", \"status\":\"internal_error\"}".into());
|
response_sender: request_tx,
|
||||||
}
|
})
|
||||||
if let Ok((exists, is_paired, is_cyan)) = request_rx.recv_timeout(DB_REQUEST_TIMEOUT) {
|
.is_err()
|
||||||
if !exists {
|
{
|
||||||
Err("{\"type\":\"pairing_response\", \"status\":\"unknown_id\"}".into())
|
return Err("{\"type\":\"pairing_response\", \"status\":\"internal_error\"}".into());
|
||||||
} else if is_paired {
|
}
|
||||||
Ok(format!(
|
if let Ok((exists, is_paired, is_cyan)) = request_rx.recv_timeout(DB_REQUEST_TIMEOUT) {
|
||||||
"{{\"type\":\"pairing_response\", \"status\":\"paired\", \"color\":\"{}\"}}",
|
if !exists {
|
||||||
if is_cyan { "cyan" } else { "magenta" }
|
Err("{\"type\":\"pairing_response\", \"status\":\"unknown_id\"}".into())
|
||||||
))
|
} else if is_paired {
|
||||||
} else {
|
Ok(format!(
|
||||||
Ok("{\"type\"\"pairing_response\", \"status\":\"waiting\"}".into())
|
"{{\"type\":\"pairing_response\", \"status\":\"paired\", \"color\":\"{}\"}}",
|
||||||
}
|
if is_cyan { "cyan" } else { "magenta" }
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Err("{\"type\":\"pairing_response\", \"status\":\"internal_error_timeout\"}".into())
|
Ok("{\"type\"\"pairing_response\", \"status\":\"waiting\"}".into())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err("{\"type\":\"invalid_syntax\"}".into())
|
Err("{\"type\":\"pairing_response\", \"status\":\"internal_error_timeout\"}".into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +97,44 @@ fn handle_disconnect(root: Value) -> Result<String, String> {
|
||||||
Err("{\"type\":\"unimplemented\"}".into())
|
Err("{\"type\":\"unimplemented\"}".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_game_state(root: Value) -> Result<String, String> {
|
fn handle_game_state(root: Value, tx: SyncSender<DBHandlerRequest>) -> Result<String, String> {
|
||||||
Err("{\"type\":\"unimplemented\"}".into())
|
let id_option = root.get("id");
|
||||||
|
if id_option.is_none() {
|
||||||
|
return Err("{\"type\":\"invalid_syntax\"}".into());
|
||||||
|
}
|
||||||
|
let player_id = id_option
|
||||||
|
.unwrap()
|
||||||
|
.as_u64()
|
||||||
|
.ok_or_else(|| String::from("{\"type\":\"invalid_syntax\"}"))?;
|
||||||
|
let player_id: u32 = player_id
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| String::from("{\"type\":\"invalid_syntax\"}"))?;
|
||||||
|
|
||||||
|
let (resp_tx, resp_rx) = sync_channel(1);
|
||||||
|
|
||||||
|
if tx
|
||||||
|
.send(DBHandlerRequest::GetGameState {
|
||||||
|
id: player_id,
|
||||||
|
response_sender: resp_tx,
|
||||||
|
})
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
return Err("{\"type\":\"game_state\", \"status\":\"internal_error\"}".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok((db_game_state, board_string_opt)) = resp_rx.recv_timeout(DB_REQUEST_TIMEOUT) {
|
||||||
|
if let Some(board_string) = board_string_opt {
|
||||||
|
Ok(format!(
|
||||||
|
"{{\"type\":\"game_state\", \"status\":\"{}\", \"board\":\"{}\"}}",
|
||||||
|
db_game_state, board_string
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(format!(
|
||||||
|
"{{\"type\":\"game_state\", \"status\":\"{}\"}}",
|
||||||
|
db_game_state
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err("{\"type\":\"game_state\", \"status\":\"internal_error_timeout\"}".into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,7 +125,7 @@ then the back-end will respond with "too\_many\_players".
|
||||||
"type": "game_state",
|
"type": "game_state",
|
||||||
"status": "not_paired", // or "unknown_id", "cyan_turn", "magenta_turn",
|
"status": "not_paired", // or "unknown_id", "cyan_turn", "magenta_turn",
|
||||||
// "cyan_won", "magenta_won", "draw",
|
// "cyan_won", "magenta_won", "draw",
|
||||||
// "opponent_disconnected"
|
// "opponent_disconnected", "internal_error"
|
||||||
|
|
||||||
// "board" may not be in the response if "unknown_id" is the status
|
// "board" may not be in the response if "unknown_id" is the status
|
||||||
"board": "abcdefg..." // 56-char long string with mapping:
|
"board": "abcdefg..." // 56-char long string with mapping:
|
||||||
|
|
Loading…
Reference in a new issue