1 // SONIC ROBO BLAST 2
2 //-----------------------------------------------------------------------------
3 // Copyright (C) 1998-2000 by DooM Legacy Team.
4 // Copyright (C) 1999-2020 by Sonic Team Junior.
5 // Copyright (C) 2020 by James R.
6 //
7 // This program is free software distributed under the
8 // terms of the GNU General Public License, version 2.
9 // See the 'LICENSE' file for more details.
10 //-----------------------------------------------------------------------------
11 /// \file mserv.c
12 /// \brief Commands used to communicate with the master server
13
14 #if !defined (UNDER_CE)
15 #include <time.h>
16 #endif
17
18 #include "doomstat.h"
19 #include "doomdef.h"
20 #include "command.h"
21 #include "i_threads.h"
22 #include "mserv.h"
23 #include "m_menu.h"
24 #include "z_zone.h"
25
26 #ifdef MASTERSERVER
27
28 static int MSId;
29 static int MSRegisteredId = -1;
30
31 static boolean MSRegistered;
32 static boolean MSInProgress;
33 static boolean MSUpdateAgain;
34
35 static time_t MSLastPing;
36
37 #ifdef HAVE_THREADS
38 static I_mutex MSMutex;
39 static I_cond MSCond;
40
41 # define Lock_state() I_lock_mutex (&MSMutex)
42 # define Unlock_state() I_unlock_mutex (MSMutex)
43 #else/*HAVE_THREADS*/
44 # define Lock_state()
45 # define Unlock_state()
46 #endif/*HAVE_THREADS*/
47
48 #ifndef NONET
49 static void Command_Listserv_f(void);
50 #endif
51
52 #endif/*MASTERSERVER*/
53
54 static void Update_parameters (void);
55
56 static void MasterServer_OnChange(void);
57
58 static CV_PossibleValue_t masterserver_update_rate_cons_t[] = {
59 {2, "MIN"},
60 {60, "MAX"},
61 {0,NULL}
62 };
63
64 consvar_t cv_masterserver = CVAR_INIT ("masterserver", "https://mb.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange);
65 consvar_t cv_servername = CVAR_INIT ("servername", "SRB2 server", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT, NULL, Update_parameters);
66
67 consvar_t cv_masterserver_update_rate = CVAR_INIT ("masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters);
68
69 INT16 ms_RoomId = -1;
70
71 #if defined (MASTERSERVER) && defined (HAVE_THREADS)
72 int ms_QueryId;
73 I_mutex ms_QueryId_mutex;
74
75 msg_server_t *ms_ServerList;
76 I_mutex ms_ServerList_mutex;
77 #endif
78
79 UINT16 current_port = 0;
80
81 // Room list is an external variable now.
82 // Avoiding having to get info ten thousand times...
83 msg_rooms_t room_list[NUM_LIST_ROOMS+1]; // +1 for easy test
84
85 /** Adds variables and commands relating to the master server.
86 *
87 * \sa cv_masterserver, cv_servername,
88 * Command_Listserv_f
89 */
AddMServCommands(void)90 void AddMServCommands(void)
91 {
92 #ifndef NONET
93 CV_RegisterVar(&cv_masterserver);
94 CV_RegisterVar(&cv_masterserver_update_rate);
95 CV_RegisterVar(&cv_masterserver_timeout);
96 CV_RegisterVar(&cv_masterserver_debug);
97 CV_RegisterVar(&cv_masterserver_token);
98 CV_RegisterVar(&cv_servername);
99 #ifdef MASTERSERVER
100 COM_AddCommand("listserv", Command_Listserv_f);
101 #endif
102 #endif
103 }
104
105 #ifdef MASTERSERVER
106
WarnGUI(void)107 static void WarnGUI (void)
108 {
109 #ifdef HAVE_THREADS
110 I_lock_mutex(&m_menu_mutex);
111 #endif
112 M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n\nCheck the console for details.\n"), NULL, MM_NOTHING);
113 #ifdef HAVE_THREADS
114 I_unlock_mutex(m_menu_mutex);
115 #endif
116 }
117
118 #define NUM_LIST_SERVER MAXSERVERLIST
GetShortServersList(INT32 room,int id)119 msg_server_t *GetShortServersList(INT32 room, int id)
120 {
121 msg_server_t *server_list;
122
123 // +1 for easy test
124 server_list = malloc(( NUM_LIST_SERVER + 1 ) * sizeof *server_list);
125
126 if (HMS_fetch_servers(server_list, room, id))
127 return server_list;
128 else
129 {
130 free(server_list);
131 WarnGUI();
132 return NULL;
133 }
134 }
135
GetRoomsList(boolean hosting,int id)136 INT32 GetRoomsList(boolean hosting, int id)
137 {
138 if (HMS_fetch_rooms( ! hosting, id))
139 return 1;
140 else
141 {
142 WarnGUI();
143 return -1;
144 }
145 }
146
147 #ifdef UPDATE_ALERT
GetMODVersion(int id)148 char *GetMODVersion(int id)
149 {
150 char *buffer;
151 int c;
152
153 (void)id;
154
155 buffer = malloc(16);
156
157 c = HMS_compare_mod_version(buffer, 16);
158
159 #ifdef HAVE_THREADS
160 I_lock_mutex(&ms_QueryId_mutex);
161 {
162 if (id != ms_QueryId)
163 c = -1;
164 }
165 I_unlock_mutex(ms_QueryId_mutex);
166 #endif
167
168 if (c > 0)
169 return buffer;
170 else
171 {
172 free(buffer);
173
174 if (! c)
175 WarnGUI();
176
177 return NULL;
178 }
179 }
180
181 // Console only version of the above (used before game init)
GetMODVersion_Console(void)182 void GetMODVersion_Console(void)
183 {
184 char buffer[16];
185
186 if (HMS_compare_mod_version(buffer, sizeof buffer) > 0)
187 I_Error(UPDATE_ALERT_STRING_CONSOLE, VERSIONSTRING, buffer);
188 }
189 #endif
190
191 #ifndef NONET
192 /** Gets a list of game servers. Called from console.
193 */
Command_Listserv_f(void)194 static void Command_Listserv_f(void)
195 {
196 CONS_Printf(M_GetText("Retrieving server list...\n"));
197
198 {
199 HMS_list_servers();
200 }
201 }
202 #endif
203
204 static void
Finish_registration(void)205 Finish_registration (void)
206 {
207 int registered;
208
209 CONS_Printf("Registering this server on the master server...\n");
210
211 registered = HMS_register();
212
213 Lock_state();
214 {
215 MSRegistered = registered;
216 MSRegisteredId = MSId;
217
218 time(&MSLastPing);
219 }
220 Unlock_state();
221
222 if (registered)
223 CONS_Printf("Master server registration successful.\n");
224 }
225
226 static void
Finish_update(void)227 Finish_update (void)
228 {
229 int registered;
230 int done;
231
232 Lock_state();
233 {
234 registered = MSRegistered;
235 MSUpdateAgain = false;/* this will happen anyway */
236 }
237 Unlock_state();
238
239 if (registered)
240 {
241 if (HMS_update())
242 {
243 Lock_state();
244 {
245 time(&MSLastPing);
246 MSRegistered = true;
247 }
248 Unlock_state();
249
250 CONS_Printf("Updated master server listing.\n");
251 }
252 else
253 Finish_registration();
254 }
255 else
256 Finish_registration();
257
258 Lock_state();
259 {
260 done = ! MSUpdateAgain;
261
262 if (done)
263 MSInProgress = false;
264 }
265 Unlock_state();
266
267 if (! done)
268 Finish_update();
269 }
270
271 static void
Finish_unlist(void)272 Finish_unlist (void)
273 {
274 int registered;
275
276 Lock_state();
277 {
278 registered = MSRegistered;
279 }
280 Unlock_state();
281
282 if (registered)
283 {
284 CONS_Printf("Removing this server from the master server...\n");
285
286 if (HMS_unlist())
287 CONS_Printf("Server deregistration request successfully sent.\n");
288
289 Lock_state();
290 {
291 MSRegistered = false;
292 }
293 Unlock_state();
294
295 #ifdef HAVE_THREADS
296 I_wake_all_cond(&MSCond);
297 #endif
298 }
299
300 Lock_state();
301 {
302 if (MSId == MSRegisteredId)
303 MSId++;
304 }
305 Unlock_state();
306 }
307
308 #ifdef HAVE_THREADS
309 static int *
Server_id(void)310 Server_id (void)
311 {
312 int *id;
313 id = malloc(sizeof *id);
314 Lock_state();
315 {
316 *id = MSId;
317 }
318 Unlock_state();
319 return id;
320 }
321
322 static int *
New_server_id(void)323 New_server_id (void)
324 {
325 int *id;
326 id = malloc(sizeof *id);
327 Lock_state();
328 {
329 *id = ++MSId;
330 I_wake_all_cond(&MSCond);
331 }
332 Unlock_state();
333 return id;
334 }
335
336 static void
Register_server_thread(int * id)337 Register_server_thread (int *id)
338 {
339 int same;
340
341 Lock_state();
342 {
343 /* wait for previous unlist to finish */
344 while (*id == MSId && MSRegistered)
345 I_hold_cond(&MSCond, MSMutex);
346
347 same = ( *id == MSId );/* it could have been a while */
348 }
349 Unlock_state();
350
351 if (same)/* it could have been a while */
352 Finish_registration();
353
354 free(id);
355 }
356
357 static void
Update_server_thread(int * id)358 Update_server_thread (int *id)
359 {
360 int same;
361
362 Lock_state();
363 {
364 same = ( *id == MSRegisteredId );
365 }
366 Unlock_state();
367
368 if (same)
369 Finish_update();
370
371 free(id);
372 }
373
374 static void
Unlist_server_thread(int * id)375 Unlist_server_thread (int *id)
376 {
377 int same;
378
379 Lock_state();
380 {
381 same = ( *id == MSRegisteredId );
382 }
383 Unlock_state();
384
385 if (same)
386 Finish_unlist();
387
388 free(id);
389 }
390
391 static void
Change_masterserver_thread(char * api)392 Change_masterserver_thread (char *api)
393 {
394 Lock_state();
395 {
396 while (MSRegistered)
397 I_hold_cond(&MSCond, MSMutex);
398 }
399 Unlock_state();
400
401 HMS_set_api(api);
402 }
403 #endif/*HAVE_THREADS*/
404
RegisterServer(void)405 void RegisterServer(void)
406 {
407 #ifdef MASTERSERVER
408 #ifdef HAVE_THREADS
409 I_spawn_thread(
410 "register-server",
411 (I_thread_fn)Register_server_thread,
412 New_server_id()
413 );
414 #else
415 Finish_registration();
416 #endif
417 #endif/*MASTERSERVER*/
418 }
419
UpdateServer(void)420 static void UpdateServer(void)
421 {
422 #ifdef HAVE_THREADS
423 I_spawn_thread(
424 "update-server",
425 (I_thread_fn)Update_server_thread,
426 Server_id()
427 );
428 #else
429 Finish_update();
430 #endif
431 }
432
UnregisterServer(void)433 void UnregisterServer(void)
434 {
435 #ifdef MASTERSERVER
436 #ifdef HAVE_THREADS
437 I_spawn_thread(
438 "unlist-server",
439 (I_thread_fn)Unlist_server_thread,
440 Server_id()
441 );
442 #else
443 Finish_unlist();
444 #endif
445 #endif/*MASTERSERVER*/
446 }
447
448 static boolean
Online(void)449 Online (void)
450 {
451 return ( serverrunning && ms_RoomId > 0 );
452 }
453
SendPingToMasterServer(void)454 static inline void SendPingToMasterServer(void)
455 {
456 int ready;
457 time_t now;
458
459 if (Online())
460 {
461 time(&now);
462
463 Lock_state();
464 {
465 ready = (
466 MSRegisteredId == MSId &&
467 ! MSInProgress &&
468 now >= ( MSLastPing + 60 * cv_masterserver_update_rate.value )
469 );
470
471 if (ready)
472 MSInProgress = true;
473 }
474 Unlock_state();
475
476 if (ready)
477 UpdateServer();
478 }
479 }
480
MasterClient_Ticker(void)481 void MasterClient_Ticker(void)
482 {
483 #ifdef MASTERSERVER
484 SendPingToMasterServer();
485 #endif
486 }
487
488 static void
Set_api(const char * api)489 Set_api (const char *api)
490 {
491 #ifdef HAVE_THREADS
492 I_spawn_thread(
493 "change-masterserver",
494 (I_thread_fn)Change_masterserver_thread,
495 strdup(api)
496 );
497 #else
498 HMS_set_api(strdup(api));
499 #endif
500 }
501
502 #endif/*MASTERSERVER*/
503
504 static void
Update_parameters(void)505 Update_parameters (void)
506 {
507 #ifdef MASTERSERVER
508 int registered;
509 int delayed;
510
511 if (Online())
512 {
513 Lock_state();
514 {
515 delayed = MSInProgress;
516
517 if (delayed)/* do another update after the current one */
518 MSUpdateAgain = true;
519 else
520 registered = MSRegistered;
521 }
522 Unlock_state();
523
524 if (! delayed && registered)
525 UpdateServer();
526 }
527 #endif/*MASTERSERVER*/
528 }
529
MasterServer_OnChange(void)530 static void MasterServer_OnChange(void)
531 {
532 #ifdef MASTERSERVER
533 UnregisterServer();
534
535 /*
536 TODO: remove this for v2, it's just a hack
537 for those coming in with an old config.
538 */
539 if (
540 ! cv_masterserver.changed &&
541 strcmp(cv_masterserver.string, "ms.srb2.org:28900") == 0
542 ){
543 CV_StealthSet(&cv_masterserver, cv_masterserver.defaultvalue);
544 }
545
546 Set_api(cv_masterserver.string);
547
548 if (Online())
549 RegisterServer();
550 #endif/*MASTERSERVER*/
551 }
552