diff --git a/src/main.rs b/src/main.rs index c61adc9..770e48d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,7 +11,6 @@ use tracing::{error, info}; // TODO: Support reconnecting behaviors // TODO: Other tournament types // TODO: Max move wait time -// TODO: Show tournament scoreboard after every round of games // TODO: Tiebreakers, guarantee some amount of going first // TODO: Send moves instantly, sleep only till waiting time @@ -22,15 +21,6 @@ async fn main() -> Result<(), anyhow::Error> { let args: Vec = env::args().collect(); let demo_mode = args.get(1).is_some() && args.get(1).unwrap() == "demo"; - let tournament_type = if !demo_mode { - if let Some(tourney) = args.get(1) { - tourney.clone() - } else { - "round_robin".to_string() - } - } else { - "round_robin".to_string() - }; let admin_password = env::var("ADMIN_AUTH").unwrap_or_else(|_| String::from("admin")); info!("Admin password: {}", admin_password); let admin_password = Arc::new(admin_password); @@ -39,11 +29,7 @@ async fn main() -> Result<(), anyhow::Error> { let listener = TcpListener::bind(&addr).await?; info!("WebSocket server listening on: {}", addr); - let server_data = Arc::new(Server::new( - admin_password.as_ref().clone(), - demo_mode, - tournament_type, - )); + let server_data = Arc::new(Server::new(admin_password.as_ref().clone(), demo_mode)); while let Ok((stream, addr)) = listener.accept().await { tokio::spawn(handle_connection(stream, addr, server_data.clone())); @@ -87,7 +73,7 @@ async fn handle_connection( if parts.len() > 1 { let requested_username = parts[1].to_string(); if let Err(e) = - sd.handle_connect(addr, tx.clone(), requested_username).await + sd.handle_connect_cmd(addr, tx.clone(), requested_username).await { error!("handle_connect: {}", e); let _ = send(&tx, e.to_string().as_str()); @@ -96,6 +82,12 @@ async fn handle_connection( let _ = send(&tx, "ERROR:INVALID:ID:"); } } + "DISCONNECT" => { + if let Err(e) = sd.handle_disconnect_cmd(addr, tx.clone()).await { + error!("handle_disconnect: {}", e); + let _ = send(&tx, e.to_string().as_str()); + } + } "READY" => { if let Err(e) = sd.handle_ready(addr, tx.clone()).await { error!("handle_ready: {}", e); @@ -183,8 +175,11 @@ async fn handle_connection( } } "TOURNAMENT" => { - if parts.get(1) == Some(&"START") { - if let Err(e) = sd.handle_tournament_start(tx.clone(), addr).await { + if parts.get(1) == Some(&"START") && parts.len() > 2 { + if let Err(e) = sd + .handle_tournament_start(tx.clone(), addr, parts[2].to_string()) + .await + { error!("handle_tournament_start: {}", e); let _ = send(&tx, e.to_string().as_str()); } diff --git a/src/server.rs b/src/server.rs index bb802d0..e9fa501 100644 --- a/src/server.rs +++ b/src/server.rs @@ -14,7 +14,7 @@ pub struct Server { } impl Server { - pub fn new(admin_password: String, demo_mode: bool, tournament_type: String) -> Server { + pub fn new(admin_password: String, demo_mode: bool) -> Server { Server { clients: Arc::new(RwLock::new(HashMap::new())), usernames: Arc::new(RwLock::new(HashMap::new())), @@ -30,7 +30,7 @@ impl Server { } // Handler for CONNECT: - pub async fn handle_connect( + pub async fn handle_connect_cmd( &self, addr: SocketAddr, tx: UnboundedSender, @@ -55,6 +55,8 @@ impl Server { drop(clients_guard); + self.remove_observer_from_all_matches(addr).await; + // not taken self.observers.write().await.remove(&addr); self.usernames.write().await.insert(requested_username.clone(), addr); @@ -71,6 +73,35 @@ impl Server { Ok(()) } + pub async fn handle_disconnect_cmd(&self, addr: SocketAddr, tx: UnboundedSender) -> Result<(), anyhow::Error> { + let clients_guard = self.clients.read().await; + let client_opt = clients_guard.get(&addr).cloned(); + + if client_opt.is_none() { + return Err(anyhow::anyhow!("ERROR:INVALID:DISCONNECT")); + } + + drop(clients_guard); + + let mut client = client_opt.as_ref().unwrap().write().await; + self.usernames.write().await.remove(&client.username); + client.ready = false; + client.color = Color::None; + + if client.current_match.is_some() { + let match_id = client.current_match.unwrap(); + drop(client); + + self.terminate_match(match_id).await; + } + + self.clients.write().await.remove(&addr); + self.observers.write().await.insert(addr, tx.clone()); + let _ = send(&tx, "DISCONNECT:ACK"); + + Ok(()) + } + // Handler for READY pub async fn handle_ready( &self, @@ -516,6 +547,7 @@ impl Server { &self, tx: UnboundedSender, addr: SocketAddr, + tournament_type: String, ) -> Result<(), anyhow::Error> { if !self.auth_check(addr).await { return Err(anyhow::anyhow!("ERROR:INVALID:AUTH")); @@ -548,7 +580,9 @@ impl Server { let mut tournament_guard = self.tournament.write().await; *tournament_guard = Some(Arc::new(RwLock::new(tourney))); - let _ = send(&tx, "TOURNAMENT:START:ACK"); + let _ = crate::send(&tx, "TOURNAMENT:START:ACK"); + + self.broadcast_message_all_observers(&format!("TOURNAMENT:START:{}", tournament_type)).await; Ok(()) } @@ -670,6 +704,26 @@ impl Server { Ok(()) } + pub async fn remove_observer_from_all_matches(&self, addr: SocketAddr) { + let matches_guard = self.matches.read().await; + + for match_guard in matches_guard.values() { + let mut found = false; + let mut a_match = match_guard.write().await; + for i in 0..a_match.viewers.len() { + if a_match.viewers[i] == addr { + a_match.viewers.remove(i); + found = true; + break; + } + } + + if found { + break; + } + } + } + pub async fn terminate_match(&self, match_id: u32) { let matches_guard = self.matches.read().await; let the_match = matches_guard.get(&match_id); diff --git a/src/tournaments/round_robin.rs b/src/tournaments/round_robin.rs index b037aa5..5d3bff3 100644 --- a/src/tournaments/round_robin.rs +++ b/src/tournaments/round_robin.rs @@ -116,27 +116,34 @@ impl Tournament for RoundRobin { self.is_completed = true; } + let clients_guard = server.clients.read().await; + let mut player_scores: Vec<(String, u32)> = Vec::new(); + for (_, player_addr) in self.players.iter() { + let player = clients_guard.get(player_addr).unwrap().read().await; + let _ = send(&player.connection.clone(), "TOURNAMENT:END"); + player_scores.push((player.username.clone(), player.score)); + } + drop(clients_guard); + + player_scores.sort_by(|a, b| b.1.cmp(&a.1)); + + let mut message = "TOURNAMENT:SCORES:".to_string(); + for (player, score) in player_scores.iter() { + message.push_str(&format!("{},{}|", player, score)) + } + message.pop(); + + server.broadcast_message_all_observers(&message).await; + if self.is_completed() { // Send scores let clients_guard = server.clients.read().await; - let mut player_scores: Vec<(String, u32)> = Vec::new(); for (_, player_addr) in self.players.iter() { let mut player = clients_guard.get(player_addr).unwrap().write().await; let _ = send(&player.connection.clone(), "TOURNAMENT:END"); - player_scores.push((player.username.clone(), player.score)); player.score = 0; player.round_robin_id = 0; } - - player_scores.sort_by(|a, b| b.1.cmp(&a.1)); - - let mut message = "TOURNAMENT:END:".to_string(); - for (player, score) in player_scores.iter() { - message.push_str(&format!("{},{}|", player, score)) - } - message.pop(); - - server.broadcast_message_all_observers(&message).await; } else { // Create next matches self.create_matches(&server.clients, &server.matches).await;