1use dashmap::DashMap;
2use std::time::Instant;
3use tokio::sync::mpsc;
4use tokio_tungstenite::tungstenite::Message;
5
6use crate::analytics::AnalyticsHandle;
7use crate::deck_play_events::DeckPlayEventHandle;
8use crate::identity::{IdentityVerifier, SessionIdentity};
9use crate::room::Room;
10
11pub struct ConnectedPlayer {
12 pub player_id: String,
13 pub username: String,
14 pub room_id: Option<String>,
15 pub sender: mpsc::UnboundedSender<Message>,
16 pub connected: bool,
17 pub generation: u64,
18 pub last_seen: Instant,
19 pub disconnected_at: Option<Instant>,
20 pub is_service: bool,
21 pub identity: Vec<SessionIdentity>,
22}
23
24pub struct UsernameSession {
25 pub player_id: String,
26 pub room_id: Option<String>,
27 pub generation: u64,
28 pub connected: bool,
29 pub sender_closed: bool,
30 pub identity: Vec<SessionIdentity>,
31}
32
33impl From<&ConnectedPlayer> for UsernameSession {
34 fn from(player: &ConnectedPlayer) -> Self {
35 UsernameSession {
36 player_id: player.player_id.clone(),
37 room_id: player.room_id.clone(),
38 generation: player.generation,
39 connected: player.connected,
40 sender_closed: player.sender.is_closed(),
41 identity: player.identity.clone(),
42 }
43 }
44}
45
46pub struct ServerState {
47 pub players: DashMap<String, ConnectedPlayer>,
48 pub rooms: DashMap<String, Room>,
49 pub server_key: String,
50 pub max_rooms: usize,
51 pub official_key: Option<String>,
52 pub analytics: AnalyticsHandle,
53 pub deck_play_events: DeckPlayEventHandle,
54 pub identity: IdentityVerifier,
55}
56
57impl ServerState {
58 pub fn new(
59 server_key: String,
60 max_rooms: usize,
61 official_key: Option<String>,
62 analytics: AnalyticsHandle,
63 deck_play_events: DeckPlayEventHandle,
64 hub_jwks_url: Option<String>,
65 ) -> Self {
66 ServerState {
67 players: DashMap::new(),
68 rooms: DashMap::new(),
69 server_key,
70 max_rooms,
71 official_key,
72 analytics,
73 deck_play_events,
74 identity: IdentityVerifier::new(hub_jwks_url),
75 }
76 }
77
78 pub fn session_by_username(&self, username: &str) -> Option<UsernameSession> {
79 let mut connected = None;
80 for entry in self.players.iter() {
81 let player = entry.value();
82 if player.username != username {
83 continue;
84 }
85 if !player.connected {
86 return Some(UsernameSession::from(player));
87 }
88 connected.get_or_insert_with(|| UsernameSession::from(player));
89 }
90 connected
91 }
92}