};
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};
#[derive(Clone, Debug)]
pub enum DBHandlerRequest {
- GetID(SyncSender<GetIDSenderType>),
+ GetID {
+ response_sender: SyncSender<GetIDSenderType>,
+ phrase: Option<String>,
+ },
CheckPairing {
id: u32,
response_sender: SyncSender<CheckPairingType>,
}
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 {
}
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;
}
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");
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);
}
}
- 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));
}
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)?;
+ }
}
}
) -> 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),
}
}
-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) {