feat: add admin award winner command (#23)

Co-authored-by: Joshua Higgins <joshuafhiggins@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit was merged in pull request #23.
This commit is contained in:
2026-03-06 00:49:50 -05:00
committed by GitHub
Unverified
parent c754b2ec72
commit 8538068442
2 changed files with 137 additions and 0 deletions

View File

@@ -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::<u32>() {
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");
}

View File

@@ -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,