fix: rework how moves are delayed

This commit is contained in:
2026-01-23 12:07:16 -05:00
Unverified
parent 07d9e099fa
commit 30d7f3bc92
3 changed files with 89 additions and 91 deletions

View File

@@ -8,7 +8,6 @@ use tokio_tungstenite::{accept_async, tungstenite::Message};
use tracing::{error, info}; use tracing::{error, info};
use local_ip_address::local_ip; use local_ip_address::local_ip;
// TODO: Allow random "player1" in demo mode
// TODO: Support reconnecting behaviors // TODO: Support reconnecting behaviors
// TODO: Other tournament types // TODO: Other tournament types
// TODO: Max move wait time // TODO: Max move wait time
@@ -22,6 +21,9 @@ async fn main() -> Result<(), anyhow::Error> {
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
let demo_mode = args.get(1).is_some() && args.get(1).unwrap() == "demo"; let demo_mode = args.get(1).is_some() && args.get(1).unwrap() == "demo";
if demo_mode {
info!("Starting server in DEMO MODE");
}
let admin_password = env::var("ADMIN_AUTH").unwrap_or_else(|_| String::from("admin")); let admin_password = env::var("ADMIN_AUTH").unwrap_or_else(|_| String::from("admin"));
info!("Admin password: {}", admin_password); info!("Admin password: {}", admin_password);
let admin_password = Arc::new(admin_password); let admin_password = Arc::new(admin_password);

View File

@@ -1,3 +1,5 @@
use std::time::Instant;
use crate::{tournaments::*, types::*, *}; use crate::{tournaments::*, types::*, *};
pub struct Server { pub struct Server {
@@ -179,13 +181,17 @@ impl Server {
current_match.player1 current_match.player1
}; };
let opponent_connection = if addr == current_match.player1 { let opponent_connection = if current_match.demo_mode {
clients_guard.get(&current_match.player2).unwrap().read().await.connection.clone() None
} else if addr == current_match.player1 {
Some(clients_guard.get(&current_match.player2).unwrap().read().await.connection.clone())
} else { } else {
clients_guard.get(&current_match.player1).unwrap().read().await.connection.clone() Some(clients_guard.get(&current_match.player1).unwrap().read().await.connection.clone())
}; };
let opponent_username = if addr == current_match.player1 { let opponent_username = if current_match.demo_mode {
SERVER_PLAYER_USERNAME.to_string()
} else if addr == current_match.player1 {
clients_guard.get(&current_match.player2).unwrap().read().await.username.clone() clients_guard.get(&current_match.player2).unwrap().read().await.username.clone()
} else { } else {
clients_guard.get(&current_match.player1).unwrap().read().await.username.clone() clients_guard.get(&current_match.player1).unwrap().read().await.username.clone()
@@ -235,7 +241,7 @@ impl Server {
tx.send(Message::Close(None))?; tx.send(Message::Close(None))?;
} else { } else {
let _ = send(&tx, "GAME:LOSS"); let _ = send(&tx, "GAME:LOSS");
let _ = send(&opponent_connection, "GAME:WINS"); let _ = send(&opponent_connection.unwrap(), "GAME:WINS");
self.broadcast_message(&viewers, &format!("GAME:WIN:{}", opponent_username)).await; self.broadcast_message(&viewers, &format!("GAME:WIN:{}", opponent_username)).await;
let mut clients_guard = self.clients.write().await; let mut clients_guard = self.clients.write().await;
@@ -254,49 +260,30 @@ impl Server {
} }
return Ok(()); return Ok(());
} else { } else {
// Place it
current_match.place_token(client.color.clone(), column); current_match.place_token(client.color.clone(), column);
current_match.move_to_dispatch = (client.color.clone(), column);
} }
// broadcast the move to viewers let mut viewer_messages = Vec::new();
self.broadcast_message( let viewers = current_match.viewers.clone();
&current_match.viewers,
&format!("GAME:MOVE:{}:{}", client.username, column), viewer_messages.push(format!("GAME:MOVE:{}:{}", client.username, column));
)
.await;
// Check game end conditions // Check game end conditions
let (winner, filled) = current_match.end_game_check(); let (winner, filled) = current_match.end_game_check();
if winner != Color::None { // Send match results
if winner == client.color { if winner == client.color {
let _ = send(&tx, "GAME:WINS"); let _ = send(&tx, "GAME:WINS");
if !current_match.demo_mode { if !current_match.demo_mode {
let _ = send(&opponent_connection, "GAME:LOSS"); let _ = send(&opponent_connection.as_ref().unwrap(), "GAME:LOSS");
}
self.broadcast_message(
&current_match.viewers,
&format!("GAME:WIN:{}", client.username),
)
.await;
} else {
let _ = send(&tx, "GAME:LOSS");
if !current_match.demo_mode {
let _ = send(&opponent_connection, "GAME:WINS");
}
self.broadcast_message(
&current_match.viewers,
&format!("GAME:WIN:{}", opponent_username),
)
.await;
} }
viewer_messages.push(format!("GAME:WIN:{}", client.username));
} else if filled { } else if filled {
let _ = send(&tx, "GAME:DRAW"); let _ = send(&tx, "GAME:DRAW");
if !current_match.demo_mode { if !current_match.demo_mode {
let _ = send(&opponent_connection, "GAME:DRAW"); let _ = send(&opponent_connection.as_ref().unwrap(), "GAME:DRAW");
} }
self.broadcast_message(&current_match.viewers, "GAME:DRAW").await; viewer_messages.push("GAME:DRAW".to_string());
} }
// remove match from matchmaker // remove match from matchmaker
@@ -342,58 +329,64 @@ impl Server {
} else if self.tournament.read().await.is_none() { } else if self.tournament.read().await.is_none() {
let _ = send(&tx, "TOURNAMENT:END"); let _ = send(&tx, "TOURNAMENT:END");
if !is_demo_mode { if !is_demo_mode {
let _ = send(&opponent_connection, "TOURNAMENT:END"); let _ = send(&opponent_connection.unwrap(), "TOURNAMENT:END");
} }
} }
return Ok(()); return Ok(());
} }
let connection_to_send = if !current_match.demo_mode { let default_waiting_time = *self.waiting_timeout.read().await;
opponent_connection.clone() let mut adjusted_waiting =
default_waiting_time as i64 + (rand::rng().random_range(0..=50) - 25);
let current_move_time = Instant::now();
if current_match.ledger.is_empty() {
adjusted_waiting = 0;
} else { } else {
tx.clone() let last_move_time = current_match.ledger.last().unwrap().2;
}; let elapsed = current_move_time.duration_since(last_move_time).as_millis() as i64;
let column_to_use = if !current_match.demo_mode { adjusted_waiting -= elapsed;
column if adjusted_waiting < 0 {
} else { adjusted_waiting = 0;
random_move(&current_match.board) }
};
if current_match.demo_mode {
let move_to_dispatch = current_match.move_to_dispatch.clone();
current_match.ledger.push(move_to_dispatch);
current_match.move_to_dispatch = (!client.color, column_to_use);
current_match.place_token(!client.color, column_to_use);
} }
let waiting = current_match.ledger.push((client.color.clone(), column, current_move_time));
*self.waiting_timeout.read().await as i64 + (rand::rng().random_range(0..=50) - 25);
let matches_move = self.matches.clone(); let demo_mode = current_match.demo_mode;
let observers_move = self.observers.clone(); let demo_move = random_move(&current_match.board);
let match_id_move = current_match.id; let no_winner = winner == Color::None && !filled;
let demo_mode_move = current_match.demo_mode; let observers = self.observers.clone();
if current_match.demo_mode {
current_match.ledger.push((!client.color, demo_move, Instant::now()));
current_match.place_token(!client.color, demo_move);
}
let opp_connection_move = opponent_connection.clone();
current_match.wait_thread = Some(tokio::spawn(async move { current_match.wait_thread = Some(tokio::spawn(async move {
tokio::time::sleep(tokio::time::Duration::from_millis(waiting as u64)).await; tokio::time::sleep(tokio::time::Duration::from_millis(adjusted_waiting as u64)).await;
let mut matches_guard = matches_move.write().await; if !demo_mode && no_winner {
let mut current_match = matches_guard.get_mut(&match_id_move).unwrap().write().await; let _ = send(&opp_connection_move.as_ref().unwrap(), &format!("OPPONENT:{}", column));
let move_to_dispatch = current_match.move_to_dispatch.clone(); }
current_match.ledger.push(move_to_dispatch);
current_match.move_to_dispatch = (Color::None, 0);
if demo_mode_move { for msg in viewer_messages {
broadcast_message(&observers, &viewers, &msg).await;
}
if demo_mode && no_winner {
tokio::time::sleep(tokio::time::Duration::from_millis(default_waiting_time)).await;
let _ = send(&tx, &format!("OPPONENT:{}", demo_move));
broadcast_message( broadcast_message(
&observers_move, &observers,
&current_match.viewers, &viewers,
&format!("GAME:MOVE:{}:{}", "demo", column_to_use), &format!("GAME:MOVE:{}:{}", SERVER_PLAYER_USERNAME, demo_move),
) )
.await; .await;
} }
drop(current_match);
drop(matches_guard);
let _ = send(&connection_to_send, &format!("OPPONENT:{}", column_to_use));
})); }));
Ok(()) Ok(())
@@ -436,17 +429,21 @@ impl Server {
let mut to_send = "GAME:LIST:".to_string(); let mut to_send = "GAME:LIST:".to_string();
for match_guard in matches_guard.values() { for match_guard in matches_guard.values() {
let a_match = match_guard.read().await; let a_match = match_guard.read().await;
let player1 = clients_guard.get(&a_match.player1).unwrap().read().await; let player1 = if a_match.player1.to_string() == SERVER_PLAYER_ADDR {
let player2 = clients_guard.get(&a_match.player2).unwrap().read().await; SERVER_PLAYER_USERNAME.to_string()
} else {
clients_guard.get(&a_match.player1).unwrap().read().await.username.clone()
};
let player2 = if a_match.player2.to_string() == SERVER_PLAYER_ADDR {
SERVER_PLAYER_USERNAME.to_string()
} else {
clients_guard.get(&a_match.player2).unwrap().read().await.username.clone()
};
to_send += a_match.id.to_string().as_str(); to_send += a_match.id.to_string().as_str();
to_send += ","; to_send += ",";
to_send += player1.username.as_str(); to_send += player1.as_str();
to_send += ","; to_send += ",";
to_send += if player1.username == player2.username { to_send += player2.as_str();
"demo"
} else {
player2.username.as_str()
};
to_send += "|"; to_send += "|";
} }

View File

@@ -1,5 +1,6 @@
use rand::Rng; use rand::Rng;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::time::Instant;
use std::{ops, vec}; use std::{ops, vec};
use tokio::sync::mpsc::UnboundedSender; use tokio::sync::mpsc::UnboundedSender;
use tokio_tungstenite::tungstenite::Message; use tokio_tungstenite::tungstenite::Message;
@@ -55,8 +56,7 @@ pub struct Match {
pub demo_mode: bool, pub demo_mode: bool,
pub board: Vec<Vec<Color>>, pub board: Vec<Vec<Color>>,
pub viewers: Vec<SocketAddr>, pub viewers: Vec<SocketAddr>,
pub ledger: Vec<(Color, usize)>, pub ledger: Vec<(Color, usize, Instant)>,
pub move_to_dispatch: (Color, usize),
pub wait_thread: Option<tokio::task::JoinHandle<()>>, pub wait_thread: Option<tokio::task::JoinHandle<()>>,
pub player1: SocketAddr, pub player1: SocketAddr,
pub player2: SocketAddr, pub player2: SocketAddr,
@@ -76,7 +76,6 @@ impl Match {
board: vec![vec![Color::None; 6]; 7], board: vec![vec![Color::None; 6]; 7],
viewers: Vec::new(), viewers: Vec::new(),
ledger: Vec::new(), ledger: Vec::new(),
move_to_dispatch: (Color::None, 0),
wait_thread: None, wait_thread: None,
player1: if player1 == first { player1 } else { player2 }, player1: if player1 == first { player1 } else { player2 },
player2: if player1 == first { player2 } else { player1 }, player2: if player1 == first { player2 } else { player1 },