team04_server/lobby/state/
clients.rs

1use std::collections::HashMap;
2
3use uuid::Uuid;
4
5use crate::{log::uuid_human_hash::uuid_human_hash, server::Connection};
6
7use super::players::Player;
8
9#[derive(Debug)]
10pub struct Clients {
11    pub players: Players,
12    pub spectators: Spectators,
13}
14impl Clients {
15    pub fn new(players: Players, spectators: Spectators) -> Self {
16        Self {
17            players,
18            spectators,
19        }
20    }
21    pub fn new_empty() -> Self {
22        Self {
23            players: Players::new_empty(),
24            spectators: Spectators::new_empty(),
25        }
26    }
27}
28
29/// Contains the players of a [`Lobby`](super::LobbyState).
30///
31/// Once the game has started, no new players will be
32/// added and no existing players will be removed.
33#[derive(Debug)]
34pub struct Players(HashMap<PlayerId, Player>);
35impl Players {
36    pub fn new(players: HashMap<PlayerId, Player>) -> Self {
37        Self(players)
38    }
39    pub fn new_empty() -> Self {
40        Self(HashMap::new())
41    }
42    /// Note: may only be used in lobbies with `!lobby.game_started()`.
43    pub(crate) fn add(&mut self, id: PlayerId, player: Player) {
44        self.0.insert(id, player);
45    }
46    /// Note: may only be used in lobbies with `!lobby.game_started()`.
47    pub(crate) fn remove(&mut self, player_id: PlayerId) -> Option<Player> {
48        self.0.remove(&player_id)
49    }
50}
51
52#[derive(Debug)]
53pub struct Spectators(Vec<Spectator>);
54impl Spectators {
55    pub fn new(spectators: Vec<Spectator>) -> Self {
56        Self(spectators)
57    }
58    pub fn new_empty() -> Self {
59        Self(Vec::new())
60    }
61    pub(crate) fn add(&mut self, spectator: Spectator) {
62        self.0.push(spectator);
63    }
64    pub(crate) fn remove_by_name(&mut self, name: &str) -> Option<Spectator> {
65        self.iter()
66            .position(|spectator| spectator.name == name)
67            .map(|i| self.0.remove(i))
68    }
69}
70
71#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
72pub struct PlayerId(Uuid);
73
74impl PlayerId {
75    pub fn new(uuid: Uuid) -> Self {
76        Self(uuid)
77    }
78    pub fn random() -> Self {
79        Self(Uuid::new_v4())
80    }
81    pub fn uuid(&self) -> &Uuid {
82        &self.0
83    }
84}
85
86#[derive(Debug)]
87pub struct Spectator {
88    name: String,
89    pub(crate) con: Connection,
90}
91impl Spectator {
92    pub fn new(name: String, con: Connection) -> Self {
93        Self { name, con }
94    }
95    pub fn name(&self) -> &str {
96        &self.name
97    }
98}
99
100#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
101pub struct ReconnectToken(Uuid);
102
103impl ReconnectToken {
104    pub fn new(uuid: Uuid) -> Self {
105        Self(uuid)
106    }
107    pub fn random() -> Self {
108        Self(Uuid::new_v4())
109    }
110    pub fn uuid(&self) -> &Uuid {
111        &self.0
112    }
113}
114
115impl Players {
116    pub fn len(&self) -> usize {
117        self.0.len()
118    }
119    pub fn contains(&self, id: &PlayerId) -> bool {
120        self.0.contains_key(id)
121    }
122    pub fn get(&self, id: &PlayerId) -> Option<&Player> {
123        self.0.get(id)
124    }
125    pub fn get_mut(&mut self, id: &PlayerId) -> Option<&mut Player> {
126        self.0.get_mut(id)
127    }
128    pub fn iter_all(&self) -> std::collections::hash_map::Iter<PlayerId, Player> {
129        self.0.iter()
130    }
131    pub fn iter_all_mut(&mut self) -> std::collections::hash_map::IterMut<PlayerId, Player> {
132        self.0.iter_mut()
133    }
134    pub fn players_all(&self) -> std::collections::hash_map::Values<PlayerId, Player> {
135        self.0.values()
136    }
137    pub fn players_all_mut(&mut self) -> std::collections::hash_map::ValuesMut<PlayerId, Player> {
138        self.0.values_mut()
139    }
140    pub fn iter_alive(&self) -> impl Iterator<Item = (&PlayerId, &Player)> {
141        self.iter_all().filter(|(_, p)| p.lives > 0)
142    }
143    pub fn iter_alive_mut(&mut self) -> impl Iterator<Item = (&PlayerId, &mut Player)> {
144        self.iter_all_mut().filter(|(_, p)| p.lives > 0)
145    }
146    pub fn players_alive(&self) -> impl Iterator<Item = &Player> {
147        self.players_all().filter(|p| p.lives > 0)
148    }
149    pub fn players_alive_mut(&mut self) -> impl Iterator<Item = &mut Player> {
150        self.players_all_mut().filter(|p| p.lives > 0)
151    }
152    pub fn ids(&self) -> std::collections::hash_map::Keys<PlayerId, Player> {
153        self.0.keys()
154    }
155}
156
157impl Spectators {
158    pub fn len(&self) -> usize {
159        self.0.len()
160    }
161    pub fn iter(&self) -> std::slice::Iter<Spectator> {
162        self.0.iter()
163    }
164    pub fn iter_mut(&mut self) -> std::slice::IterMut<Spectator> {
165        self.0.iter_mut()
166    }
167}
168
169impl Clients {
170    pub async fn broadcast_message(&mut self, message: &str) {
171        self.players.broadcast_message(message).await;
172        self.spectators.broadcast_message(message).await;
173    }
174}
175
176impl Players {
177    pub async fn broadcast_message(&mut self, message: &str) {
178        for player in self.players_all_mut() {
179            player.send_message(message).await;
180        }
181    }
182}
183
184impl Spectators {
185    pub async fn broadcast_message(&mut self, message: &str) {
186        for spectator in self.iter_mut() {
187            spectator.send_message(message).await.ok();
188        }
189    }
190}
191
192impl Spectator {
193    pub async fn send_message(
194        &mut self,
195        message: &str,
196    ) -> Result<(), tokio_tungstenite::tungstenite::Error> {
197        self.con.send(message).await
198    }
199}
200
201impl std::fmt::Display for PlayerId {
202    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
203        write!(f, "{} ({})", uuid_human_hash(&self.0), self.0)
204    }
205}
206
207#[cfg(test)]
208mod test {
209    use crate::{
210        lobby::state::{
211            clients::{PlayerId, Players},
212            players::Player,
213        },
214        messages::client_role::PlayerRole,
215        server::Connection,
216    };
217
218    use super::{Spectator, Spectators};
219
220    #[test]
221    fn remove_player_by_id() {
222        let johnny = PlayerId::random();
223        let mut players = Players::new(
224            [
225                (
226                    PlayerId::random(),
227                    Player::new("jimmy".to_owned(), PlayerRole::Player, Connection::fake().0),
228                ),
229                (
230                    johnny,
231                    Player::new(
232                        "johnny".to_owned(),
233                        PlayerRole::Player,
234                        Connection::fake().0,
235                    ),
236                ),
237            ]
238            .into_iter()
239            .collect(),
240        );
241        assert_eq!(players.len(), 2);
242        players.add(
243            PlayerId::random(),
244            Player::new("jenny".to_owned(), PlayerRole::Player, Connection::fake().0),
245        );
246        assert!(players.players_all().any(|player| player.name() == "jenny"));
247        assert!(players.remove(PlayerId::random()).is_none());
248        assert_eq!(players.len(), 3);
249        assert!(players.remove(johnny).is_some());
250        assert_eq!(players.len(), 2);
251        assert!(!players.iter_all().any(|(id, _)| *id == johnny));
252    }
253
254    #[test]
255    fn remove_spectator_by_name() {
256        let mut spectators = Spectators::new_empty();
257        spectators.add(Spectator::new(
258            "Spectator #1".to_owned(),
259            Connection::fake().0,
260        ));
261        spectators.add(Spectator::new("john".to_owned(), Connection::fake().0));
262        assert!(spectators.iter().any(|spec| spec.name() == "john"));
263        assert_eq!(spectators.len(), 2);
264        assert!(spectators.remove_by_name("John").is_none());
265        assert!(spectators.iter().any(|spec| spec.name() == "john"));
266        assert_eq!(spectators.len(), 2);
267        assert!(spectators.remove_by_name("john").is_some());
268        assert!(!spectators.iter().any(|spec| spec.name() == "john"));
269        assert_eq!(spectators.len(), 1);
270    }
271}