From 14c94d47c0ecc631ece6cf8230429a23aacba84d Mon Sep 17 00:00:00 2001 From: Joshua Higgins Date: Thu, 23 Apr 2026 14:26:47 -0400 Subject: [PATCH] fix: improve disconnect behaviors --- .gitignore | 6 ++- src/main.rs | 50 +++++++++++---------- src/server.rs | 117 ++++++++++++++++++++++++++------------------------ 3 files changed, 94 insertions(+), 79 deletions(-) diff --git a/.gitignore b/.gitignore index d5235d8..53f2406 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,8 @@ target /node_modules -bracket_pairings.txt \ No newline at end of file +bracket_pairings.txt + +.cursor/ +.claude/ +.codex/ \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index caaf0da..a9062e2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -305,33 +305,39 @@ async fn handle_connection( // Remove and terminate any matches // We may not be a client disconnecting, do this check - let clients_guard = sd.clients.read().await; - for client in clients_guard.values() { - let client = client.read().await; - if client.addr == addr { - let username = client.username.clone(); - let tournament_guard = sd.tournament.read().await; - if client.current_match.is_some() { - sd.disconnected_clients.write().await.push(username.clone()); - } else if tournament_guard.is_some() { - let tourney = tournament_guard.clone().unwrap(); - if tourney.read().await.contains_player(username.clone()) { - sd.disconnected_clients.write().await.push(username.clone()); - } + if let Some(username) = sd.usernames.read().await.get(&addr).cloned() { + let tournament_guard = sd.tournament.read().await; + let client = sd.clients.read().await.get(&username).cloned().unwrap(); + let client = client.write().await; + if client.current_match.is_some() { + let current_match = sd.matches.read().await.get(&client.current_match.unwrap()).cloned().unwrap(); + let current_match = current_match.read().await; + if current_match.timeout_thread.is_some() { + current_match.timeout_thread.as_ref().unwrap().abort(); } - - drop(client); - drop(clients_guard); - + + if current_match.demo_mode { + sd.matches.write().await.remove(¤t_match.id); + sd.broadcast(&format!("GAME:{}:TERMINATED", current_match.id)).await; + sd.clients.write().await.remove(&username); + } else { + sd.disconnected_clients.write().await.push(username.clone()); + } + } else if tournament_guard.is_some() { + let tourney = tournament_guard.clone().unwrap(); + if tourney.read().await.contains_player(username.clone()) { + sd.disconnected_clients.write().await.push(username.clone()); + } else { + sd.clients.write().await.remove(&username); + } + } else { sd.clients.write().await.remove(&username); - sd.usernames.write().await.remove(&addr); - - sd.broadcast(&format!("DISCONNECT:{}", username)).await; - - break; } + + sd.broadcast(&format!("DISCONNECT:{}", username)).await; } + sd.usernames.write().await.remove(&addr); sd.observers.write().await.remove(&addr); let mut admin_guard = sd.admin.write().await; diff --git a/src/server.rs b/src/server.rs index 29b9b0b..14b4970 100644 --- a/src/server.rs +++ b/src/server.rs @@ -63,11 +63,7 @@ impl Server { ))); } - let mut reconnecting = false; - let disconnected_guard = self.disconnected_clients.read().await; - if disconnected_guard.contains(&requested_username) { - reconnecting = true; - } + let reconnecting = self.disconnected_clients.read().await.contains(&requested_username); let clients_guard = self.clients.read().await; let existing_client = clients_guard.get(&requested_username).cloned(); @@ -176,15 +172,22 @@ impl Server { let _ = send(&tx, "RECONNECT:ACK"); - let matches_guard = self.matches.read().await; - let the_match = matches_guard.get(&client.current_match.unwrap()).unwrap().read().await; - - let last = the_match.ledger.last(); - if last.is_some() && last.unwrap().0 != client.color { - let _ = send( - &tx, - &format!("OPPONENT:{}", the_match.ledger.last().unwrap().1), - ); + if let Some(current_match_id) = client.current_match { + let matches_guard = self.matches.read().await; + let the_match = matches_guard.get(¤t_match_id).unwrap().read().await; + + let last = the_match.ledger.last(); + if last.is_some() && last.unwrap().0 != client.color { + let _ = send( + &tx, + &format!("OPPONENT:{}", the_match.ledger.last().unwrap().1), + ); + } else if last.is_none() && client.color == Color::Red { + let _ = send(&tx, "GAME:START:1"); + } + } else { + // Clear they're state just in case, even if it's not terminated + let _ = send(&tx, "GAME:TERMINATED"); } } else { return Err(anyhow::anyhow!(format!( @@ -281,53 +284,55 @@ impl Server { let mut client = clients_guard.get(&username).unwrap().write().await; let mut opponent = clients_guard.get(&opponent_username).unwrap().write().await; - let match_id: u32 = gen_match_id(&self.matches).await; - let new_match = Arc::new(RwLock::new(Match::new( - match_id, - username.clone(), - opponent_username, - false, - ))); - 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); + if opponent.ready { + let match_id: u32 = gen_match_id(&self.matches).await; + let new_match = Arc::new(RwLock::new(Match::new( + match_id, + username.clone(), + opponent_username, + false, + ))); + 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.current_match = Some(match_id); - client.color = if new_match.read().await.player1 == username { - let _ = send(&tx, "GAME:START:1"); - let _ = send(&opponent.connection, "GAME:START:0"); - Color::Red - } else { - let _ = send(&tx, "GAME:START:0"); - let _ = send(&opponent.connection, "GAME:START:1"); - Color::Yellow - }; + client.ready = false; + client.current_match = Some(match_id); + client.color = if new_match.read().await.player1 == username { + let _ = send(&tx, "GAME:START:1"); + let _ = send(&opponent.connection, "GAME:START:0"); + Color::Red + } else { + let _ = send(&tx, "GAME:START:0"); + let _ = send(&opponent.connection, "GAME:START:1"); + Color::Yellow + }; - opponent.ready = false; - opponent.current_match = Some(match_id); - opponent.color = !client.color; - let opponent_username = opponent.username.clone(); + opponent.ready = false; + opponent.current_match = Some(match_id); + opponent.color = !client.color; + let opponent_username = opponent.username.clone(); - self - .reservations - .write() - .await - .retain(|(p1, p2)| !(p1 == &client.username && p2 == &opponent.username)); + self + .reservations + .write() + .await + .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; + 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(()); + } } let clients_guard = self.clients.read().await;