team04_server/server/state/
mod.rs1use std::collections::BTreeMap;
2use std::sync::Arc;
3use std::{collections::HashMap, time::Instant};
4
5use tokio::sync::{Mutex, MutexGuard};
6use uuid::Uuid;
7
8use crate::config::ConfigSet;
9use crate::lobby::state::{
10 LobbyState, SharedLobbyState,
11 clients::{Clients, PlayerId, ReconnectToken},
12};
13use crate::log::uuid_human_hash::uuid_human_hash;
14use crate::{LOCK_WAIT_TIME_MS, log};
15
16#[derive(Debug)]
17pub struct ServerState {
18 lobbies: HashMap<LobbyId, SharedLobbyState>,
19 player_ids: HashMap<PlayerId, Result<LobbyId, Instant>>,
24 reconnect_tokens: HashMap<ReconnectToken, Result<(LobbyId, PlayerId), Instant>>,
30}
31
32#[derive(Debug)]
33pub struct SyncedServerState(Mutex<ServerState>);
34
35impl SyncedServerState {
36 pub fn try_lock(&self) -> Result<MutexGuard<ServerState>, tokio::sync::TryLockError> {
37 self.0.try_lock()
38 }
39 pub async fn lock(&self) -> MutexGuard<ServerState> {
45 let now = tokio::time::Instant::now();
46 let task = tokio::spawn(async move {
47 tokio::time::sleep(tokio::time::Duration::from_millis(LOCK_WAIT_TIME_MS.0)).await;
48 log::info!("[lock] getting server lock took more than {} ms", LOCK_WAIT_TIME_MS.0; &log::pfx());
49 tokio::time::sleep(tokio::time::Duration::from_millis(LOCK_WAIT_TIME_MS.1)).await;
50 let _ondrop = Ondrop(now);
51 log::warning!("[lock] getting server lock took more than {} ms", LOCK_WAIT_TIME_MS.1; &log::pfx());
52 tokio::time::sleep(tokio::time::Duration::MAX).await;
53 struct Ondrop(tokio::time::Instant);
54 impl Drop for Ondrop {
55 fn drop(&mut self) {
56 log::warning!("[lock] finally got server lock after {} ms", self.0.elapsed().as_millis(); &log::pfx());
57 }
58 }
59 });
60 let lock = self.0.lock().await;
61 task.abort();
62 lock
63 }
64}
65
66impl ServerState {
67 pub fn new(configs: BTreeMap<u64, ConfigSet>) -> Arc<SyncedServerState> {
68 Self::new_internal(configs, true)
69 }
70 #[cfg(test)]
71 pub(crate) fn new_without_task(configs: BTreeMap<u64, ConfigSet>) -> Arc<SyncedServerState> {
72 Self::new_internal(configs, false)
73 }
74 fn new_internal(configs: BTreeMap<u64, ConfigSet>, spawn_task: bool) -> Arc<SyncedServerState> {
75 let selbst = Arc::new(SyncedServerState(Mutex::new(Self {
76 lobbies: HashMap::new(),
77 player_ids: HashMap::new(),
78 reconnect_tokens: HashMap::new(),
79 })));
80 let mut sperre = selbst
83 .0
84 .try_lock()
85 .expect("noone else should have a lock at this time");
86 for (_, conf) in configs {
87 sperre.add_lobby(conf, Arc::clone(&selbst), spawn_task);
88 }
89 drop(sperre);
90 selbst
91 }
92
93 pub fn add_lobby(
96 &mut self,
97 conf: ConfigSet,
98 shared_server_state: Arc<SyncedServerState>,
99 spawn_task: bool,
100 ) {
101 let id = LobbyId::random();
102 let lobby = SharedLobbyState::new(LobbyState::new(
103 id,
104 Clients::new_empty(),
105 conf,
106 shared_server_state,
107 ));
108 if spawn_task {
109 tokio::task::spawn(crate::lobby::game::run(lobby.clone()));
112 }
113 self.lobbies.insert(id, lobby);
114 }
115 pub async fn remove_lobby(&mut self, id: LobbyId) {
116 if let Some(l) = self.lobbies.remove(&id) {
117 let mut lock = l.lock().await;
118 for p in lock.clients.players.players_all_mut() {
119 if let Some(con) = &mut p.con {
120 con.terminate();
121 }
122 }
123 for p in lock.clients.spectators.iter_mut() {
124 p.con.terminate();
125 }
126 }
127 }
128
129 pub fn lobbies(&self) -> impl Iterator<Item = (&LobbyId, &SharedLobbyState)> {
130 self.lobbies.iter()
131 }
132 pub fn lobby(&self, id: &LobbyId) -> Option<SharedLobbyState> {
133 self.lobbies.get(id).cloned()
134 }
135 pub fn gen_random_player_id_and_reconnect_token(
136 &mut self,
137 lobby_id: LobbyId,
138 ) -> (PlayerId, ReconnectToken) {
139 let mut player_id = PlayerId::random();
140 while self.player_ids.contains_key(&player_id) {
141 player_id = PlayerId::random();
142 }
143 self.player_ids.insert(player_id, Ok(lobby_id));
144
145 let mut token = ReconnectToken::random();
146 while self.reconnect_tokens.contains_key(&token) {
147 token = ReconnectToken::random();
148 }
149 self.reconnect_tokens
150 .insert(token, Ok((lobby_id, player_id)));
151
152 (player_id, token)
153 }
154}
155
156#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
157pub struct LobbyId(Uuid);
158
159impl LobbyId {
160 pub fn new(uuid: Uuid) -> Self {
161 Self(uuid)
162 }
163 pub fn random() -> Self {
164 Self(Uuid::new_v4())
165 }
166 pub fn uuid(&self) -> &Uuid {
167 &self.0
168 }
169}
170
171impl ServerState {
172 pub async fn cleanup(&mut self) {
173 let now = Instant::now();
174 self.reconnect_tokens.retain(|_token, value| match value {
175 Ok((_lobby_id, _player_id)) => true,
176 Err(time) => *time > now,
177 });
178 self.player_ids.retain(|_token, value| match value {
179 Ok(_lobby_id) => true,
180 Err(time) => *time > now,
181 });
182 }
183}
184
185impl std::fmt::Display for LobbyId {
186 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
187 write!(f, "{} ({})", uuid_human_hash(&self.0), self.0)
188 }
189}
190
191#[cfg(test)]
192mod test {
193 use std::{
194 sync::Arc,
195 time::{Duration, Instant},
196 };
197
198 use tokio::{spawn, time::sleep};
199
200 use crate::lobby::{
201 state::clients::{PlayerId, ReconnectToken},
202 test::get_server_and_lobby,
203 };
204
205 #[tokio::test]
206 async fn cleanup() {
207 let (server, _) = get_server_and_lobby();
208 let server2 = Arc::clone(&server);
209 spawn(async move {
210 let lock_for_a_bit = server2.lock().await;
211 sleep(Duration::from_millis(133)).await;
212 drop(lock_for_a_bit);
213 });
214 sleep(Duration::from_millis(10)).await;
215 let mut lock = server.lock().await;
216 lock.player_ids.clear();
217 lock.player_ids.insert(
218 PlayerId::random(),
219 Err(Instant::now() - Duration::from_secs(60)),
220 );
221 lock.player_ids.insert(
222 PlayerId::random(),
223 Err(Instant::now() - Duration::from_secs(180)),
224 );
225 lock.player_ids.insert(
226 PlayerId::random(),
227 Err(Instant::now() + Duration::from_secs(60)),
228 );
229 lock.reconnect_tokens.clear();
230 lock.reconnect_tokens.insert(
231 ReconnectToken::random(),
232 Err(Instant::now() - Duration::from_secs(60)),
233 );
234 lock.reconnect_tokens.insert(
235 ReconnectToken::random(),
236 Err(Instant::now() + Duration::from_secs(180)),
237 );
238 assert_eq!(lock.player_ids.len(), 3);
239 assert_eq!(lock.reconnect_tokens.len(), 2);
240 lock.cleanup().await;
241 assert_eq!(lock.player_ids.len(), 1);
242 assert_eq!(lock.reconnect_tokens.len(), 1);
243 }
244}