feat: remove the need for constant polling data

This commit is contained in:
2026-03-27 13:08:20 -04:00
Unverified
parent 73e435d5ec
commit 4e131fac31
4 changed files with 124 additions and 15 deletions

View File

@@ -326,6 +326,8 @@ async fn handle_connection(
sd.clients.write().await.remove(&username); sd.clients.write().await.remove(&username);
sd.usernames.write().await.remove(&addr); sd.usernames.write().await.remove(&addr);
sd.broadcast(&format!("DISCONNECT:{}", username)).await;
break; break;
} }
} }

View File

@@ -44,14 +44,21 @@ impl Server {
) -> Result<(), anyhow::Error> { ) -> Result<(), anyhow::Error> {
if requested_username.is_empty() { if requested_username.is_empty() {
return Err(anyhow::anyhow!(format!( return Err(anyhow::anyhow!(format!(
"ERROR:INVALID:ID:{}", "ERROR:INVALID:CONNECT:{}",
requested_username
)));
}
if requested_username.chars().any(|c| matches!(c, ':' | ',' | '|') || c.is_control()) {
return Err(anyhow::anyhow!(format!(
"ERROR:INVALID:CONNECT:{}",
requested_username requested_username
))); )));
} }
if requested_username == SERVER_PLAYER_USERNAME { if requested_username == SERVER_PLAYER_USERNAME {
return Err(anyhow::anyhow!(format!( return Err(anyhow::anyhow!(format!(
"ERROR:INVALID:ID:{}", "ERROR:INVALID:CONNECT:{}",
requested_username requested_username
))); )));
} }
@@ -66,7 +73,7 @@ impl Server {
let existing_client = clients_guard.get(&requested_username).cloned(); let existing_client = clients_guard.get(&requested_username).cloned();
if existing_client.is_some() && !reconnecting { if existing_client.is_some() && !reconnecting {
return Err(anyhow::anyhow!(format!( return Err(anyhow::anyhow!(format!(
"ERROR:INVALID:ID:{}", "ERROR:INVALID:CONNECT:{}",
requested_username requested_username
))); )));
} }
@@ -81,12 +88,14 @@ impl Server {
self.clients.write().await.insert( self.clients.write().await.insert(
requested_username.clone(), requested_username.clone(),
Arc::new(RwLock::new(Client::new( Arc::new(RwLock::new(Client::new(
requested_username, requested_username.clone(),
tx.clone(), tx.clone(),
addr.to_string().parse()?, addr.to_string().parse()?,
))), ))),
); );
self.broadcast(&format!("CONNECT:{}", requested_username)).await;
return Ok(()); return Ok(());
} }
@@ -217,6 +226,7 @@ impl Server {
self.clients.write().await.remove(&username); self.clients.write().await.remove(&username);
self.usernames.write().await.remove(&addr); self.usernames.write().await.remove(&addr);
let _ = send(&tx, "DISCONNECT:ACK"); let _ = send(&tx, "DISCONNECT:ACK");
self.broadcast(&format!("DISCONNECT:{}", username)).await;
Ok(()) Ok(())
} }
@@ -264,6 +274,7 @@ impl Server {
let _ = send(&tx, "READY:ACK"); let _ = send(&tx, "READY:ACK");
drop(client); drop(client);
drop(clients_guard); drop(clients_guard);
self.broadcast(&format!("READY:{}:{}", username, true)).await;
if let Some(opponent_username) = self.find_reservation_opponent(&username).await { if let Some(opponent_username) = self.find_reservation_opponent(&username).await {
let clients_guard = self.clients.read().await; let clients_guard = self.clients.read().await;
@@ -278,6 +289,14 @@ impl Server {
false, false,
))); )));
self.matches.write().await.insert(match_id, new_match.clone()); self.matches.write().await.insert(match_id, new_match.clone());
let match_guard = new_match.read().await;
self
.broadcast(&format!(
"GAME:START:{},{},{}",
match_id, match_guard.player1, match_guard.player2
))
.await;
drop(match_guard);
client.ready = false; client.ready = false;
client.current_match = Some(match_id); client.current_match = Some(match_id);
@@ -294,6 +313,7 @@ impl Server {
opponent.ready = false; opponent.ready = false;
opponent.current_match = Some(match_id); opponent.current_match = Some(match_id);
opponent.color = !client.color; opponent.color = !client.color;
let opponent_username = opponent.username.clone();
self self
.reservations .reservations
@@ -301,6 +321,12 @@ impl Server {
.await .await
.retain(|(p1, p2)| !(p1 == &client.username && p2 == &opponent.username)); .retain(|(p1, p2)| !(p1 == &client.username && p2 == &opponent.username));
drop(opponent);
drop(client);
drop(clients_guard);
self.broadcast(&format!("READY:{}:{}", username, false)).await;
self.broadcast(&format!("READY:{}:{}", opponent_username, false)).await;
return Ok(()); return Ok(());
} }
@@ -316,6 +342,15 @@ impl Server {
is_demo_mode, is_demo_mode,
))); )));
self.matches.write().await.insert(match_id, new_match.clone()); self.matches.write().await.insert(match_id, new_match.clone());
let match_guard = new_match.read().await;
self
.broadcast(&format!(
"GAME:START:{},{},{}",
match_id, match_guard.player1, match_guard.player2
))
.await;
drop(match_guard);
client.ready = false; client.ready = false;
client.current_match = Some(match_id); client.current_match = Some(match_id);
client.color = if new_match.read().await.player1 == username { client.color = if new_match.read().await.player1 == username {
@@ -355,6 +390,9 @@ impl Server {
} }
})); }));
} }
drop(client);
self.broadcast(&format!("READY:{}:{}", username, false)).await;
} }
Ok(()) Ok(())
@@ -1202,8 +1240,17 @@ impl Server {
false, false,
))); )));
self.matches.write().await.insert(match_id, new_match.clone()); self.matches.write().await.insert(match_id, new_match.clone());
let match_guard = new_match.read().await;
self
.broadcast(&format!(
"GAME:START:{},{},{}",
match_id, match_guard.player1, match_guard.player2
))
.await;
drop(match_guard);
player1.ready = false; player1.ready = false;
let player1_name = player1.username.clone();
player1.current_match = Some(match_id); player1.current_match = Some(match_id);
player1.color = if new_match.read().await.player1 == player1_username { player1.color = if new_match.read().await.player1 == player1_username {
let _ = send(&tx, "GAME:START:1"); let _ = send(&tx, "GAME:START:1");
@@ -1216,6 +1263,7 @@ impl Server {
}; };
player2.ready = false; player2.ready = false;
let player2_name = player2.username.clone();
player2.current_match = Some(match_id); player2.current_match = Some(match_id);
player2.color = !player1.color; player2.color = !player1.color;
@@ -1224,6 +1272,12 @@ impl Server {
.write() .write()
.await .await
.retain(|(p1, p2)| !(p1 == &player1_username && p2 == &player2_username)); .retain(|(p1, p2)| !(p1 == &player1_username && p2 == &player2_username));
drop(player2);
drop(player1);
drop(clients_guard);
self.broadcast(&format!("READY:{}:{}", player1_name, false)).await;
self.broadcast(&format!("READY:{}:{}", player2_name, false)).await;
} }
} }

View File

@@ -19,6 +19,7 @@ pub struct KnockoutBracket {
pub started: bool, pub started: bool,
pub clients: Clients, pub clients: Clients,
pub matches: Matches, pub matches: Matches,
pub observers: Observers,
pub usernames: Vec<String>, pub usernames: Vec<String>,
} }
@@ -39,12 +40,16 @@ impl KnockoutBracket {
player2_username.clone(), player2_username.clone(),
false, false,
))); )));
let match_guard = new_match.read().await; let match_guard = new_match.read().await;
let mut player1 = clients_guard.get(&player1_username).unwrap().write().await;
let mut player1 = clients_guard.get(&player1_username).unwrap().write().await;
player1.current_match = Some(match_id); player1.current_match = Some(match_id);
player1.ready = false; player1.ready = false;
broadcast_message(
&self.observers,
&format!("READY:{}:{}", player1.username.clone(), false),
)
.await;
if match_guard.player1 == player1_username { if match_guard.player1 == player1_username {
player1.color = Color::Red; player1.color = Color::Red;
@@ -53,13 +58,16 @@ impl KnockoutBracket {
player1.color = Color::Yellow; player1.color = Color::Yellow;
let _ = send(&player1.connection, "GAME:START:0"); let _ = send(&player1.connection, "GAME:START:0");
} }
drop(player1); drop(player1);
let mut player2 = clients_guard.get(&player2_username).unwrap().write().await; let mut player2 = clients_guard.get(&player2_username).unwrap().write().await;
player2.current_match = Some(match_id); player2.current_match = Some(match_id);
player2.ready = false; player2.ready = false;
broadcast_message(
&self.observers,
&format!("READY:{}:{}", player2.username.clone(), false),
)
.await;
if match_guard.player1 == player2_username { if match_guard.player1 == player2_username {
player2.color = Color::Red; player2.color = Color::Red;
@@ -68,10 +76,17 @@ impl KnockoutBracket {
player2.color = Color::Yellow; player2.color = Color::Yellow;
let _ = send(&player2.connection, "GAME:START:0"); let _ = send(&player2.connection, "GAME:START:0");
} }
drop(player2); drop(player2);
self.matches.write().await.insert(match_id, new_match.clone()); self.matches.write().await.insert(match_id, new_match.clone());
broadcast_message(
&self.observers,
&format!(
"GAME:START:{},{},{}",
match_id, match_guard.player1, match_guard.player2
),
)
.await;
i += 2 i += 2
} }
@@ -95,6 +110,7 @@ impl Tournament for KnockoutBracket {
started: false, started: false,
clients: server.clients.clone(), clients: server.clients.clone(),
matches: server.matches.clone(), matches: server.matches.clone(),
observers: server.observers.clone(),
usernames: ready_players.to_vec(), usernames: ready_players.to_vec(),
} }
} }
@@ -219,6 +235,7 @@ impl Tournament for KnockoutBracket {
player1.current_match = Some(match_id); player1.current_match = Some(match_id);
player1.ready = false; player1.ready = false;
let player1_name = player1.username.clone();
if match_guard.player1 == player1.username { if match_guard.player1 == player1.username {
player1.color = Color::Red; player1.color = Color::Red;
@@ -234,6 +251,7 @@ impl Tournament for KnockoutBracket {
player2.current_match = Some(match_id); player2.current_match = Some(match_id);
player2.ready = false; player2.ready = false;
let player2_name = player2.username.clone();
if match_guard.player1 == player2.username { if match_guard.player1 == player2.username {
player2.color = Color::Red; player2.color = Color::Red;
@@ -245,8 +263,27 @@ impl Tournament for KnockoutBracket {
drop(player2); drop(player2);
broadcast_message(
&self.observers,
&format!("READY:{}:{}", player1_name, false),
)
.await;
broadcast_message(
&self.observers,
&format!("READY:{}:{}", player2_name, false),
)
.await;
self.current_matches.push(match_id); self.current_matches.push(match_id);
self.matches.write().await.insert(match_id, new_match.clone()); self.matches.write().await.insert(match_id, new_match.clone());
broadcast_message(
&self.observers,
&format!(
"GAME:START:{},{},{}",
match_id, match_guard.player1, match_guard.player2
),
)
.await;
} }
let mut loser = String::new(); let mut loser = String::new();

View File

@@ -16,6 +16,7 @@ pub struct RoundRobin {
pub completed: bool, pub completed: bool,
pub current_matches: Vec<ID>, pub current_matches: Vec<ID>,
pub usernames: Vec<String>, pub usernames: Vec<String>,
pub observers: Observers,
} }
impl RoundRobin { impl RoundRobin {
@@ -39,12 +40,16 @@ impl RoundRobin {
))); )));
self.current_matches.push(match_id.clone()); self.current_matches.push(match_id.clone());
let match_guard = new_match.read().await; let match_guard = new_match.read().await;
let mut player1 = clients_guard.get(&player1_username.0).unwrap().write().await;
let mut player1 = clients_guard.get(&player1_username.0).unwrap().write().await;
player1.current_match = Some(match_id); player1.current_match = Some(match_id);
player1.ready = false; player1.ready = false;
broadcast_message(
&self.observers,
&format!("READY:{}:{}", player1.username.clone(), false),
)
.await;
if match_guard.player1 == player1_username.0 { if match_guard.player1 == player1_username.0 {
player1.color = Color::Red; player1.color = Color::Red;
@@ -53,13 +58,16 @@ impl RoundRobin {
player1.color = Color::Yellow; player1.color = Color::Yellow;
let _ = send(&player1.connection, "GAME:START:0"); let _ = send(&player1.connection, "GAME:START:0");
} }
drop(player1); drop(player1);
let mut player2 = clients_guard.get(&player2_addr.0).unwrap().write().await; let mut player2 = clients_guard.get(&player2_addr.0).unwrap().write().await;
player2.current_match = Some(match_id); player2.current_match = Some(match_id);
player2.ready = false; player2.ready = false;
broadcast_message(
&self.observers,
&format!("READY:{}:{}", player2.username.clone(), false),
)
.await;
if match_guard.player1 == player2_addr.0 { if match_guard.player1 == player2_addr.0 {
player2.color = Color::Red; player2.color = Color::Red;
@@ -68,17 +76,24 @@ impl RoundRobin {
player2.color = Color::Yellow; player2.color = Color::Yellow;
let _ = send(&player2.connection, "GAME:START:0"); let _ = send(&player2.connection, "GAME:START:0");
} }
drop(player2); drop(player2);
matches.write().await.insert(match_id, new_match.clone()); matches.write().await.insert(match_id, new_match.clone());
broadcast_message(
&self.observers,
&format!(
"GAME:START:{},{},{}",
match_id, match_guard.player1, match_guard.player2
),
)
.await;
} }
} }
} }
#[async_trait] #[async_trait]
impl Tournament for RoundRobin { impl Tournament for RoundRobin {
async fn new(ready_players: &[String], _: &Server) -> RoundRobin { async fn new(ready_players: &[String], server: &Server) -> RoundRobin {
let mut result = RoundRobin { let mut result = RoundRobin {
players: HashMap::new(), players: HashMap::new(),
top_half: Vec::new(), top_half: Vec::new(),
@@ -86,6 +101,7 @@ impl Tournament for RoundRobin {
completed: false, completed: false,
current_matches: Vec::new(), current_matches: Vec::new(),
usernames: ready_players.to_vec(), usernames: ready_players.to_vec(),
observers: server.observers.clone(),
}; };
let size = ready_players.len(); let size = ready_players.len();