1use crate::{
4 api::{
5 self, ApiLeaderboardRecordList, RestRequest,
6 WriteLeaderboardRecordRequestLeaderboardRecordWrite,
7 },
8 async_client::AsyncRequestTick,
9 rt_api::{Presence, Socket, SocketEvent},
10};
11
12use nanoserde::DeJson;
13use std::{cell::RefCell, collections::HashMap, rc::Rc};
14use crate::matchmaker::Matchmaker;
15
16pub enum Event {
17 Presence {
18 joins: Vec<Presence>,
19 leaves: Vec<Presence>,
20 },
21 MatchData {
22 data: Vec<u8>,
23 opcode: i32,
24 user_id: String,
25 },
26}
27
28pub struct NakamaState {
29 server_url: String,
30 ws_url: String,
31 port: u32,
32
33 pub socket: Option<Socket>,
34 pub username: Option<String>,
35 pub token: Option<String>,
36 pub refresh_token: Option<String>,
37 pub leaderboards: HashMap<String, Rc<ApiLeaderboardRecordList>>,
39 pub match_id: Option<String>,
40 pub rpc_response: Option<String>,
41 pub error: Option<String>,
42 pub next_request: Option<Box<dyn AsyncRequestTick>>,
43}
44
45impl NakamaState {
46 pub fn reset(&mut self) {
47 self.socket = None;
48 self.username = None;
49 self.token = None;
50 self.refresh_token = None;
51 self.match_id = None;
52 self.error = None;
53 }
54
55 pub fn make_request<T, F>(&mut self, request: RestRequest<T>, on_success: F)
56 where
57 T: nanoserde::DeJson + 'static,
58 F: FnMut(T) -> () + 'static,
59 {
60 assert!(self.next_request.is_none());
61
62 let mut request = crate::async_client::make_request(&self.server_url, self.port, request);
63 request.on_success(on_success);
64 self.next_request = Some(Box::new(request));
65 }
66}
67
68pub struct ApiClient {
72 key: String,
73 events: Vec<Event>,
74 pub session_id: Option<String>,
75 pub matchmaker_token: Option<String>,
76 state: Rc<RefCell<NakamaState>>,
77 ongoing_request: Option<Box<dyn AsyncRequestTick>>,
78 socket_response: HashMap<u32, SocketEvent>,
79}
80
81impl ApiClient {
82 pub fn new(key: &str, server: &str, port: u32, protocol: &str) -> ApiClient {
83 ApiClient {
84 key: key.to_owned(),
85 state: Rc::new(RefCell::new(NakamaState {
86 ws_url: match protocol {
87 "http" => format!("ws://{}", server.to_owned()),
88 "https" => format!("wss://{}", server.to_owned()),
89 _ => panic!("Unsupported protocol"),
90 },
91
92 server_url: match protocol {
93 "http" => format!("http://{}", server.to_owned()),
94 "https" => format!("https://{}", server.to_owned()),
95 _ => panic!("Unsupported protocol"),
96 },
97 port,
98 socket: None,
99 token: None,
100 refresh_token: None,
101 leaderboards: HashMap::new(),
102 rpc_response: None,
103 error: None,
104 username: None,
105 match_id: None,
106 next_request: None,
107 })),
108 socket_response: HashMap::new(),
109 ongoing_request: None,
110 events: vec![],
111 session_id: None,
112 matchmaker_token: None,
113 }
114 }
115
116 pub fn in_progress(&self) -> bool {
117 self.ongoing_request.is_some() || self.state.borrow().next_request.is_some()
118 }
119
120 pub fn authenticate(&mut self, email: &str, password: &str) {
121 self.session_id = None;
122 self.state.borrow_mut().socket = None;
123 self.state.borrow_mut().username = None;
124
125 let request = api::authenticate_email(
126 &self.key,
127 "",
128 api::ApiAccountEmail {
129 email: email.to_owned(),
130 password: password.to_owned(),
131 vars: std::collections::HashMap::new(),
132 },
133 Some(false),
134 None,
135 );
136
137 self.state.borrow_mut().make_request(request, {
138 let state2 = self.state.clone();
139 move |session| {
140 let mut state = state2.borrow_mut();
141 state.socket = Some(Socket::connect(
142 &state.ws_url,
143 state.port,
144 false,
145 &session.token,
146 ));
147 state.token = Some(session.token);
148 state.refresh_token = Some(session.refresh_token);
149
150 let request = api::get_account(&state.token.as_ref().unwrap());
151 state.make_request(request, {
152 let state = state2.clone();
153 move |account| {
154 let mut state = state.borrow_mut();
155 state.username = Some(account.user.username);
156 }
157 });
158 }
159 });
160 }
161
162 pub fn register(&mut self, email: &str, password: &str, username: &str) {
163 let request = api::authenticate_email(
164 &self.key,
165 "",
166 api::ApiAccountEmail {
167 email: email.to_owned(),
168 password: password.to_owned(),
169 vars: std::collections::HashMap::new(),
170 },
171 Some(true),
172 Some(username),
173 );
174
175 self.state.borrow_mut().make_request(request, {
176 let state2 = self.state.clone();
177 move |session| {
178 let mut state = state2.borrow_mut();
179 state.socket = Some(Socket::connect(
180 &state.ws_url,
181 state.port,
182 false,
183 &session.token,
184 ));
185 state.token = Some(session.token);
186
187 let request = api::get_account(&state.token.as_ref().unwrap());
188 state.make_request(request, {
189 let state = state2.clone();
190 move |account| {
191 let mut state = state.borrow_mut();
192 state.username = Some(account.user.username);
193 }
194 });
195 }
196 });
197 }
198
199 pub fn username(&self) -> Option<String> {
200 self.state.borrow().username.clone()
201 }
202
203 pub fn rpc(&mut self, name: &str, body: &str) {
204 self.state.borrow_mut().rpc_response = None;
205
206 let request = api::rpc_func(
207 &self.state.borrow().token.as_ref().unwrap(),
208 name,
209 body,
210 None,
211 );
212 self.state.borrow_mut().make_request(request, {
213 let state2 = self.state.clone();
214 move |response| {
215 state2.borrow_mut().rpc_response = Some(response.payload);
216 }
217 });
218 }
219
220 pub fn logout(&mut self) {
221 self.session_id = None;
234 self.matchmaker_token = None;
235 self.state.borrow_mut().reset();
236 }
237
238 pub fn authenticated(&self) -> bool {
239 self.state.borrow().username.is_some()
240 && self.state.borrow().socket.is_some()
241 && self.state.borrow().socket.as_ref().unwrap().connected()
242 }
243
244 pub fn write_leaderboard_record(&mut self, leaderboard_id: &str, score: i32) {
245 assert!(self.state.borrow().token.is_some());
246 let request = api::write_leaderboard_record(
247 &self.state.borrow().token.as_ref().unwrap(),
248 leaderboard_id,
249 WriteLeaderboardRecordRequestLeaderboardRecordWrite {
250 metadata: "".to_owned(),
251 subscore: "0".to_owned(),
252 score: score.to_string(),
253 },
254 );
255
256 self.state
257 .borrow_mut()
258 .make_request(request, |_response| {})
259 }
260
261 pub fn list_leaderboard_records(&mut self, leaderboard_id: &str) {
262 assert!(self.state.borrow().token.is_some());
263
264 let request = api::list_leaderboard_records(
265 &self.state.borrow().token.as_ref().unwrap(),
266 leaderboard_id,
267 &[],
268 Some(100),
270 None,
271 None,
272 );
273
274 let id = leaderboard_id.to_owned();
275 self.state.borrow_mut().make_request(request, {
276 let state2 = self.state.clone();
277 move |response| {
278 state2
279 .borrow_mut()
280 .leaderboards
281 .insert(id.clone(), Rc::new(response));
282 }
283 })
284 }
285
286 pub fn leaderboard_records(
287 &self,
288 leaderboard_id: &str,
289 ) -> Option<Rc<ApiLeaderboardRecordList>> {
290 self.state
291 .borrow()
292 .leaderboards
293 .get(leaderboard_id)
294 .map(|records| records.clone())
295 }
296
297 pub fn try_recv(&mut self) -> Option<Event> {
298 self.events.pop()
299 }
300
301 pub fn tick(&mut self) {
302 let mut state = self.state.borrow_mut();
303 if let Some(ref mut socket) = state.socket {
304 if let Some(msg) = socket.try_recv() {
305 let event: SocketEvent = DeJson::deserialize_json(&msg).unwrap();
306
307 if let Some(ref cid) = event.cid {
308 self.socket_response
309 .insert(cid.parse::<u32>().unwrap(), event.clone());
310 }
311 if let Some(presence) = event.match_presence_event {
312 self.events.push(Event::Presence {
313 joins: presence.joins.iter().cloned().collect::<Vec<_>>(),
314 leaves: presence.leaves.iter().cloned().collect::<Vec<_>>(),
315 });
316 }
317
318 if let Some(new_match) = event.new_match {
319 self.session_id = Some(new_match.self_user.session_id.clone());
320 state.match_id = Some(new_match.match_id.clone());
321
322 self.events.push(Event::Presence {
323 joins: new_match.presences.clone(),
324 leaves: vec![],
325 });
326 }
327
328 if let Some(data) = event.match_data {
329 self.events.push(Event::MatchData {
330 user_id: data.presence.session_id,
331 opcode: data.op_code.parse().unwrap(),
332 data: data.data,
333 });
334 }
335
336 if let Some(matched) = event.matchmaker_matched {
337 self.matchmaker_token = Some(matched.token);
338 }
339 }
340 }
341 drop(state);
342
343 if let Some(ref mut request) = self.ongoing_request {
344 if request.tick() {
345 self.ongoing_request = None;
346 }
347 }
348
349 if let Some(request) = self.state.borrow_mut().next_request.take() {
350 assert!(self.ongoing_request.is_none());
351
352 self.ongoing_request = Some(request);
353 }
354 }
355
356 pub fn match_id(&self) -> Option<String> {
357 self.state.borrow().match_id.clone()
358 }
359
360 pub fn rpc_response(&self) -> Option<String> {
361 self.state.borrow().rpc_response.clone()
362 }
363
364 pub fn socket_add_matchmaker(
365 &mut self,
366 matchmaker: &Matchmaker,
367 ) {
368 let mut state = &mut *self.state.borrow_mut();
369
370 self.matchmaker_token = None;
371 state.match_id = None;
372
373 state.socket.as_mut().unwrap().add_matchmaker(
374 matchmaker.min_count,
375 matchmaker.max_count,
376 matchmaker.query.as_str(),
377 matchmaker.string_properties().as_str(),
378 matchmaker.numeric_properties().as_str(),
379 );
380 }
381
382 pub fn socket_create_match(&mut self) -> u32 {
383 self.state
384 .borrow_mut()
385 .socket
386 .as_mut()
387 .unwrap()
388 .create_match()
389 }
390
391 pub fn socket_join_match_by_id(&mut self, match_id: &str) -> u32 {
392 self.state
393 .borrow_mut()
394 .socket
395 .as_mut()
396 .unwrap()
397 .join_match_by_id(match_id)
398 }
399
400 pub fn socket_join_match_by_token(&mut self, token_id: &str) -> u32 {
401 self.state
402 .borrow_mut()
403 .socket
404 .as_mut()
405 .unwrap()
406 .join_match_by_token(token_id)
407 }
408 pub fn socket_leave_match(&mut self) -> u32 {
409 let state = &mut *self.state.borrow_mut();
410
411 state
412 .socket
413 .as_mut()
414 .unwrap()
415 .leave_match(state.match_id.as_ref().unwrap())
416 }
417
418 pub fn socket_send<T: nanoserde::SerBin>(&mut self, opcode: i32, data: &T) {
419 let binary_data = nanoserde::SerBin::serialize_bin(data);
420
421 let state = &mut *self.state.borrow_mut();
422
423 state.socket.as_mut().unwrap().match_data_send(
424 state.match_id.as_ref().unwrap(),
425 opcode,
426 &binary_data,
427 );
428 }
429
430 pub fn socket_response(&self, cid: u32) -> Option<SocketEvent> {
431 self.socket_response.get(&cid).cloned()
432 }
433
434 pub fn error(&self) -> Option<String> {
435 self.state.borrow().error.clone()
436 }
437}