team04_server/server/
mod.rs

1//! Contains connection handling, clients joining/leaving lobbies, and creation of new lobbies.
2
3pub mod connection;
4pub mod state;
5
6pub use connection::Connection;
7
8use std::{collections::BTreeMap, sync::Arc, time::Duration};
9
10use state::ServerState;
11
12#[cfg(test)]
13use crate::mock::TcpListener;
14#[cfg(not(test))]
15use tokio::net::TcpListener;
16
17use crate::{config::ConfigSet, log};
18
19pub async fn run(
20    listener: TcpListener,
21    addr_str: &str,
22    configs: BTreeMap<u64, ConfigSet>,
23) -> tokio::io::Result<()> {
24    log::info!("running websocket server on address {addr_str}"; &log::pfx());
25
26    let server_state = ServerState::new(configs);
27
28    let _cleanup_job = {
29        let server_state = Arc::clone(&server_state);
30        tokio::task::spawn(async move {
31            loop {
32                tokio::time::sleep(Duration::from_secs(5 * 60)).await;
33                let mut server_state = server_state.lock().await;
34                server_state.cleanup().await;
35            }
36        })
37    };
38
39    // accept connections
40    loop {
41        let (con, _) = listener.accept().await?;
42        let server_state = Arc::clone(&server_state);
43        let (abort_tx, abort_rx) = tokio::sync::oneshot::channel();
44        let conn_task = tokio::task::spawn(async move {
45            let mut in_lobby = None;
46            match connection::handle(con, &server_state, &mut in_lobby, abort_rx.await.unwrap())
47                .await
48            {
49                Ok(()) => (),
50                Err(e) => {
51                    let mut pfx = log::pfx();
52                    if let Some((lobby, _, player_id)) = &in_lobby {
53                        pfx.lobby(lobby.id());
54                        if let Some(player) = player_id {
55                            pfx.player(*player);
56                        }
57                    }
58                    log::info!("closing websocket connection, reason: {e:?}"; &pfx);
59                }
60            }
61            if let Some((lobby, name, player_id)) = in_lobby {
62                lobby.lock().await.client_leave(&name, player_id).await;
63            }
64        });
65        abort_tx.send(conn_task.abort_handle()).unwrap();
66    }
67}