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