From aaba392352d179f5059aba0210cc3fb4492b1ab3 Mon Sep 17 00:00:00 2001 From: Rem Helper Date: Fri, 6 Mar 2026 00:02:12 +0000 Subject: [PATCH 1/4] Add admin award winner command --- src/main.rs | 15 ++++++ src/server.rs | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) diff --git a/src/main.rs b/src/main.rs index 027b74e..1cf8054 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(addr, match_id, winner).await + { + error!("handle_game_award: {}", 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..775e6cd 100644 --- a/src/server.rs +++ b/src/server.rs @@ -818,6 +818,140 @@ impl Server { Ok(()) } + pub async fn handle_game_award( + &self, + addr: SocketAddr, + match_id: u32, + winner_username: String, + ) -> Result<(), anyhow::Error> { + if !self.auth_check(addr).await { + return Err(anyhow::anyhow!("ERROR:INVALID:AUTH")); + } + + let server_player_addr: SocketAddr = SERVER_PLAYER_ADDR.to_string().parse()?; + + let (player1_addr, player2_addr, viewers, demo_mode) = { + let mut matches_guard = self.matches.write().await; + let the_match = matches_guard + .get(&match_id) + .ok_or_else(|| anyhow::anyhow!("ERROR:INVALID:AWARD"))? + .clone(); + let mut the_match = the_match.write().await; + + if let Some(wait_thread) = &the_match.wait_thread { + wait_thread.abort(); + } + + if let Some(timeout_thread) = &the_match.timeout_thread { + timeout_thread.abort(); + } + + let player1_addr = the_match.player1; + let player2_addr = the_match.player2; + let viewers = the_match.viewers.clone(); + let demo_mode = the_match.demo_mode; + + matches_guard.remove(&match_id); + + (player1_addr, player2_addr, viewers, demo_mode) + }; + + let clients_guard = self.clients.read().await; + let player1_name = if player1_addr == server_player_addr { + SERVER_PLAYER_USERNAME.to_string() + } else { + clients_guard + .get(&player1_addr) + .ok_or_else(|| anyhow::anyhow!("ERROR:INVALID:AWARD"))? + .read() + .await + .username + .clone() + }; + let player2_name = if player2_addr == server_player_addr { + SERVER_PLAYER_USERNAME.to_string() + } else { + clients_guard + .get(&player2_addr) + .ok_or_else(|| anyhow::anyhow!("ERROR:INVALID:AWARD"))? + .read() + .await + .username + .clone() + }; + drop(clients_guard); + + let winner_username = winner_username.trim().to_string(); + let winner_is_player1 = winner_username == player1_name; + let winner_is_player2 = winner_username == player2_name; + + if !winner_is_player1 && !winner_is_player2 { + return Err(anyhow::anyhow!("ERROR:INVALID:AWARD")); + } + + let winner_addr = if winner_is_player1 { + player1_addr + } else { + player2_addr + }; + let loser_addr = if winner_is_player1 { + player2_addr + } else { + player1_addr + }; + + self.broadcast_message(&viewers, &format!("GAME:WIN:{}", winner_username)) + .await; + + let mut clients_guard = self.clients.write().await; + if winner_addr != server_player_addr { + let mut winner = clients_guard + .get_mut(&winner_addr) + .ok_or_else(|| anyhow::anyhow!("ERROR:INVALID:AWARD"))? + .write() + .await; + let _ = send(&winner.connection, "GAME:WINS"); + winner.current_match = None; + winner.color = Color::None; + } + + if loser_addr != server_player_addr { + let mut loser = clients_guard + .get_mut(&loser_addr) + .ok_or_else(|| anyhow::anyhow!("ERROR:INVALID:AWARD"))? + .write() + .await; + let _ = send(&loser.connection, "GAME:LOSS"); + loser.current_match = None; + loser.color = Color::None; + } + drop(clients_guard); + + if self.tournament.read().await.is_some() && self.matches.read().await.is_empty() { + let mut tournament_guard = self.tournament.write().await; + let tourney = tournament_guard.as_mut().unwrap(); + tourney.write().await.inform_winner(winner_addr, false); + tourney.write().await.next(&self).await; + if tourney.read().await.is_completed() { + *tournament_guard = None; + } + } else if !demo_mode && self.tournament.read().await.is_none() { + let clients_guard = self.clients.read().await; + if winner_addr != server_player_addr { + if let Some(winner) = clients_guard.get(&winner_addr) { + let _ = send(&winner.read().await.connection, "TOURNAMENT:END"); + } + } + if loser_addr != server_player_addr { + if let Some(loser) = clients_guard.get(&loser_addr) { + let _ = send(&loser.read().await.connection, "TOURNAMENT:END"); + } + } + } + + Ok(()) + } + pub async fn handle_tournament_start( &self, addr: SocketAddr, -- 2.49.1 From a4e5e25f0c4eece9dff7354b8beaac95912aae92 Mon Sep 17 00:00:00 2001 From: Joshua Higgins Date: Fri, 6 Mar 2026 00:21:54 -0500 Subject: [PATCH 2/4] fix: ai garabage --- src/main.rs | 4 +- src/server.rs | 176 +++++++++++++++++++++----------------------------- 2 files changed, 76 insertions(+), 104 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1cf8054..ae5bb6a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -173,9 +173,9 @@ async fn handle_connection( Ok(match_id) => { let winner = parts[3].to_string(); if let Err(e) = - sd.handle_game_award(addr, match_id, winner).await + sd.handle_game_award_winner(addr, match_id, winner).await { - error!("handle_game_award: {}", e); + error!("handle_game_award_winner: {}", e); let _ = send(&tx, e.to_string().as_str()); } } diff --git a/src/server.rs b/src/server.rs index 775e6cd..9744d2d 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,133 +819,104 @@ impl Server { Ok(()) } - pub async fn handle_game_award( + 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::anyhow!("ERROR:INVALID:AUTH")); + return Err(anyhow!("ERROR:INVALID:AUTH")); } - let server_player_addr: SocketAddr = SERVER_PLAYER_ADDR.to_string().parse()?; + 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 (player1_addr, player2_addr, viewers, demo_mode) = { - let mut matches_guard = self.matches.write().await; - let the_match = matches_guard - .get(&match_id) - .ok_or_else(|| anyhow::anyhow!("ERROR:INVALID:AWARD"))? - .clone(); - let mut the_match = the_match.write().await; + let the_match = found_match.read().await; + if winner_username != the_match.player1.to_string() + && winner_username != the_match.player2.to_string() + { + return Err(anyhow!("ERROR:INVALID:AWARD")); + } - if let Some(wait_thread) = &the_match.wait_thread { - wait_thread.abort(); - } + self.matches.write().await.remove(&match_id); - if let Some(timeout_thread) = &the_match.timeout_thread { - timeout_thread.abort(); - } + if let Some(wait_thread) = &the_match.wait_thread { + wait_thread.abort(); + } - let player1_addr = the_match.player1; - let player2_addr = the_match.player2; - let viewers = the_match.viewers.clone(); - let demo_mode = the_match.demo_mode; + if let Some(timeout_thread) = &the_match.timeout_thread { + timeout_thread.abort(); + } - matches_guard.remove(&match_id); - - (player1_addr, player2_addr, viewers, demo_mode) - }; + self.broadcast_message(&the_match.viewers, &format!("GAME:WIN:{}", winner_username)).await; let clients_guard = self.clients.read().await; - let player1_name = if player1_addr == server_player_addr { - SERVER_PLAYER_USERNAME.to_string() + 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 { - clients_guard - .get(&player1_addr) - .ok_or_else(|| anyhow::anyhow!("ERROR:INVALID:AWARD"))? - .read() - .await - .username - .clone() + player2.connection.clone() }; - let player2_name = if player2_addr == server_player_addr { - SERVER_PLAYER_USERNAME.to_string() + + let loser_tx = if player1.username != winner_username { + player1.connection.clone() } else { - clients_guard - .get(&player2_addr) - .ok_or_else(|| anyhow::anyhow!("ERROR:INVALID:AWARD"))? - .read() - .await - .username - .clone() + 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 winner_username = winner_username.trim().to_string(); - let winner_is_player1 = winner_username == player1_name; - let winner_is_player2 = winner_username == player2_name; + let _ = send(&winner_tx, "GAME:WINS"); + let _ = send(&loser_tx, "GAME:LOSS"); - if !winner_is_player1 && !winner_is_player2 { - return Err(anyhow::anyhow!("ERROR:INVALID:AWARD")); - } - - let winner_addr = if winner_is_player1 { - player1_addr - } else { - player2_addr - }; - let loser_addr = if winner_is_player1 { - player2_addr - } else { - player1_addr - }; - - self.broadcast_message(&viewers, &format!("GAME:WIN:{}", winner_username)) - .await; - - let mut clients_guard = self.clients.write().await; - if winner_addr != server_player_addr { - let mut winner = clients_guard - .get_mut(&winner_addr) - .ok_or_else(|| anyhow::anyhow!("ERROR:INVALID:AWARD"))? - .write() - .await; - let _ = send(&winner.connection, "GAME:WINS"); - winner.current_match = None; - winner.color = Color::None; - } - - if loser_addr != server_player_addr { - let mut loser = clients_guard - .get_mut(&loser_addr) - .ok_or_else(|| anyhow::anyhow!("ERROR:INVALID:AWARD"))? - .write() - .await; - let _ = send(&loser.connection, "GAME:LOSS"); - loser.current_match = None; - loser.color = Color::None; - } - drop(clients_guard); - - if self.tournament.read().await.is_some() && self.matches.read().await.is_empty() { + 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); - tourney.write().await.next(&self).await; - if tourney.read().await.is_completed() { - *tournament_guard = None; - } - } else if !demo_mode && self.tournament.read().await.is_none() { - let clients_guard = self.clients.read().await; - if winner_addr != server_player_addr { - if let Some(winner) = clients_guard.get(&winner_addr) { - let _ = send(&winner.read().await.connection, "TOURNAMENT:END"); - } - } - if loser_addr != server_player_addr { - if let Some(loser) = clients_guard.get(&loser_addr) { - let _ = send(&loser.read().await.connection, "TOURNAMENT:END"); + if self.matches.read().await.is_empty() { + tourney.write().await.next(&self).await; + if tourney.read().await.is_completed() { + *tournament_guard = None; } } } -- 2.49.1 From da49712a79f79bb7f270c612587ab35ce43003ff Mon Sep 17 00:00:00 2001 From: Joshua Higgins Date: Fri, 6 Mar 2026 00:42:47 -0500 Subject: [PATCH 3/4] fix: proper winner username validation Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/server.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/server.rs b/src/server.rs index 9744d2d..1cd75a8 100644 --- a/src/server.rs +++ b/src/server.rs @@ -835,11 +835,24 @@ impl Server { drop(matches_guard); let the_match = found_match.read().await; - if winner_username != the_match.player1.to_string() - && winner_username != the_match.player2.to_string() - { + + // 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); -- 2.49.1 From 667c0d1f181b98f908020b02bb95973e9c9ef507 Mon Sep 17 00:00:00 2001 From: Joshua Higgins Date: Fri, 6 Mar 2026 00:48:46 -0500 Subject: [PATCH 4/4] fix: bug in case of arranged matches --- src/server.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server.rs b/src/server.rs index 1cd75a8..f4384b7 100644 --- a/src/server.rs +++ b/src/server.rs @@ -932,6 +932,9 @@ impl Server { *tournament_guard = None; } } + } else { + let _ = send(&winner_tx, "TOURNAMENT:END"); + let _ = send(&loser_tx, "TOURNAMENT:END"); } Ok(()) -- 2.49.1