test network 4
[carveJwlIkooP6JGAAIwe30JlM.git] / gameserver.c
1 /*
2 * Copyright (C) 2021-2023 Mt.ZERO Software, Harry Godden - All Rights Reserved
3 */
4
5 #define _DEFAULT_SOURCE
6 #include <signal.h>
7 #include <unistd.h>
8 #include <time.h>
9
10 volatile sig_atomic_t sig_stop;
11
12 static void inthandler( int signum ) {
13 sig_stop = 1;
14 }
15
16 #include "gameserver.h"
17 #include "highscores.c"
18 #include "servermonitor_server.c"
19 #include "vg/vg_opt.h"
20
21 static const u64 k_connection_unauthorized = 0xffffffffffffffff;
22
23 static u64_steamid get_connection_authsteamid( SteamNetworkingMessage_t *msg ){
24 i64 userdata = SteamAPI_ISteamNetworkingSockets_GetConnectionUserData(
25 hSteamNetworkingSockets, msg->m_conn );
26
27 return *((u64_steamid *)&userdata);
28 }
29
30 static void set_connection_authsteamid(HSteamNetConnection con, u64_steamid id){
31 i64 userdata = *((i64 *)&id);
32
33 SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
34 hSteamNetworkingSockets, con, userdata );
35 }
36
37 static void gameserver_send_to_all( int ignore,
38 const void *pData, u32 cbData,
39 int nSendFlags ){
40 for( int i=0; i<vg_list_size(gameserver.clients); i++ ){
41 struct gameserver_client *client = &gameserver.clients[i];
42
43 if( (i==ignore) || !client->active )
44 continue;
45
46 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
47 hSteamNetworkingSockets, client->connection,
48 pData, cbData, nSendFlags, NULL );
49 }
50 }
51
52 static void gameserver_populate_join_msg( int index, netmsg_playerjoin *msg ){
53 memset( msg, 0, sizeof(*msg) );
54 msg->inetmsg_id = k_inetmsg_playerjoin;
55 msg->index = index;
56 vg_strncpy( gameserver.clients[index].username, msg->username,
57 sizeof(msg->username), k_strncpy_always_add_null );
58 }
59
60 static void gameserver_player_join( int index ){
61 struct gameserver_client *joiner = &gameserver.clients[index];
62
63 netmsg_playerjoin join;
64 gameserver_populate_join_msg( index, &join );
65 gameserver_send_to_all( index, &join, sizeof(join),
66 k_nSteamNetworkingSend_Reliable );
67
68 /* update the joining user about current connections */
69 for( int i=0; i<vg_list_size(gameserver.clients); i++ ){
70 struct gameserver_client *client = &gameserver.clients[i];
71
72 if( (i==index) || !client->active )
73 continue;
74
75 netmsg_playerjoin init;
76 gameserver_populate_join_msg( i, &init );
77
78 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
79 hSteamNetworkingSockets, joiner->connection,
80 &init, sizeof(init), k_nSteamNetworkingSend_Reliable, NULL );
81 }
82 }
83
84 static void gameserver_player_leave( int index ){
85 netmsg_playerjoin leave;
86 leave.inetmsg_id = k_inetmsg_playerleave;
87 leave.index = index;
88
89 vg_info( "Player leave (%d)\n", index );
90 gameserver_send_to_all( index, &leave, sizeof(leave),
91 k_nSteamNetworkingSend_Reliable );
92 }
93
94 static void new_client_connecting( HSteamNetConnection client ){
95 int index = -1;
96
97 /* TODO: LRU */
98 for( int i=0; i<vg_list_size(gameserver.clients); i++ ){
99 if( !gameserver.clients[i].active ){
100 index = i;
101 break;
102 }
103 }
104
105 if( index == -1 ){
106 vg_error( "Server full\n" );
107 SteamAPI_ISteamNetworkingSockets_CloseConnection(
108 hSteamNetworkingSockets, client,
109 4500,
110 NULL, 1 );
111 return;
112 }
113
114 EResult accept_status = SteamAPI_ISteamNetworkingSockets_AcceptConnection(
115 hSteamNetworkingSockets, client );
116 if( accept_status == k_EResultOK ){
117 vg_success( "Accepted client (id: %u, index: %d)\n", client, index );
118
119 gameserver.clients[index].active = 1;
120 gameserver.clients[index].connection = client;
121
122 SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup(
123 hSteamNetworkingSockets,
124 client, gameserver.client_group );
125
126 /* Just to be sure */
127 set_connection_authsteamid( client, -1 );
128 gameserver_player_join( index );
129 }
130 else{
131 vg_warn( "Error accepting client (id: %u)\n", client );
132 SteamAPI_ISteamNetworkingSockets_CloseConnection(
133 hSteamNetworkingSockets, client,
134 k_ESteamNetConnectionEnd_Misc_InternalError,
135 NULL, 1 );
136 gameserver.clients[index].active = 0;
137 gameserver.clients[index].connection = 0;
138 }
139 }
140
141 static void on_auth_status( CallbackMsg_t *msg ){
142 SteamNetAuthenticationStatus_t *info = (void *)msg->m_pubParam;
143 vg_info( " Authentication availibility: %s\n",
144 string_ESteamNetworkingAvailability(info->m_eAvail) );
145 vg_info( " %s\n", info->m_debugMsg );
146 }
147
148 static int gameserver_client_index( HSteamNetConnection hconn ){
149 for( int i=0; i<vg_list_size(gameserver.clients); i++ ){
150 struct gameserver_client *client = &gameserver.clients[i];
151
152 if( client->active ){
153 if( client->connection == hconn ){
154 return i;
155 }
156 }
157 }
158 return -1;
159 }
160
161 static void on_connect_status( CallbackMsg_t *msg ){
162 SteamNetConnectionStatusChangedCallback_t *info = (void *)msg->m_pubParam;
163 vg_info( " Connection status changed for %lu\n", info->m_hConn );
164
165 vg_info( " %s -> %s\n",
166 string_ESteamNetworkingConnectionState(info->m_eOldState),
167 string_ESteamNetworkingConnectionState(info->m_info.m_eState) );
168
169 if( info->m_info.m_eState==k_ESteamNetworkingConnectionState_Connecting ){
170 new_client_connecting( info->m_hConn );
171 }
172
173 if( (info->m_info.m_eState ==
174 k_ESteamNetworkingConnectionState_ClosedByPeer ) ||
175 (info->m_info.m_eState ==
176 k_ESteamNetworkingConnectionState_ProblemDetectedLocally ) ){
177
178 int client_id = gameserver_client_index( info->m_hConn );
179 if( client_id != -1 ){
180 struct gameserver_client *client = &gameserver.clients[client_id];
181 client->connection = 0;
182 client->active = 0;
183 gameserver_player_leave(client_id);
184 }
185
186 vg_info( "End reason: %d\n", info->m_info.m_eEndReason );
187 SteamAPI_ISteamNetworkingSockets_CloseConnection(
188 hSteamNetworkingSockets, info->m_hConn, 0, NULL, 0 );
189 }
190 }
191
192 static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
193 if( gameserver.auth_mode != eServerModeAuthentication ){
194 vg_error( "Running server without authentication. "
195 "Connection %u tried to authenticate.\n", msg->m_conn );
196 return;
197 }
198
199 if( get_connection_authsteamid( msg ) != k_connection_unauthorized ){
200 vg_warn( "Already authorized this user but app ticket was sent"
201 " again (%u)\n", msg->m_conn );
202 return;
203 }
204
205 vg_low( "Attempting to verify user\n" );
206
207 if( msg->m_cbSize < sizeof(netmsg_auth) ){
208 vg_error( "Malformed auth ticket, too small (%u)\n", msg->m_conn );
209 return;
210 }
211
212 netmsg_auth *auth = msg->m_pData;
213
214 if( msg->m_cbSize < sizeof(netmsg_auth)+auth->ticket_length ||
215 auth->ticket_length > 1024 ){
216 vg_error( "Malformed auth ticket, ticket_length incorrect (%u)\n",
217 auth->ticket_length );
218 return;
219 }
220
221 u8 decrypted[1024];
222 u32 ticket_len = 1024;
223
224 int success = SteamEncryptedAppTicket_BDecryptTicket(
225 auth->ticket, auth->ticket_length, decrypted,
226 &ticket_len, gameserver.app_symmetric_key,
227 k_nSteamEncryptedAppTicketSymmetricKeyLen );
228
229 if( !success ){
230 vg_error( "Failed to decrypt users ticket (client %u)\n", msg->m_conn );
231 vg_error( " ticket length: %u\n", auth->ticket_length );
232
233 SteamAPI_ISteamNetworkingSockets_CloseConnection(
234 hSteamNetworkingSockets,
235 msg->m_conn, 0, NULL, 1 );
236 return;
237 }
238
239 if( SteamEncryptedAppTicket_GetTicketIssueTime( decrypted, ticket_len )){
240 RTime32 ctime = time(NULL),
241 tickettime = SteamEncryptedAppTicket_GetTicketIssueTime(
242 decrypted, ticket_len ),
243 expiretime = tickettime + 24*3*60*60;
244
245 if( ctime > expiretime ){
246 vg_error( "Ticket expired (client %u)\n", msg->m_conn );
247
248 /* TODO: Send expired information */
249 SteamAPI_ISteamNetworkingSockets_CloseConnection(
250 hSteamNetworkingSockets,
251 msg->m_conn, 0, NULL, 1 );
252 return;
253 }
254 }
255
256 CSteamID steamid;
257 SteamEncryptedAppTicket_GetTicketSteamID( decrypted, ticket_len, &steamid );
258 vg_success( "User is authenticated! steamid %lu (%u)\n",
259 steamid.m_unAll64Bits, msg->m_conn );
260
261 set_connection_authsteamid( msg->m_conn, steamid.m_unAll64Bits );
262 }
263
264 static int inet_require_auth( SteamNetworkingMessage_t *msg ){
265 if( gameserver.auth_mode == eServerModeNoAuthentication )
266 return 1;
267
268 if( get_connection_authsteamid( msg ) == k_connection_unauthorized ){
269 vg_warn( "Unauthorized request! Disconnecting client: %u\n",
270 msg->m_conn );
271
272 SteamAPI_ISteamNetworkingSockets_CloseConnection(
273 hSteamNetworkingSockets,
274 msg->m_conn, 0, NULL, 1 );
275
276 return 0;
277 }
278 else return 1;
279 }
280
281 /*
282 * Player updates sent to us
283 * -----------------------------------------------------------------------------
284 */
285
286 static int packet_minsize( SteamNetworkingMessage_t *msg, u32 size ){
287 if( msg->m_cbSize < size ) {
288 vg_error( "Invalid packet size (must be at least %u)\n", size );
289 return 0;
290 }
291 else{
292 return 1;
293 }
294 }
295
296 static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg ){
297 netmsg_blank *tmp = msg->m_pData;
298
299 if( tmp->inetmsg_id == k_inetmsg_playerusername ){
300 if( !packet_minsize( msg, sizeof(netmsg_playerusername) ))
301 return;
302
303 int client_id = gameserver_client_index( msg->m_conn );
304 if( client_id != -1 ){
305 struct gameserver_client *client = &gameserver.clients[ client_id ];
306 netmsg_playerusername *src = msg->m_pData;
307
308 vg_strncpy( src->username, client->username, sizeof(client->username),
309 k_strncpy_always_add_null );
310
311 /* update other users about this change */
312 netmsg_playerusername msg;
313 memset( &msg, 0, sizeof(msg) );
314 msg.inetmsg_id = k_inetmsg_playerusername;
315 msg.index = client_id;
316 vg_strncpy( client->username, msg.username, sizeof(msg.username),
317 k_strncpy_always_add_null );
318
319 gameserver_send_to_all( client_id, &msg, sizeof(msg),
320 k_nSteamNetworkingSend_Reliable );
321 }
322 }
323 }
324
325 #if 0
326 static void on_inet_score_request( SteamNetworkingMessage_t *msg ){
327 if( !inet_require_auth(msg) ) return;
328
329 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
330 hSteamNetworkingSockets, msg->m_conn,
331 &scoreboard_client_data, sizeof(netmsg_scoreboard),
332 k_nSteamNetworkingSend_Reliable, NULL );
333 }
334
335 static void on_inet_set_nickname( SteamNetworkingMessage_t *msg ){
336 if(!inet_require_auth(msg)) return;
337
338 u64_steamid steamid = get_connection_authsteamid(msg);
339 netmsg_set_nickname *setnick = msg->m_pData;
340 if( msg->m_cbSize < sizeof(netmsg_set_nickname) ){
341 vg_warn( "Invalid nickname request from client: %u, steamid: %lu\n",
342 msg->m_conn, steamid );
343 return;
344 }
345
346 highscore_set_user_nickname( steamid, setnick->nickname );
347 }
348
349 static void on_inet_set_score( SteamNetworkingMessage_t *msg ){
350 if(!inet_require_auth(msg)) return;
351
352 u64_steamid steamid = get_connection_authsteamid(msg);
353
354 if( msg->m_cbSize < sizeof(netmsg_set_score) ){
355 vg_warn( "Invalid set score post from client: %u, steamid: %lu\n",
356 msg->m_conn, steamid );
357 return;
358 }
359
360 netmsg_set_score *info = msg->m_pData;
361
362 if( msg->m_cbSize < sizeof(netmsg_set_score) +
363 sizeof(struct netmsg_score_record)*info->record_count ){
364 vg_warn( "Malformed set score post from client: %u, steamid: %lu\n",
365 msg->m_conn, steamid );
366 return;
367 }
368
369 for( int i=0; i<info->record_count; i++ ){
370 highscore_record temp;
371 temp.trackid = info->records[i].trackid;
372 temp.datetime = time(NULL);
373 temp.playerid = steamid;
374 temp.points = info->records[i].points;
375 temp.time = info->records[i].time;
376
377 highscores_push_record( &temp );
378 }
379 }
380 #endif
381
382 static void poll_connections(void){
383 SteamNetworkingMessage_t *messages[32];
384 int len;
385
386 while(1){
387 len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
388 hSteamNetworkingSockets,
389 gameserver.client_group, messages, vg_list_size(messages) );
390
391 if( len <= 0 )
392 return;
393
394 for( int i=0; i<len; i++ ){
395 SteamNetworkingMessage_t *msg = messages[i];
396
397 if( msg->m_cbSize < sizeof(netmsg_blank) ){
398 vg_warn( "Discarding message (too small: %d)\n",
399 msg->m_cbSize );
400 continue;
401 }
402
403 netmsg_blank *tmp = msg->m_pData;
404
405 if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) ){
406 gameserver_rx_200_300( msg );
407 }
408 else{
409 if( tmp->inetmsg_id == k_inetmsg_auth )
410 gameserver_rx_auth( msg );
411 #if 0
412 else if( tmp->inetmsg_id == k_inetmsg_scores_request )
413 on_inet_score_request( msg );
414 else if( tmp->inetmsg_id == k_inetmsg_set_nickname )
415 on_inet_set_nickname( msg );
416 else if( tmp->inetmsg_id == k_inetmsg_set_score )
417 on_inet_set_score( msg );
418 else if( tmp->inetmsg_id == k_inetmsg_playerframe )
419 on_inet_playerframe( msg );
420 #endif
421 else {
422 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
423 tmp->inetmsg_id );
424 }
425 }
426
427
428 SteamAPI_SteamNetworkingMessage_t_Release( msg );
429 }
430 }
431 }
432
433 static u64 seconds_to_server_ticks( double s ){
434 return s / 0.01;
435 }
436
437 static void generate_boards(void){
438 FILE *fp = fopen( "www/html/srhighscores.txt", "w" );
439
440 if( !fp ){
441 vg_error( "Can't write boards to www/html/srhighscores.txt\n" );
442 return;
443 }
444
445 for( int i=0; i<vg_list_size(track_infos); i++ ){
446 struct netmsg_board *board = &scoreboard_client_data.boards[i];
447
448 highscores_board_generate( board->data, i, 10 );
449 highscores_board_printf( fp, board->data, 10 );
450 }
451
452 fclose( fp );
453 }
454
455 int main( int argc, char *argv[] ){
456 signal( SIGINT, inthandler );
457 signal( SIGQUIT, inthandler );
458 signal( SIGPIPE, SIG_IGN );
459
460 char *arg;
461 while( vg_argp( argc, argv ) ){
462 if( vg_long_opt( "noauth" ) )
463 gameserver.auth_mode = eServerModeNoAuthentication;
464 }
465
466 /* TODO: Options to override, ammend, remove etc */
467
468 vg_set_mem_quota( 80*1024*1024 );
469 vg_alloc_quota();
470
471 highscores_init( 250000, 10000 );
472
473 if( !highscores_read() )
474 highscores_create_db();
475
476 steamworks_ensure_txt( "2103940" );
477
478 if( gameserver.auth_mode == eServerModeAuthentication ){
479 if( !vg_load_steam_symetric_key( "application_key",
480 gameserver.app_symmetric_key )){
481 return 0;
482 }
483 }
484 else{
485 vg_warn( "Running without user authentication.\n" );
486 }
487
488 if( !SteamGameServer_Init( 0, 27400, 27401,
489 gameserver.auth_mode, "1.0.0.0" ) ){
490 vg_error( "SteamGameServer_Init failed\n" );
491 return 0;
492 }
493
494 void *hSteamGameServer = SteamAPI_SteamGameServer();
495 SteamAPI_ISteamGameServer_LogOnAnonymous( hSteamGameServer );
496
497 SteamAPI_ManualDispatch_Init();
498 HSteamPipe hsteampipe = SteamGameServer_GetHSteamPipe();
499
500 //hSteamHTTP = SteamAPI_SteamGameServerHTTP();
501 hSteamNetworkingSockets =
502 SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
503
504 /*
505 * Server code
506 */
507
508 steam_register_callback( k_iSteamNetAuthenticationStatus, on_auth_status );
509 steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack,
510 on_connect_status );
511
512 vg_success( "Steamworks API running\n" );
513 steamworks_event_loop( hsteampipe );
514
515 /*
516 * Create a listener
517 */
518
519 HSteamListenSocket listener;
520 SteamNetworkingIPAddr localAddr;
521 SteamAPI_SteamNetworkingIPAddr_Clear( &localAddr );
522 localAddr.m_port = 27402;
523
524 listener = SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
525 hSteamNetworkingSockets, &localAddr, 0, NULL );
526 gameserver.client_group = SteamAPI_ISteamNetworkingSockets_CreatePollGroup(
527 hSteamNetworkingSockets );
528
529 u64 server_ticks = 8000,
530 last_record_save = 8000,
531 last_scoreboard_gen = 0,
532 last_monitor_heartbeat = 0;
533
534 generate_boards();
535 monitor_start_server();
536
537 while( !sig_stop ){
538 monitor_event_loop();
539 steamworks_event_loop( hsteampipe );
540 poll_connections();
541
542 usleep(10000);
543 server_ticks ++;
544
545 if( server_ticks >
546 (last_monitor_heartbeat + seconds_to_server_ticks(10.0))){
547 last_monitor_heartbeat = server_ticks;
548 monitor_heartbeat();
549 }
550
551 if( server_ticks > last_scoreboard_gen + seconds_to_server_ticks(60.0) ){
552 last_scoreboard_gen = server_ticks;
553 generate_boards();
554 }
555
556 if( server_ticks > last_record_save + seconds_to_server_ticks(10.0*60.0)){
557 last_record_save = server_ticks;
558 highscores_serialize_all();
559 }
560 }
561
562 highscores_serialize_all();
563
564 SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets,
565 gameserver.client_group );
566 SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
567 hSteamNetworkingSockets, listener );
568
569 vg_info( "Shutting down\n..." );
570 SteamGameServer_Shutdown();
571
572 return 0;
573 }