aws_gamelift_server_sdk_rs/
server_state.rs

1use crate::{entity::GetInstanceCertificateResult, error::GameLiftErrorType};
2use tokio::task::JoinHandle;
3
4const HEALTHCHECK_TIMEOUT_SECONDS: u64 = 60;
5
6#[derive(Default)]
7struct SessionState {
8    is_process_ready: bool,
9    game_session_id: Option<crate::entity::GameSessionId>,
10    termination_time: Option<crate::entity::TerminationTimeType>,
11}
12
13#[derive(Default)]
14pub struct ServerStateInner {
15    process_parameters: Option<crate::process_parameters::ProcessParameters>,
16    session_state: parking_lot::RwLock<SessionState>,
17    http_client: crate::http_client::HttpClient,
18}
19
20impl ServerStateInner {
21    pub fn is_process_ready(&self) -> bool {
22        self.session_state.read().is_process_ready
23    }
24
25    pub fn get_game_session_id(&self) -> Option<crate::entity::GameSessionId> {
26        self.session_state.read().game_session_id.clone()
27    }
28
29    pub fn get_termination_time(&self) -> Option<crate::entity::TerminationTimeType> {
30        self.session_state.read().termination_time
31    }
32
33    pub async fn on_start_game_session(&self, game_session: crate::entity::GameSession) {
34        if !self.is_process_ready() {
35            log::debug!("Got a game session on inactive process. Ignoring.");
36            return;
37        }
38
39        self.session_state.write().game_session_id =
40            Some(game_session.game_session_id.clone().unwrap());
41        (self.process_parameters.as_ref().unwrap().on_start_game_session)(game_session).await;
42    }
43
44    pub async fn on_terminate_process(&self, termination_time: i64) {
45        log::debug!(
46            "ServerState got the terminateProcess signal. TerminateProcess: {}",
47            termination_time
48        );
49        self.session_state.write().termination_time = Some(termination_time);
50        (self.process_parameters.as_ref().unwrap().on_process_terminate)().await;
51    }
52
53    pub async fn on_update_game_session(
54        &self,
55        game_session: crate::entity::GameSession,
56        update_reason: crate::entity::UpdateReason,
57        backfill_ticket_id: String,
58    ) {
59        if !self.is_process_ready() {
60            log::warn!("Got an updated game session on inactive process.");
61            return;
62        }
63        (self.process_parameters.as_ref().unwrap().on_update_game_session)(
64            crate::entity::UpdateGameSession {
65                game_session: Some(game_session),
66                update_reason,
67                backfill_ticket_id,
68            },
69        )
70        .await;
71    }
72
73    pub async fn report_health(&self) {
74        if !self.is_process_ready() {
75            log::debug!("Reporting Health on an inactive process. Ignoring.");
76            return;
77        }
78
79        log::debug!("Reporting health using the OnHealthCheck callback.");
80
81        let result = tokio::time::timeout(
82            std::time::Duration::from_secs(HEALTHCHECK_TIMEOUT_SECONDS),
83            (self.process_parameters.as_ref().unwrap().on_health_check)(),
84        )
85        .await;
86
87        let report_health_result = if let Ok(health_check_result) = result {
88            self.http_client.report_health(health_check_result).await
89        } else {
90            self.http_client.report_health(false).await
91        };
92
93        if let Err(error) = report_health_result {
94            log::warn!("Could not send health starus: {:?}", error);
95        }
96    }
97}
98
99pub struct ServerState {
100    inner: std::sync::Arc<tokio::sync::RwLock<ServerStateInner>>,
101    websocket_listener: Option<crate::web_socket_listener::WebSocketListener>,
102    health_report_task: Option<JoinHandle<()>>,
103}
104
105impl Default for ServerState {
106    fn default() -> Self {
107        Self {
108            inner: std::sync::Arc::new(tokio::sync::RwLock::new(ServerStateInner::default())),
109            websocket_listener: None,
110            health_report_task: None,
111        }
112    }
113}
114
115impl ServerState {
116    pub async fn process_ready(
117        &mut self,
118        process_parameters: crate::process_parameters::ProcessParameters,
119    ) -> Result<(), crate::error::GameLiftErrorType> {
120        let port = process_parameters.port;
121        let log_paths = process_parameters.log_parameters.log_paths.clone();
122
123        let result = {
124            let mut inner = self.inner.write().await;
125
126            inner.session_state.write().is_process_ready = true;
127            inner.process_parameters = Some(process_parameters);
128
129            inner.http_client.process_ready(port, log_paths).await
130        };
131
132        self.start_health_check().await;
133
134        result
135    }
136
137    pub async fn process_ending(&self) -> Result<(), crate::error::GameLiftErrorType> {
138        let inner = self.inner.read().await;
139
140        inner.session_state.write().is_process_ready = false;
141        inner.http_client.process_ending().await
142    }
143
144    pub async fn activate_game_session(&self) -> Result<(), GameLiftErrorType> {
145        let inner = self.inner.read().await;
146
147        let game_session_id = inner.get_game_session_id();
148        if let Some(game_session_id) = game_session_id {
149            inner.http_client.activate_game_session(game_session_id).await
150        } else {
151            Err(crate::error::GameLiftErrorType::GameSessionIdNotSet)
152        }
153    }
154
155    pub async fn terminate_game_session(&self) -> Result<(), GameLiftErrorType> {
156        let inner = self.inner.read().await;
157
158        let game_session_id = inner.get_game_session_id();
159        if let Some(game_session_id) = game_session_id {
160            inner.http_client.terminate_game_session(game_session_id).await
161        } else {
162            Err(crate::error::GameLiftErrorType::GameSessionIdNotSet)
163        }
164    }
165
166    pub async fn get_game_session_id(
167        &self,
168    ) -> Result<crate::entity::GameSessionId, crate::error::GameLiftErrorType> {
169        match self.inner.read().await.get_game_session_id() {
170            Some(game_session_id) => Ok(game_session_id),
171            None => Err(crate::error::GameLiftErrorType::GameSessionIdNotSet),
172        }
173    }
174
175    pub async fn get_termination_time(
176        &self,
177    ) -> Result<crate::entity::TerminationTimeType, crate::error::GameLiftErrorType> {
178        match self.inner.read().await.get_termination_time() {
179            Some(value) => Ok(value),
180            None => Err(crate::error::GameLiftErrorType::TerminationTimeNotSet),
181        }
182    }
183
184    pub async fn update_player_session_creation_policy(
185        &self,
186        player_session_policy: crate::entity::PlayerSessionCreationPolicy,
187    ) -> Result<(), GameLiftErrorType> {
188        let inner = self.inner.read().await;
189
190        let game_session_id = inner.get_game_session_id();
191        if let Some(game_session_id) = game_session_id {
192            inner
193                .http_client
194                .update_player_session_creation_policy(game_session_id, player_session_policy)
195                .await
196        } else {
197            Err(crate::error::GameLiftErrorType::GameSessionIdNotSet)
198        }
199    }
200
201    pub async fn accept_player_session(
202        &self,
203        player_session_id: crate::entity::PlayerSessionId,
204    ) -> Result<(), GameLiftErrorType> {
205        let inner = self.inner.read().await;
206
207        let game_session_id = inner.get_game_session_id();
208        if let Some(game_session_id) = game_session_id {
209            inner.http_client.accept_player_session(player_session_id, game_session_id).await
210        } else {
211            Err(crate::error::GameLiftErrorType::GameSessionIdNotSet)
212        }
213    }
214
215    pub async fn remove_player_session(
216        &self,
217        player_session_id: crate::entity::PlayerSessionId,
218    ) -> Result<(), GameLiftErrorType> {
219        let inner = self.inner.read().await;
220
221        let game_session_id = inner.get_game_session_id();
222        if let Some(game_session_id) = game_session_id {
223            inner.http_client.remove_player_session(player_session_id, game_session_id).await
224        } else {
225            Err(crate::error::GameLiftErrorType::GameSessionIdNotSet)
226        }
227    }
228
229    pub async fn describe_player_sessions(
230        &self,
231        request: crate::entity::DescribePlayerSessionsRequest,
232    ) -> Result<crate::entity::DescribePlayerSessionsResult, GameLiftErrorType> {
233        self.inner.read().await.http_client.describe_player_sessions(request).await
234    }
235
236    pub async fn backfill_matchmaking(
237        &self,
238        request: crate::entity::StartMatchBackfillRequest,
239    ) -> Result<crate::entity::StartMatchBackfillResult, GameLiftErrorType> {
240        self.inner.read().await.http_client.backfill_matchmaking(request).await
241    }
242
243    pub async fn stop_matchmaking(
244        &self,
245        request: crate::entity::StopMatchBackfillRequest,
246    ) -> Result<(), GameLiftErrorType> {
247        self.inner.read().await.http_client.stop_matchmaking(request).await
248    }
249
250    async fn start_health_check(&mut self) {
251        log::debug!("Health check started.");
252
253        let inner_state = self.inner.clone();
254        let report_health_task = async move {
255            while inner_state.read().await.is_process_ready() {
256                {
257                    inner_state.read().await.report_health().await;
258                }
259
260                tokio::time::sleep(std::time::Duration::from_secs(HEALTHCHECK_TIMEOUT_SECONDS))
261                    .await;
262            }
263        };
264
265        self.health_report_task = Some(tokio::spawn(report_health_task));
266    }
267
268    pub async fn initialize_networking(&mut self) -> Result<(), crate::error::GameLiftErrorType> {
269        self.websocket_listener =
270            Some(crate::web_socket_listener::WebSocketListener::new(self.inner.clone()));
271        self.websocket_listener.as_mut().unwrap().connect().await
272    }
273
274    pub async fn get_instance_certificate(
275        &self,
276    ) -> Result<GetInstanceCertificateResult, GameLiftErrorType> {
277        self.inner.read().await.http_client.get_instance_certificate().await
278    }
279
280    pub async fn shutdown(&self) -> bool {
281        self.inner.read().await.session_state.write().is_process_ready = false;
282        if let Some(health_report_task) = &self.health_report_task {
283            health_report_task.abort();
284        }
285        self.websocket_listener.as_ref().unwrap().disconnect()
286    }
287}