back-end: Impl "phrase", update protocol

This commit is contained in:
Stephen Seo 2022-04-27 11:42:28 +09:00
parent 87d93e5b4f
commit f9338d4093
3 changed files with 70 additions and 19 deletions

View file

@ -13,6 +13,7 @@ use crate::constants::{
};
use crate::state::{board_from_string, new_string_board, string_from_board, BoardState, Turn};
use std::collections::HashMap;
use std::sync::mpsc::{Receiver, RecvTimeoutError, SyncSender};
use std::time::{Duration, Instant};
use std::{fmt, thread};
@ -118,7 +119,10 @@ impl fmt::Display for DBPlaceError {
#[derive(Clone, Debug)]
pub enum DBHandlerRequest {
GetID(SyncSender<GetIDSenderType>),
GetID {
response_sender: SyncSender<GetIDSenderType>,
phrase: Option<String>,
},
CheckPairing {
id: u32,
response_sender: SyncSender<CheckPairingType>,
@ -163,7 +167,10 @@ impl DBHandler {
}
let db_request = rx_recv_result.unwrap();
match db_request {
DBHandlerRequest::GetID(player_tx) => {
DBHandlerRequest::GetID {
response_sender,
phrase,
} => {
// got request to create new player, create new player
let conn_result = self.get_conn(DBFirstRun::NotFirstRun);
if let Err(e) = conn_result {
@ -172,10 +179,10 @@ impl DBHandler {
}
let conn = conn_result.unwrap();
let create_player_result = self.create_new_player(Some(&conn));
let create_player_result = self.create_new_player(Some(&conn), phrase);
if let Err(e) = create_player_result {
println!("{}", e);
player_tx.send((None, None)).ok();
response_sender.send((None, None)).ok();
// don't stop server because player limit may have been reached
return false;
}
@ -197,11 +204,11 @@ impl DBHandler {
if paired {
// don't stop server on send fail, may have timed
// out and dropped the receiver
player_tx.send((Some(player_id), Some(is_cyan))).ok();
response_sender.send((Some(player_id), Some(is_cyan))).ok();
} else {
// don't stop server on send fail, may have timed
// out and dropped the receiver
player_tx.send((Some(player_id), None)).ok();
response_sender.send((Some(player_id), None)).ok();
}
} else {
println!("Internal error, created player doesn't exist");
@ -341,7 +348,11 @@ impl DBHandler {
Ok(())
}
fn create_new_player(&self, conn: Option<&Connection>) -> Result<u32, String> {
fn create_new_player(
&self,
conn: Option<&Connection>,
phrase: Option<String>,
) -> Result<u32, String> {
let mut _conn_result = Err(String::new());
let conn = if conn.is_none() {
_conn_result = self.get_conn(DBFirstRun::NotFirstRun);
@ -380,7 +391,10 @@ impl DBHandler {
}
}
let insert_result = conn.execute("INSERT INTO players (id) VALUES (?);", [player_id]);
let insert_result = conn.execute(
"INSERT INTO players (id, phrase) VALUES (?, ?);",
params![player_id, phrase],
);
if let Err(e) = insert_result {
return Err(format!("Failed to insert player into db: {:?}", e));
}
@ -399,23 +413,39 @@ impl DBHandler {
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;")
.prepare("SELECT id, phrase 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))?;
let mut phrase_map: HashMap<String, u32> = HashMap::new();
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))?);
if let Ok(phrase_text) = row.get::<usize, String>(1) {
// pair players with matching phrases
if let Some(matching_player_id) = phrase_map.get(&phrase_text) {
let players: [u32; 2] = [
*matching_player_id,
row.get(0).map_err(|e| format!("{:?}", e))?,
];
self.create_game(Some(conn), &players)?;
phrase_map.remove(&phrase_text);
} else {
phrase_map.insert(phrase_text, 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)?;
// pair players that did not use a phrase
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)?;
}
}
}

View file

@ -24,7 +24,7 @@ pub fn handle_json(
) -> Result<String, String> {
if let Some(Value::String(type_str)) = root.get("type") {
match type_str.as_str() {
"pairing_request" => handle_pairing_request(tx),
"pairing_request" => handle_pairing_request(root, tx),
"check_pairing" => handle_check_pairing(root, tx),
"place_token" => handle_place_token(root, tx),
"disconnect" => handle_disconnect(root, tx),
@ -36,9 +36,21 @@ pub fn handle_json(
}
}
fn handle_pairing_request(tx: SyncSender<DBHandlerRequest>) -> Result<String, String> {
fn handle_pairing_request(root: Value, tx: SyncSender<DBHandlerRequest>) -> Result<String, String> {
let (player_tx, player_rx) = sync_channel::<GetIDSenderType>(1);
if tx.send(DBHandlerRequest::GetID(player_tx)).is_err() {
let mut phrase: Option<String> = None;
if let Some(phrase_text) = root.get("phrase") {
if let Some(phrase_str) = phrase_text.as_str() {
phrase = Some(phrase_str.to_owned());
}
}
if tx
.send(DBHandlerRequest::GetID {
response_sender: player_tx,
phrase,
})
.is_err()
{
return Err("{\"type\":\"pairing_response\", \"status\":\"internal_error\"}".into());
}
if let Ok((pid_opt, is_cyan_opt)) = player_rx.recv_timeout(DB_REQUEST_TIMEOUT) {

View file

@ -14,6 +14,15 @@ of the request, and the backend will respond with JSON.
}
```
An optional "phrase" parameter can be sent to match against other players with
the same "phrase".
```
{
"type": "pairing_request",
"phrase": "user_defined_phrase",
}
```
2. Check pairing status
```