aws_gamelift_server_sdk_rs/
server_state.rs1use 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}