feat: knockout brackets
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
const WebSocket = require("ws");
|
||||
const readline = require("readline");
|
||||
|
||||
const DEFAULT_URL = "wss://connect4.abunchofknowitalls.com";
|
||||
const DEFAULT_URL = "ws://localhost:8080";
|
||||
|
||||
let ws;
|
||||
let pingInterval;
|
||||
|
||||
147
src/server.rs
147
src/server.rs
@@ -429,6 +429,9 @@ impl Server {
|
||||
invalid = true;
|
||||
}
|
||||
|
||||
let player1 = current_match.player1.clone();
|
||||
let player2 = current_match.player2.clone();
|
||||
|
||||
// Terminate games if a player makes an invalid move
|
||||
if invalid {
|
||||
let current_match_id = current_match.id;
|
||||
@@ -468,7 +471,16 @@ impl Server {
|
||||
|
||||
let mut tournament_guard = self.tournament.write().await;
|
||||
let tourney = tournament_guard.as_mut().unwrap();
|
||||
tourney.write().await.inform_winner(opponent_username, false);
|
||||
tourney
|
||||
.write()
|
||||
.await
|
||||
.inform_winner(
|
||||
opponent_username,
|
||||
current_match_id,
|
||||
player1.clone(),
|
||||
player2.clone(),
|
||||
)
|
||||
.await;
|
||||
drop(tournament_guard);
|
||||
|
||||
self.matches.write().await.remove(¤t_match_id).unwrap();
|
||||
@@ -539,32 +551,55 @@ impl Server {
|
||||
|
||||
matches_guard.remove(¤t_match_id).unwrap();
|
||||
|
||||
if self.tournament.read().await.is_some() && matches_guard.is_empty() {
|
||||
drop(matches_guard);
|
||||
drop(clients_guard);
|
||||
|
||||
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(username.clone(), filled);
|
||||
tourney.write().await.next(&self).await;
|
||||
if tourney.read().await.is_completed() {
|
||||
*tournament_guard = None;
|
||||
}
|
||||
} else if self.tournament.read().await.is_none() {
|
||||
let _ = send(&tx, "TOURNAMENT:END");
|
||||
if !is_demo_mode {
|
||||
let opponent = opponent.clone().unwrap();
|
||||
let opponent = opponent.read().await;
|
||||
let _ = send(&opponent.connection, "TOURNAMENT:END");
|
||||
let winner = if filled {
|
||||
String::new()
|
||||
} else {
|
||||
username.clone()
|
||||
};
|
||||
|
||||
tourney
|
||||
.write()
|
||||
.await
|
||||
.inform_winner(winner, current_match_id, player1.clone(), player2.clone())
|
||||
.await;
|
||||
|
||||
if matches_guard.is_empty() {
|
||||
drop(matches_guard);
|
||||
drop(clients_guard);
|
||||
|
||||
tourney.write().await.next(&self).await;
|
||||
if tourney.read().await.is_completed() {
|
||||
let tournament_players = tourney.read().await.get_players();
|
||||
let clients_guard = self.clients.read().await;
|
||||
|
||||
for player in tournament_players {
|
||||
let player = clients_guard.get(&player);
|
||||
|
||||
if player.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let player = player.unwrap().read().await;
|
||||
let _ = send(&player.connection, "TOURNAMENT:END");
|
||||
}
|
||||
|
||||
*tournament_guard = None;
|
||||
|
||||
self.broadcast("TOURNAMENT:END").await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let default_waiting_time = *self.waiting_timeout.read().await;
|
||||
let mut adjusted_waiting =
|
||||
default_waiting_time as i64 + (rand::rng().random_range(0..=50) - 25);
|
||||
let set_waiting_time = *self.waiting_timeout.read().await;
|
||||
let mut variance = rand::rng().random_range(0..=(set_waiting_time / 200)) as i64;
|
||||
variance *= rand::rng().random_range(0..=2) - 1;
|
||||
let mut adjusted_waiting = set_waiting_time as i64 + variance;
|
||||
let current_move_time = Instant::now();
|
||||
|
||||
if current_match.ledger.is_empty() {
|
||||
@@ -609,7 +644,7 @@ impl Server {
|
||||
}
|
||||
|
||||
if demo_mode && no_winner {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(default_waiting_time)).await;
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(set_waiting_time)).await;
|
||||
let _ = send(&client_tx, &format!("OPPONENT:{}", demo_move));
|
||||
let observers_guard = observers.read().await;
|
||||
let msg = format!(
|
||||
@@ -640,7 +675,8 @@ impl Server {
|
||||
tokio::time::sleep(tokio::time::Duration::from_millis(max_timeout as u64)).await;
|
||||
|
||||
let matches_guard = matches.read().await;
|
||||
let the_match = matches_guard.get(&match_id);
|
||||
let the_match = matches_guard.get(&match_id).cloned();
|
||||
drop(matches_guard);
|
||||
if let Some(the_match) = the_match {
|
||||
let the_match = the_match.read().await;
|
||||
if the_match.ledger.len() == ledger_size {
|
||||
@@ -670,7 +706,7 @@ impl Server {
|
||||
|
||||
let mut tournament_guard = tournament.write().await;
|
||||
let tourney = tournament_guard.as_mut().unwrap();
|
||||
tourney.write().await.inform_winner(client_username, false);
|
||||
tourney.write().await.inform_winner(client_username, match_id, player1, player2).await;
|
||||
drop(tournament_guard);
|
||||
|
||||
matches.write().await.remove(&match_id).unwrap();
|
||||
@@ -820,7 +856,23 @@ impl Server {
|
||||
let tourney = tournament_guard.as_mut().unwrap();
|
||||
tourney.write().await.next(&self).await;
|
||||
if tourney.read().await.is_completed() {
|
||||
let tournament_players = tourney.read().await.get_players();
|
||||
let clients_guard = self.clients.read().await;
|
||||
|
||||
for player in tournament_players {
|
||||
let player = clients_guard.get(&player);
|
||||
|
||||
if player.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let player = player.unwrap().read().await;
|
||||
let _ = send(&player.connection, "TOURNAMENT:END");
|
||||
}
|
||||
|
||||
*tournament_guard = None;
|
||||
|
||||
self.broadcast("TOURNAMENT:END").await;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -926,11 +978,36 @@ impl Server {
|
||||
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_username, false);
|
||||
tourney
|
||||
.write()
|
||||
.await
|
||||
.inform_winner(
|
||||
winner_username,
|
||||
match_id,
|
||||
the_match.player1.clone(),
|
||||
the_match.player2.clone(),
|
||||
)
|
||||
.await;
|
||||
if self.matches.read().await.is_empty() {
|
||||
tourney.write().await.next(&self).await;
|
||||
if tourney.read().await.is_completed() {
|
||||
let tournament_players = tourney.read().await.get_players();
|
||||
let clients_guard = self.clients.read().await;
|
||||
|
||||
for player in tournament_players {
|
||||
let player = clients_guard.get(&player);
|
||||
|
||||
if player.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let player = player.unwrap().read().await;
|
||||
let _ = send(&player.connection, "TOURNAMENT:END");
|
||||
}
|
||||
|
||||
*tournament_guard = None;
|
||||
|
||||
self.broadcast("TOURNAMENT:END").await;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -968,14 +1045,26 @@ impl Server {
|
||||
|
||||
drop(clients_guard);
|
||||
|
||||
let mut tourney = match tournament_type.as_str() {
|
||||
"RoundRobin" => RoundRobin::new(&ready_players),
|
||||
&_ => RoundRobin::new(&ready_players),
|
||||
};
|
||||
tourney.start(&self).await;
|
||||
let tourney: Option<Arc<RwLock<dyn Tournament + Send + Sync + 'static>>> =
|
||||
match tournament_type.as_str() {
|
||||
"RoundRobin" => Some(Arc::new(RwLock::new(
|
||||
RoundRobin::new(&ready_players, &self).await,
|
||||
))),
|
||||
"KnockoutBracket" => Some(Arc::new(RwLock::new(
|
||||
KnockoutBracket::new(&ready_players, &self).await,
|
||||
))),
|
||||
&_ => None,
|
||||
};
|
||||
|
||||
if tourney.is_none() {
|
||||
return Err(anyhow::anyhow!("ERROR:INVALID:TOURNAMENT"));
|
||||
}
|
||||
|
||||
let tourney = tourney.unwrap();
|
||||
tourney.write().await.start(&self).await;
|
||||
|
||||
let mut tournament_guard = self.tournament.write().await;
|
||||
*tournament_guard = Some(Arc::new(RwLock::new(tourney)));
|
||||
*tournament_guard = Some(tourney);
|
||||
|
||||
// Clear any pending reservations when a tournament starts
|
||||
self.reservations.write().await.clear();
|
||||
|
||||
292
src/tournaments/knockout_bracket.rs
Normal file
292
src/tournaments/knockout_bracket.rs
Normal file
@@ -0,0 +1,292 @@
|
||||
use async_trait::async_trait;
|
||||
|
||||
use crate::{
|
||||
server::*,
|
||||
tournaments::{RoundRobin, Tournament},
|
||||
*,
|
||||
};
|
||||
|
||||
type Score = u32;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct KnockoutBracket {
|
||||
pub blitz_round_robin: RoundRobin,
|
||||
pub players: Vec<(String, Score, bool)>,
|
||||
pub pairings: Vec<String>,
|
||||
pub current_matches: Vec<u32>,
|
||||
pub previous_wait: u64,
|
||||
pub completed: bool,
|
||||
pub started: bool,
|
||||
pub clients: Clients,
|
||||
pub matches: Matches,
|
||||
pub usernames: Vec<String>,
|
||||
}
|
||||
|
||||
impl KnockoutBracket {
|
||||
async fn create_matches(&mut self) {
|
||||
let clients_guard = self.clients.read().await;
|
||||
|
||||
let mut i = 0;
|
||||
while i < self.pairings.len() {
|
||||
let player1_username = self.pairings[i].clone();
|
||||
let player2_username = self.pairings[i + 1].clone();
|
||||
|
||||
let match_id: u32 = gen_match_id(&self.matches).await;
|
||||
self.current_matches.push(match_id);
|
||||
let new_match = Arc::new(RwLock::new(Match::new(
|
||||
match_id,
|
||||
player1_username.clone(),
|
||||
player2_username.clone(),
|
||||
false,
|
||||
)));
|
||||
|
||||
let match_guard = new_match.read().await;
|
||||
let mut player1 = clients_guard.get(&player1_username).unwrap().write().await;
|
||||
|
||||
player1.current_match = Some(match_id);
|
||||
player1.ready = false;
|
||||
|
||||
if match_guard.player1 == player1_username {
|
||||
player1.color = Color::Red;
|
||||
let _ = send(&player1.connection, "GAME:START:1");
|
||||
} else {
|
||||
player1.color = Color::Yellow;
|
||||
let _ = send(&player1.connection, "GAME:START:0");
|
||||
}
|
||||
|
||||
drop(player1);
|
||||
|
||||
let mut player2 = clients_guard.get(&player2_username).unwrap().write().await;
|
||||
|
||||
player2.current_match = Some(match_id);
|
||||
player2.ready = false;
|
||||
|
||||
if match_guard.player1 == player2_username {
|
||||
player2.color = Color::Red;
|
||||
let _ = send(&player2.connection, "GAME:START:1");
|
||||
} else {
|
||||
player2.color = Color::Yellow;
|
||||
let _ = send(&player2.connection, "GAME:START:0");
|
||||
}
|
||||
|
||||
drop(player2);
|
||||
|
||||
self.matches.write().await.insert(match_id, new_match.clone());
|
||||
|
||||
i += 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Tournament for KnockoutBracket {
|
||||
async fn new(ready_players: &[String], server: &Server) -> KnockoutBracket {
|
||||
let previous_wait = server.waiting_timeout.read().await.clone();
|
||||
|
||||
*server.waiting_timeout.write().await = 5;
|
||||
|
||||
KnockoutBracket {
|
||||
blitz_round_robin: RoundRobin::new(ready_players, server).await,
|
||||
players: Vec::new(),
|
||||
pairings: Vec::new(),
|
||||
current_matches: Vec::new(),
|
||||
previous_wait,
|
||||
completed: false,
|
||||
started: false,
|
||||
clients: server.clients.clone(),
|
||||
matches: server.matches.clone(),
|
||||
usernames: ready_players.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn next(&mut self, server: &Server) {
|
||||
if self.completed {
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.started {
|
||||
self.blitz_round_robin.next(server).await;
|
||||
}
|
||||
|
||||
if self.blitz_round_robin.completed && !self.started {
|
||||
*server.waiting_timeout.write().await = self.previous_wait;
|
||||
|
||||
let mut players = Vec::new();
|
||||
for player in self.blitz_round_robin.players.values() {
|
||||
players.push((player.0.clone(), player.1, false));
|
||||
}
|
||||
|
||||
players.sort_by(|a, b| b.1.cmp(&a.1));
|
||||
self.players = players;
|
||||
|
||||
for player in &self.players {
|
||||
self.pairings.push(player.0.clone());
|
||||
}
|
||||
|
||||
self.create_matches().await;
|
||||
|
||||
self.started = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if self.pairings.len() == 1 {
|
||||
self.completed = true;
|
||||
} else {
|
||||
self.pairings.retain(|p| !p.is_empty());
|
||||
self.create_matches().await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn start(&mut self, server: &Server) {
|
||||
self.blitz_round_robin.start(server).await;
|
||||
}
|
||||
|
||||
async fn cancel(&mut self, server: &Server) {
|
||||
if !self.started {
|
||||
self.blitz_round_robin.cancel(server).await;
|
||||
return;
|
||||
}
|
||||
|
||||
for match_id in &self.current_matches {
|
||||
server.terminate_match(*match_id).await;
|
||||
}
|
||||
|
||||
let clients_guard = server.clients.read().await;
|
||||
for username in &self.players {
|
||||
let client = clients_guard.get(&username.0).cloned();
|
||||
if client.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let client = client.unwrap();
|
||||
let client = client.read().await;
|
||||
|
||||
let _ = send(&client.connection, "TOURNAMENT:END");
|
||||
}
|
||||
}
|
||||
|
||||
async fn inform_winner(
|
||||
&mut self,
|
||||
winner: String,
|
||||
match_id: u32,
|
||||
player1: String,
|
||||
player2: String,
|
||||
) {
|
||||
if !self.started {
|
||||
self.blitz_round_robin.inform_winner(winner, match_id, player1, player2).await;
|
||||
return;
|
||||
}
|
||||
|
||||
let mut winner = winner;
|
||||
|
||||
// there's a tie
|
||||
if winner.is_empty() {
|
||||
let mut player1_track = (String::new(), 0, false);
|
||||
let mut player2_track = (String::new(), 0, false);
|
||||
|
||||
for player in self.players.iter_mut() {
|
||||
if player.0 == player1 {
|
||||
player1_track = player.clone();
|
||||
} else if player.0 == player2 {
|
||||
player2_track = player.clone();
|
||||
}
|
||||
|
||||
if !player1_track.0.is_empty() && !player2_track.0.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if player1_track.2 {
|
||||
if player1_track.1 < player2_track.1 {
|
||||
winner = player2_track.0.clone();
|
||||
} else {
|
||||
winner = player1_track.0.clone();
|
||||
}
|
||||
}
|
||||
|
||||
let match_id: u32 = gen_match_id(&self.matches).await;
|
||||
self.current_matches.push(match_id);
|
||||
let new_match = Arc::new(RwLock::new(Match::new(
|
||||
match_id,
|
||||
player1.clone(),
|
||||
player2.clone(),
|
||||
false,
|
||||
)));
|
||||
|
||||
let match_guard = new_match.read().await;
|
||||
let clients_guard = self.clients.read().await;
|
||||
let mut player1 = clients_guard.get(&player1).unwrap().write().await;
|
||||
|
||||
player1.current_match = Some(match_id);
|
||||
player1.ready = false;
|
||||
|
||||
if match_guard.player1 == player1.username {
|
||||
player1.color = Color::Red;
|
||||
let _ = send(&player1.connection, "GAME:START:1");
|
||||
} else {
|
||||
player1.color = Color::Yellow;
|
||||
let _ = send(&player1.connection, "GAME:START:0");
|
||||
}
|
||||
|
||||
drop(player1);
|
||||
|
||||
let mut player2 = clients_guard.get(&player2).unwrap().write().await;
|
||||
|
||||
player2.current_match = Some(match_id);
|
||||
player2.ready = false;
|
||||
|
||||
if match_guard.player1 == player2.username {
|
||||
player2.color = Color::Red;
|
||||
let _ = send(&player2.connection, "GAME:START:1");
|
||||
} else {
|
||||
player2.color = Color::Yellow;
|
||||
let _ = send(&player2.connection, "GAME:START:0");
|
||||
}
|
||||
|
||||
drop(player2);
|
||||
|
||||
self.current_matches.push(match_id);
|
||||
self.matches.write().await.insert(match_id, new_match.clone());
|
||||
}
|
||||
|
||||
let mut loser = String::new();
|
||||
for i in 0..self.pairings.len() {
|
||||
if self.pairings[i] == winner {
|
||||
if i % 2 == 0 {
|
||||
loser = self.pairings[i + 1].clone();
|
||||
self.pairings[i + 1].clear();
|
||||
} else {
|
||||
loser = self.pairings[i - 1].clone();
|
||||
self.pairings[i - 1].clear();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset tie tracking
|
||||
for player in self.players.iter_mut() {
|
||||
if player.0 == winner || player.0 == loser {
|
||||
player.2 = false;
|
||||
}
|
||||
}
|
||||
|
||||
self.current_matches.retain(|v| *v != match_id);
|
||||
}
|
||||
|
||||
fn contains_player(&self, username: String) -> bool {
|
||||
self.usernames.contains(&username)
|
||||
}
|
||||
|
||||
fn is_completed(&self) -> bool {
|
||||
self.completed
|
||||
}
|
||||
|
||||
fn get_players(&self) -> Vec<String> {
|
||||
self.usernames.clone()
|
||||
}
|
||||
|
||||
fn get_type(&self) -> String {
|
||||
"KnockoutBracket".to_string()
|
||||
}
|
||||
}
|
||||
@@ -4,17 +4,26 @@ use crate::server::Server;
|
||||
|
||||
pub mod round_robin;
|
||||
pub use round_robin::RoundRobin;
|
||||
pub mod knockout_bracket;
|
||||
pub use knockout_bracket::KnockoutBracket;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Tournament {
|
||||
fn new(ready_players: &[String]) -> Self
|
||||
async fn new(ready_players: &[String], server: &Server) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
async fn next(&mut self, server: &Server);
|
||||
async fn start(&mut self, server: &Server);
|
||||
async fn cancel(&mut self, server: &Server);
|
||||
fn inform_winner(&mut self, winner: String, is_tie: bool);
|
||||
async fn inform_winner(
|
||||
&mut self,
|
||||
winner: String,
|
||||
match_id: u32,
|
||||
player1: String,
|
||||
player2: String,
|
||||
);
|
||||
fn contains_player(&self, username: String) -> bool;
|
||||
fn is_completed(&self) -> bool;
|
||||
fn get_players(&self) -> Vec<String>;
|
||||
fn get_type(&self) -> String;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use tracing::info;
|
||||
|
||||
use crate::{server::Server, *};
|
||||
|
||||
@@ -12,11 +13,13 @@ pub struct RoundRobin {
|
||||
pub players: HashMap<ID, (String, Score)>,
|
||||
pub top_half: Vec<ID>,
|
||||
pub bottom_half: Vec<ID>,
|
||||
pub is_completed: bool,
|
||||
pub completed: bool,
|
||||
pub current_matches: Vec<ID>,
|
||||
pub usernames: Vec<String>,
|
||||
}
|
||||
|
||||
impl RoundRobin {
|
||||
async fn create_matches(&self, clients: &Clients, matches: &Matches) {
|
||||
async fn create_matches(&mut self, clients: &Clients, matches: &Matches) {
|
||||
let clients_guard = clients.read().await;
|
||||
for (i, id) in self.top_half.iter().enumerate() {
|
||||
let player1_username = self.players.get(id).unwrap();
|
||||
@@ -35,6 +38,8 @@ impl RoundRobin {
|
||||
false,
|
||||
)));
|
||||
|
||||
self.current_matches.push(match_id.clone());
|
||||
|
||||
let match_guard = new_match.read().await;
|
||||
let mut player1 = clients_guard.get(&player1_username.0).unwrap().write().await;
|
||||
|
||||
@@ -73,12 +78,14 @@ impl RoundRobin {
|
||||
|
||||
#[async_trait]
|
||||
impl Tournament for RoundRobin {
|
||||
fn new(ready_players: &[String]) -> RoundRobin {
|
||||
async fn new(ready_players: &[String], _: &Server) -> RoundRobin {
|
||||
let mut result = RoundRobin {
|
||||
players: HashMap::new(),
|
||||
top_half: Vec::new(),
|
||||
bottom_half: Vec::new(),
|
||||
is_completed: false,
|
||||
completed: false,
|
||||
current_matches: Vec::new(),
|
||||
usernames: ready_players.to_vec(),
|
||||
};
|
||||
|
||||
let size = ready_players.len();
|
||||
@@ -98,8 +105,10 @@ impl Tournament for RoundRobin {
|
||||
result
|
||||
}
|
||||
|
||||
fn inform_winner(&mut self, winner: String, is_tie: bool) {
|
||||
if is_tie {
|
||||
async fn inform_winner(&mut self, winner: String, match_id: u32, _: String, _: String) {
|
||||
info!("RoundRobin: told winner was \"{}\"", winner);
|
||||
|
||||
if winner.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,24 +118,17 @@ impl Tournament for RoundRobin {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_player(&self, username: String) -> bool {
|
||||
for (_, (player_username, _)) in self.players.iter() {
|
||||
if *player_username == username {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
self.current_matches.retain(|id| !(*id == match_id));
|
||||
}
|
||||
|
||||
async fn next(&mut self, server: &Server) {
|
||||
if self.is_completed {
|
||||
if self.completed {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.top_half.len() <= 1 || self.bottom_half.is_empty() {
|
||||
self.is_completed = true;
|
||||
self.completed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -138,20 +140,20 @@ impl Tournament for RoundRobin {
|
||||
|
||||
let expected_bottom_start = self.top_half.len() as u32;
|
||||
if self.top_half[1] == 1 && self.bottom_half[0] == expected_bottom_start {
|
||||
self.is_completed = true;
|
||||
self.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.0).unwrap().read().await;
|
||||
let _ = send(&player.connection.clone(), "TOURNAMENT:END");
|
||||
player_scores.push((player.username.clone(), player_addr.1));
|
||||
}
|
||||
drop(clients_guard);
|
||||
|
||||
player_scores.sort_by(|a, b| b.1.cmp(&a.1));
|
||||
|
||||
// Send scores
|
||||
let mut message = "TOURNAMENT:SCORES:".to_string();
|
||||
for (player, score) in player_scores.iter() {
|
||||
message.push_str(&format!("{},{}|", player, score))
|
||||
@@ -160,15 +162,7 @@ impl Tournament for RoundRobin {
|
||||
|
||||
server.broadcast(&message).await;
|
||||
|
||||
if self.is_completed() {
|
||||
// Send scores
|
||||
let clients_guard = server.clients.read().await;
|
||||
for (_, player_addr) in self.players.iter() {
|
||||
let player = clients_guard.get(&player_addr.0).unwrap().read().await;
|
||||
let _ = send(&player.connection.clone(), "TOURNAMENT:END");
|
||||
}
|
||||
} else {
|
||||
// Create next matches
|
||||
if !self.is_completed() {
|
||||
self.create_matches(&server.clients, &server.matches).await;
|
||||
}
|
||||
}
|
||||
@@ -178,36 +172,32 @@ impl Tournament for RoundRobin {
|
||||
}
|
||||
|
||||
async fn cancel(&mut self, server: &Server) {
|
||||
for (_, addr) in self.players.iter() {
|
||||
let clients_guard = server.clients.read().await;
|
||||
for match_id in &self.current_matches {
|
||||
server.terminate_match(*match_id).await;
|
||||
}
|
||||
|
||||
let client = clients_guard.get(&addr.0);
|
||||
let clients_guard = server.clients.read().await;
|
||||
for (_, (username, _)) in self.players.iter() {
|
||||
let client = clients_guard.get(username);
|
||||
if client.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let client = client.unwrap().read().await;
|
||||
let client_connection = client.connection.clone();
|
||||
let client_ready = client.ready;
|
||||
|
||||
let match_id = client.current_match;
|
||||
if match_id.is_none() {
|
||||
continue;
|
||||
}
|
||||
let match_id = match_id.unwrap();
|
||||
|
||||
drop(client);
|
||||
drop(clients_guard);
|
||||
|
||||
server.terminate_match(match_id).await;
|
||||
|
||||
if !client_ready {
|
||||
let _ = send(&client_connection, "TOURNAMENT:END");
|
||||
}
|
||||
let _ = send(&client.connection, "TOURNAMENT:END");
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_player(&self, username: String) -> bool {
|
||||
self.usernames.contains(&username)
|
||||
}
|
||||
|
||||
fn is_completed(&self) -> bool {
|
||||
self.is_completed
|
||||
self.completed
|
||||
}
|
||||
|
||||
fn get_players(&self) -> Vec<String> {
|
||||
self.usernames.clone()
|
||||
}
|
||||
|
||||
fn get_type(&self) -> String {
|
||||
|
||||
Reference in New Issue
Block a user