diff --git a/src/main.rs b/src/main.rs index 027b74e..ae5bb6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -168,6 +168,21 @@ async fn handle_connection( let _ = send(&tx, "ERROR:INVALID:TERMINATE"); } } + } else if parts.get(1) == Some(&"AWARD") && parts.len() > 3 { + match parts[2].parse::() { + Ok(match_id) => { + let winner = parts[3].to_string(); + if let Err(e) = + sd.handle_game_award_winner(addr, match_id, winner).await + { + error!("handle_game_award_winner: {}", e); + let _ = send(&tx, e.to_string().as_str()); + } + } + Err(_) => { + let _ = send(&tx, "ERROR:INVALID:AWARD"); + } + } } else { let _ = send(&tx, "ERROR:INVALID:GAME"); } diff --git a/src/server.rs b/src/server.rs index b8cc9c9..f4384b7 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,3 +1,4 @@ +use anyhow::anyhow; use std::time::Instant; use crate::{tournaments::*, types::*, *}; @@ -818,6 +819,127 @@ impl Server { Ok(()) } + pub async fn handle_game_award_winner( + &self, + addr: SocketAddr, + match_id: u32, + winner_username: String, + ) -> Result<(), anyhow::Error> { + if !self.auth_check(addr).await { + return Err(anyhow!("ERROR:INVALID:AUTH")); + } + + let matches_guard = self.matches.read().await; + let found_match = + matches_guard.get(&match_id).ok_or_else(|| anyhow!("ERROR:INVALID:AWARD"))?.clone(); + drop(matches_guard); + + let the_match = found_match.read().await; + + // Validate that the declared winner is actually one of the players in this match + let clients_guard = self.clients.read().await; + let player1_client = clients_guard.get(&the_match.player1); + let player2_client = clients_guard.get(&the_match.player2); + + // If we cannot resolve both players, or the winner username doesn't match either, reject + if let (Some(p1_arc), Some(p2_arc)) = (player1_client, player2_client) { + let p1 = p1_arc.read().await; + let p2 = p2_arc.read().await; + + if winner_username != p1.username && winner_username != p2.username { + return Err(anyhow!("ERROR:INVALID:AWARD")); + } + } else { + return Err(anyhow!("ERROR:INVALID:AWARD")); + } + drop(clients_guard); + + self.matches.write().await.remove(&match_id); + + if let Some(wait_thread) = &the_match.wait_thread { + wait_thread.abort(); + } + + if let Some(timeout_thread) = &the_match.timeout_thread { + timeout_thread.abort(); + } + + self.broadcast_message(&the_match.viewers, &format!("GAME:WIN:{}", winner_username)).await; + + let clients_guard = self.clients.read().await; + if the_match.demo_mode { + let player_win = if winner_username != SERVER_PLAYER_USERNAME { + "WINS" + } else { + "LOSS" + }; + let mut the_player = if the_match.player1 != SERVER_PLAYER_ADDR.parse()? { + clients_guard.get(&the_match.player1).unwrap().write().await + } else { + clients_guard.get(&the_match.player2).unwrap().write().await + }; + + let _ = send(&the_player.connection, &format!("GAME:{}", player_win)); + let _ = send(&the_player.connection, "TOURNAMENT:END"); + + the_player.color = Color::None; + the_player.current_match = None; + + return Ok(()); + } + + let mut player1 = clients_guard.get(&the_match.player1).unwrap().write().await; + let mut player2 = clients_guard.get(&the_match.player2).unwrap().write().await; + + player1.current_match = None; + player1.color = Color::None; + + player2.current_match = None; + player2.color = Color::None; + + let winner_tx = if player1.username == winner_username { + player1.connection.clone() + } else { + player2.connection.clone() + }; + + let loser_tx = if player1.username != winner_username { + player1.connection.clone() + } else { + player2.connection.clone() + }; + + let winner_addr = if player1.username == winner_username { + player1.addr.clone() + } else { + player2.addr.clone() + }; + + drop(player1); + drop(player2); + drop(clients_guard); + + let _ = send(&winner_tx, "GAME:WINS"); + let _ = send(&loser_tx, "GAME:LOSS"); + + if self.tournament.read().await.is_some() { + let mut tournament_guard = self.tournament.write().await; + let tourney = tournament_guard.as_mut().unwrap(); + tourney.write().await.inform_winner(winner_addr, false); + if self.matches.read().await.is_empty() { + tourney.write().await.next(&self).await; + if tourney.read().await.is_completed() { + *tournament_guard = None; + } + } + } else { + let _ = send(&winner_tx, "TOURNAMENT:END"); + let _ = send(&loser_tx, "TOURNAMENT:END"); + } + + Ok(()) + } + pub async fn handle_tournament_start( &self, addr: SocketAddr,