fix: improve disconnect behaviors

This commit is contained in:
2026-04-23 14:26:47 -04:00
Unverified
parent 21f921dbe0
commit 14c94d47c0
3 changed files with 94 additions and 79 deletions

6
.gitignore vendored
View File

@@ -30,4 +30,8 @@ target
/node_modules /node_modules
bracket_pairings.txt bracket_pairings.txt
.cursor/
.claude/
.codex/

View File

@@ -305,33 +305,39 @@ async fn handle_connection(
// Remove and terminate any matches // Remove and terminate any matches
// We may not be a client disconnecting, do this check // We may not be a client disconnecting, do this check
let clients_guard = sd.clients.read().await; if let Some(username) = sd.usernames.read().await.get(&addr).cloned() {
for client in clients_guard.values() { let tournament_guard = sd.tournament.read().await;
let client = client.read().await; let client = sd.clients.read().await.get(&username).cloned().unwrap();
if client.addr == addr { let client = client.write().await;
let username = client.username.clone(); if client.current_match.is_some() {
let tournament_guard = sd.tournament.read().await; let current_match = sd.matches.read().await.get(&client.current_match.unwrap()).cloned().unwrap();
if client.current_match.is_some() { let current_match = current_match.read().await;
sd.disconnected_clients.write().await.push(username.clone()); if current_match.timeout_thread.is_some() {
} else if tournament_guard.is_some() { current_match.timeout_thread.as_ref().unwrap().abort();
let tourney = tournament_guard.clone().unwrap();
if tourney.read().await.contains_player(username.clone()) {
sd.disconnected_clients.write().await.push(username.clone());
}
} }
drop(client); if current_match.demo_mode {
drop(clients_guard); sd.matches.write().await.remove(&current_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.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); sd.observers.write().await.remove(&addr);
let mut admin_guard = sd.admin.write().await; let mut admin_guard = sd.admin.write().await;

View File

@@ -63,11 +63,7 @@ impl Server {
))); )));
} }
let mut reconnecting = false; let reconnecting = self.disconnected_clients.read().await.contains(&requested_username);
let disconnected_guard = self.disconnected_clients.read().await;
if disconnected_guard.contains(&requested_username) {
reconnecting = true;
}
let clients_guard = self.clients.read().await; let clients_guard = self.clients.read().await;
let existing_client = clients_guard.get(&requested_username).cloned(); let existing_client = clients_guard.get(&requested_username).cloned();
@@ -176,15 +172,22 @@ impl Server {
let _ = send(&tx, "RECONNECT:ACK"); let _ = send(&tx, "RECONNECT:ACK");
let matches_guard = self.matches.read().await; if let Some(current_match_id) = client.current_match {
let the_match = matches_guard.get(&client.current_match.unwrap()).unwrap().read().await; let matches_guard = self.matches.read().await;
let the_match = matches_guard.get(&current_match_id).unwrap().read().await;
let last = the_match.ledger.last();
if last.is_some() && last.unwrap().0 != client.color { let last = the_match.ledger.last();
let _ = send( if last.is_some() && last.unwrap().0 != client.color {
&tx, let _ = send(
&format!("OPPONENT:{}", the_match.ledger.last().unwrap().1), &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 { } else {
return Err(anyhow::anyhow!(format!( return Err(anyhow::anyhow!(format!(
@@ -281,53 +284,55 @@ impl Server {
let mut client = clients_guard.get(&username).unwrap().write().await; let mut client = clients_guard.get(&username).unwrap().write().await;
let mut opponent = clients_guard.get(&opponent_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; if opponent.ready {
let new_match = Arc::new(RwLock::new(Match::new( let match_id: u32 = gen_match_id(&self.matches).await;
match_id, let new_match = Arc::new(RwLock::new(Match::new(
username.clone(), match_id,
opponent_username, username.clone(),
false, opponent_username,
))); false,
self.matches.write().await.insert(match_id, new_match.clone()); )));
let match_guard = new_match.read().await; self.matches.write().await.insert(match_id, new_match.clone());
self let match_guard = new_match.read().await;
.broadcast(&format!( self
"GAME:START:{},{},{}", .broadcast(&format!(
match_id, match_guard.player1, match_guard.player2 "GAME:START:{},{},{}",
)) match_id, match_guard.player1, match_guard.player2
.await; ))
drop(match_guard); .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 {
let _ = send(&tx, "GAME:START:1"); let _ = send(&tx, "GAME:START:1");
let _ = send(&opponent.connection, "GAME:START:0"); let _ = send(&opponent.connection, "GAME:START:0");
Color::Red Color::Red
} else { } else {
let _ = send(&tx, "GAME:START:0"); let _ = send(&tx, "GAME:START:0");
let _ = send(&opponent.connection, "GAME:START:1"); let _ = send(&opponent.connection, "GAME:START:1");
Color::Yellow Color::Yellow
}; };
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(); let opponent_username = opponent.username.clone();
self self
.reservations .reservations
.write() .write()
.await .await
.retain(|(p1, p2)| !(p1 == &client.username && p2 == &opponent.username)); .retain(|(p1, p2)| !(p1 == &client.username && p2 == &opponent.username));
drop(opponent); drop(opponent);
drop(client); drop(client);
drop(clients_guard); drop(clients_guard);
self.broadcast(&format!("READY:{}:{}", username, false)).await; self.broadcast(&format!("READY:{}:{}", username, false)).await;
self.broadcast(&format!("READY:{}:{}", opponent_username, false)).await; self.broadcast(&format!("READY:{}:{}", opponent_username, false)).await;
return Ok(()); return Ok(());
}
} }
let clients_guard = self.clients.read().await; let clients_guard = self.clients.read().await;