team04_server/lobby/game/
start.rs

1use std::time::Duration;
2
3use rand::seq::IndexedRandom;
4
5use crate::{
6    lobby::state::{LobbyState, SharedLobbyState, matchups::Matchups},
7    log,
8    messages::{MessageTx, game_start::GameStart, player_character::PlayerCharacter},
9};
10
11impl SharedLobbyState {
12    /// Waits for the game to be started if it has
13    /// not started yet. If the game has been started
14    /// already, returns immediately.
15    pub async fn wait_for_game_start(&self) {
16        let lock = self.lock().await;
17        let mut wait = lock.on_game_start.recv();
18        drop(lock);
19        wait.wait_for(|v| *v).await.ok();
20    }
21
22    /// Waits for up to `timeout` (taken from the lobby's config) for the game to be started.
23    /// If it has not started by then, it is started by force.
24    ///
25    /// If a timer is already running and `extend_timer` is `false`, the game will start
26    /// when the previous timer ends.
27    /// If a timer is already running and `extend_timer` is `true`, the previous timer will
28    /// be ignored once it finishes, and a new timer is started.
29    ///
30    /// This means that this function can take longer than `timeout`, if it is called a second
31    /// time with `extend_timer` being `true`.
32    pub async fn start_game_after_timeout(&self, extend_timer: bool) {
33        let mut lock = self.lock().await;
34        let mut pfx = log::pfx();
35        pfx.lobby(lock.id());
36
37        // if another timer is running and we aren't allowed to extend, wait for game start and return
38        if lock.game_start_timer_id != 0 && !extend_timer {
39            log::debug!("timer id {} > 0 and extend_timer = false, not starting a new timer", lock.game_start_timer_id; &pfx);
40            drop(lock);
41            return self.wait_for_game_start().await;
42        }
43
44        let timer_id = (lock.game_start_timer_id + 1).max(1);
45        log::debug!("starting new timer with id {timer_id}"; &pfx);
46        lock.game_start_timer_id = timer_id;
47        let mut wait = lock.on_game_start.recv();
48        let timeout = Duration::from_millis(lock.configs.game_config.timeout_lobby);
49        drop(lock);
50
51        let waiter = wait.wait_for(|v| *v);
52        if tokio::time::timeout(timeout, waiter).await.is_err() {
53            log::debug!("timer ended before the game started, ..."; &pfx);
54            // timeout
55            let mut lock = self.lock().await;
56            if timer_id == lock.game_start_timer_id {
57                log::debug!("... starting game now"; &pfx);
58                lock.consider_starting_game().await;
59            } else {
60                drop(lock);
61                log::debug!("... but another timer has taken over"; &pfx);
62                // another timer has taken over and will start the game,
63                // wait for that to happen
64                wait.wait_for(|v| *v).await.ok();
65            }
66        }
67    }
68}
69
70impl LobbyState {
71    pub async fn consider_starting_game(&mut self) {
72        if !self.game_started()
73            && self.clients.players.len() >= self.min_players()
74            && self
75                .clients
76                .players
77                .players_all()
78                .all(|player| player.ready)
79        {
80            self.start_game_now().await;
81        }
82    }
83    pub fn could_start_timer_for_game_start(&self) -> bool {
84        !self.game_started() && self.clients.players.len() >= self.min_players()
85    }
86    pub async fn start_game_now(&mut self) {
87        if !self.game_started() {
88            log::info!("starting game now"; log::pfx().lobby(self.id()));
89            self.set_game_started();
90            let available_characters = self.available_characters();
91            for player in self.clients.players.players_all_mut() {
92                player.ready = true;
93                if player.character.is_none() {
94                    player.character = Some(
95                        available_characters
96                            .choose(&mut rand::rng())
97                            .copied()
98                            .unwrap_or_else(PlayerCharacter::random),
99                    );
100                }
101                player.lives = self.configs.game_config.player_lives as i64;
102                player.red_lightsabers.clear();
103                player.green_lightsabers.clear();
104                player.blue_lightsabers.clear();
105            }
106            self.prev_matchups = Matchups::new(self.clients.players.len());
107            self.clients
108                .broadcast_message(&GameStart::new().serialize())
109                .await;
110        }
111    }
112}