team04_server/lobby/game/
lightsaber_purchase.rs1use std::{sync::Arc, time::Duration};
2
3use tokio::sync::Semaphore;
4
5use crate::{
6 lobby::state::{LobbyPhase, LockedLobbyState, SharedLobbyState},
7 log,
8 messages::{MessageTx, lightsaber_options::LightsaberOptions},
9 unit::LightsaberType,
10};
11
12impl SharedLobbyState {
13 pub async fn lightsaber_purchase(&self) -> LockedLobbyState {
26 let mut pfx = log::pfx();
27 pfx.lobby(self.id());
28
29 let mut lock = self.lock().await;
30 assert!(lock.game_started());
31
32 let timeout = Duration::from_millis(lock.configs.game_config.timeout_lightsaber_shop_phase);
33 lock.phase = LobbyPhase::LightsaberShopPhase;
34 lock.broadcast_gamestate().await;
35
36 let options = {
38 let cfg = &lock.configs.game_config;
39 LightsaberOptions {
40 red: rand::random_range(cfg.lightsaber_modifier_red.interval()),
41 green: rand::random_range(cfg.lightsaber_modifier_green.interval()),
42 blue: rand::random_range(cfg.lightsaber_modifier_blue.interval()),
43 }
44 };
45 lock.hist_lightsaber_options.push(options);
46 log::debug!(
47 "Lightsaber purchase phase {} starting with\n R={}\n G={}\n B={}",
48 lock.hist_lightsaber_options.len(),
49 options.red, options.green, options.blue; &pfx
50 );
51
52 let player_count = lock.clients.players.len();
54 let semaphore = Arc::new(Semaphore::new(player_count));
55 for player in lock.clients.players.players_alive_mut() {
56 let permit = Arc::clone(&semaphore)
57 .try_acquire_owned()
58 .expect("there are enough permits available for all players");
59 player.lightsaber_choice = Err(Some(permit));
60 player.lightsaber_choice_allowed = true;
61 }
62
63 lock.clients.broadcast_message(&options.serialize()).await;
65
66 drop(lock);
67
68 let hit_timeout =
70 tokio::time::timeout(timeout, semaphore.acquire_many(player_count as u32))
71 .await
72 .is_err();
73 log::debug!("Lightsaber purchase phase ended {}", if hit_timeout { "due to timeout" } else { "early (all players have purchased)" }; &pfx);
74
75 let mut lock = self.lock().await;
77 for player in lock.clients.players.players_alive_mut() {
78 player.lightsaber_choice_allowed = false;
79 if player.lightsaber_choice.is_err() {
80 player.lightsaber_choice = Ok(LightsaberType::random());
81 }
82 }
83 lock
84 }
85}
86
87#[cfg(test)]
88mod test {
89
90 use crate::{
91 lobby::test::{
92 FakeCon, config_set_modified, get_server_and_lobby, get_server_and_lobby_with_config,
93 player_join,
94 },
95 messages::{RxMessage, lightsaber_chosen::LightsaberChosen},
96 unit::LightsaberType,
97 };
98
99 #[tokio::test]
100 async fn test_timeout() {
101 let (server, lobby) = get_server_and_lobby_with_config(config_set_modified(
102 |game| {
103 game.timeout_lightsaber_shop_phase = 0;
104 },
105 |_| {},
106 |_| {},
107 ));
108 player_join(&server, &lobby).await;
109 player_join(&server, &lobby).await;
110 lobby.lock().await.start_game_now().await;
111 let lobby_after_lightsaber_purchase = lobby.lightsaber_purchase().await;
112 let lightsabers = lobby_after_lightsaber_purchase
113 .clients
114 .players
115 .players_alive()
116 .map(|p| *p.lightsaber_choice.as_ref().unwrap())
117 .collect::<Vec<_>>();
118 assert_eq!(2, lightsabers.len());
119 }
120
121 #[tokio::test]
122 async fn test_making_choice() {
123 let (server, lobby) = get_server_and_lobby();
124 let (p1id, _, mut p1con) = player_join(&server, &lobby).await;
125 let (p2id, _, mut p2con) = player_join(&server, &lobby).await;
126 lobby.lock().await.start_game_now().await;
127 p1con.clear();
128 p2con.clear();
129
130 let phase = tokio::spawn({
132 let lobby = lobby.clone();
133 async move {
134 let _ = lobby.lightsaber_purchase().await;
135 }
136 });
137
138 p1con.recv().await;
140 p2con.recv().await;
141
142 p1con.recv().await;
144 p2con.recv().await;
145
146 lobby
148 .lock()
149 .await
150 .message_from(
151 &p1id,
152 RxMessage::LightsaberChosen(LightsaberChosen {
153 choice: LightsaberType::Red,
154 }),
155 String::new(),
156 )
157 .await;
158 lobby
159 .lock()
160 .await
161 .message_from(
162 &p2id,
163 RxMessage::LightsaberChosen(LightsaberChosen {
164 choice: LightsaberType::Green,
165 }),
166 String::new(),
167 )
168 .await;
169
170 phase.await.unwrap();
171 let lightsabers = lobby
172 .lock()
173 .await
174 .clients
175 .players
176 .players_alive()
177 .map(|p| *p.lightsaber_choice.as_ref().unwrap())
178 .collect::<Vec<_>>();
179 assert!(
180 lightsabers == vec![LightsaberType::Red, LightsaberType::Green]
181 || lightsabers == vec![LightsaberType::Green, LightsaberType::Red]
182 );
183 }
184}