9ac4b2f05ef3bbed94ee7e207058d11ddae88c54
[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 #include "gameserver.h"
13 #include "highscores.c"
14 #include "servermonitor_server.c"
15 #include "vg/vg_opt.h"
16 #include "network_common.h"
17 #include "gameserver_db.h"
18 #include "vg/vg_m.h"
19 #include "vg/vg_msg.h"
20
21 static u64 const k_steamid_max = 0xffffffffffffffff;
22
23 static void inthandler( int signum ) {
24 sig_stop = 1;
25 }
26
27 /*
28 * Send message to single client, with authentication checking
29 */
30 static void gameserver_send_to_client( i32 client_id,
31 const void *pData, u32 cbData,
32 int nSendFlags ){
33 struct gameserver_client *client = &gameserver.clients[ client_id ];
34
35 if( !client->steamid )
36 return;
37
38 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
39 hSteamNetworkingSockets, client->connection,
40 pData, cbData, nSendFlags, NULL );
41 }
42
43 /*
44 * Send message to all clients if they are authenticated
45 */
46 static void gameserver_send_to_all( int ignore,
47 const void *pData, u32 cbData,
48 int nSendFlags ){
49 for( int i=0; i<vg_list_size(gameserver.clients); i++ ){
50 struct gameserver_client *client = &gameserver.clients[i];
51
52 if( i != ignore )
53 gameserver_send_to_client( i, pData, cbData, nSendFlags );
54 }
55 }
56
57 /*
58 * handle server update that client #'index' has joined
59 */
60 static void gameserver_player_join( int index ){
61 struct gameserver_client *joiner = &gameserver.clients[index];
62
63 netmsg_playerjoin join = { .inetmsg_id = k_inetmsg_playerjoin,
64 .index = index,
65 .steamid = joiner->steamid };
66
67 gameserver_send_to_all( index, &join, sizeof(join),
68 k_nSteamNetworkingSend_Reliable );
69
70 /*
71 * update the joining user about current connections
72 */
73 netmsg_playerusername *username =
74 alloca( sizeof(netmsg_playerusername) + NETWORK_USERNAME_MAX );
75 username->inetmsg_id = k_inetmsg_playerusername;
76
77 netmsg_playeritem *item =
78 alloca( sizeof(netmsg_playeritem) + ADDON_UID_MAX );
79 item->inetmsg_id = k_inetmsg_playeritem;
80
81 for( int i=0; i<vg_list_size(gameserver.clients); i++ ){
82 struct gameserver_client *client = &gameserver.clients[i];
83
84 if( (i == index) || !client->steamid )
85 continue;
86
87 /* join */
88 netmsg_playerjoin init = { .inetmsg_id = k_inetmsg_playerjoin,
89 .index = i,
90 .steamid = client->steamid };
91 gameserver_send_to_client( index, &init, sizeof(init),
92 k_nSteamNetworkingSend_Reliable );
93
94 /* username */
95 username->index = i;
96 u32 chs = vg_strncpy( client->username, username->name,
97 NETWORK_USERNAME_MAX,
98 k_strncpy_always_add_null );
99 u32 size = sizeof(netmsg_playerusername) + chs + 1;
100 gameserver_send_to_client( index, username, size,
101 k_nSteamNetworkingSend_Reliable );
102
103 /* items */
104 for( int j=0; j<k_netmsg_playeritem_max; j++ ){
105 chs = vg_strncpy( client->items[j], item->uid, ADDON_UID_MAX,
106 k_strncpy_always_add_null );
107 item->type_index = j;
108 item->client = i;
109 size = sizeof(netmsg_playeritem) + chs + 1;
110 gameserver_send_to_client( index, item, size,
111 k_nSteamNetworkingSend_Reliable );
112 }
113 }
114 }
115
116 /*
117 * Handle server update that player has left
118 */
119 static void gameserver_player_leave( int index ){
120 if( gameserver.auth_mode == eServerModeAuthentication ){
121 if( !gameserver.clients[ index ].steamid )
122 return;
123 }
124
125 netmsg_playerleave leave;
126 leave.inetmsg_id = k_inetmsg_playerleave;
127 leave.index = index;
128
129 vg_info( "Player leave (%d)\n", index );
130 gameserver_send_to_all( index, &leave, sizeof(leave),
131 k_nSteamNetworkingSend_Reliable );
132 }
133
134 /*
135 * Deletes client at index and disconnects the connection handle if it was
136 * set.
137 */
138 static void remove_client( int index ){
139 struct gameserver_client *client = &gameserver.clients[index];
140 if( client->connection ){
141 SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
142 hSteamNetworkingSockets, client->connection, -1 );
143 SteamAPI_ISteamNetworkingSockets_CloseConnection(
144 hSteamNetworkingSockets, client->connection,
145 k_ESteamNetConnectionEnd_Misc_InternalError,
146 NULL, 1 );
147 }
148 memset( client, 0, sizeof(struct gameserver_client) );
149 }
150
151 /*
152 * Handle incoming new connection and init flags on the steam handle. if the
153 * server is full the userdata (client_id) will be set to -1 on the handle.
154 */
155 static void handle_new_connection( HSteamNetConnection conn ){
156 SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
157 hSteamNetworkingSockets, conn, -1 );
158
159 int index = -1;
160
161 for( int i=0; i<vg_list_size(gameserver.clients); i++ ){
162 if( !gameserver.clients[i].active ){
163 index = i;
164 break;
165 }
166 }
167
168 if( index == -1 ){
169 vg_error( "Server full\n" );
170 SteamAPI_ISteamNetworkingSockets_CloseConnection(
171 hSteamNetworkingSockets, conn,
172 4500,
173 NULL, 1 );
174 return;
175 }
176
177 struct gameserver_client *client = &gameserver.clients[index];
178 EResult accept_status = SteamAPI_ISteamNetworkingSockets_AcceptConnection(
179 hSteamNetworkingSockets, conn );
180
181 if( accept_status == k_EResultOK ){
182 vg_success( "Accepted client (id: %u, index: %d)\n", conn, index );
183
184 client->active = 1;
185 client->connection = conn;
186
187 SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup(
188 hSteamNetworkingSockets, conn, gameserver.client_group );
189
190 SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
191 hSteamNetworkingSockets, conn, index );
192
193 if( gameserver.auth_mode != eServerModeAuthentication ){
194 client->steamid = k_steamid_max;
195 gameserver_player_join( index );
196 }
197 }
198 else{
199 vg_warn( "Error accepting connection (id: %u)\n", conn );
200 SteamAPI_ISteamNetworkingSockets_CloseConnection(
201 hSteamNetworkingSockets, conn,
202 k_ESteamNetConnectionEnd_Misc_InternalError,
203 NULL, 1 );
204 }
205 }
206
207 static void on_auth_status( CallbackMsg_t *msg ){
208 SteamNetAuthenticationStatus_t *info = (void *)msg->m_pubParam;
209 vg_info( " Authentication availibility: %s\n",
210 string_ESteamNetworkingAvailability(info->m_eAvail) );
211 vg_info( " %s\n", info->m_debugMsg );
212 }
213
214 /*
215 * Get client id of connection handle. Will be -1 if unkown to us either because
216 * the server is full or we already disconnected them
217 */
218 static i32 gameserver_conid( HSteamNetConnection hconn ){
219 i64 id = SteamAPI_ISteamNetworkingSockets_GetConnectionUserData(
220 hSteamNetworkingSockets, hconn );
221
222 if( (id < 0) || (id >= NETWORK_MAX_PLAYERS) )
223 return -1;
224
225 return id;
226 }
227
228 /*
229 * Callback for steam connection state change
230 */
231 static void on_connect_status( CallbackMsg_t *msg ){
232 SteamNetConnectionStatusChangedCallback_t *info = (void *)msg->m_pubParam;
233 vg_info( " Connection status changed for %lu\n", info->m_hConn );
234
235 vg_info( " %s -> %s\n",
236 string_ESteamNetworkingConnectionState(info->m_eOldState),
237 string_ESteamNetworkingConnectionState(info->m_info.m_eState) );
238
239 if( info->m_info.m_eState==k_ESteamNetworkingConnectionState_Connecting ){
240 handle_new_connection( info->m_hConn );
241 }
242
243 if( (info->m_info.m_eState ==
244 k_ESteamNetworkingConnectionState_ClosedByPeer ) ||
245 (info->m_info.m_eState ==
246 k_ESteamNetworkingConnectionState_ProblemDetectedLocally ) ||
247 (info->m_info.m_eState ==
248 k_ESteamNetworkingConnectionState_Dead) ||
249 (info->m_info.m_eState ==
250 k_ESteamNetworkingConnectionState_None) )
251 {
252 vg_info( "End reason: %d\n", info->m_info.m_eEndReason );
253
254 int client_id = gameserver_conid( info->m_hConn );
255 if( client_id != -1 ){
256 gameserver_player_leave( client_id );
257 remove_client( client_id );
258 }
259 else {
260 SteamAPI_ISteamNetworkingSockets_CloseConnection(
261 hSteamNetworkingSockets, info->m_hConn, 0, NULL, 0 );
262 }
263 }
264 }
265
266 /*
267 * recieve auth ticket from connection. will only accept it if we've added them
268 * to the client list first.
269 */
270 static void gameserver_rx_auth( SteamNetworkingMessage_t *msg ){
271 if( gameserver.auth_mode != eServerModeAuthentication ){
272 vg_error( "Running server without authentication. "
273 "Connection %u tried to authenticate.\n", msg->m_conn );
274 return;
275 }
276
277 int client_id = gameserver_conid( msg->m_conn );
278 if( client_id == -1 ) {
279 vg_warn( "Recieved auth ticket from unkown connection (%u)\n",
280 msg->m_conn );
281 return;
282 }
283
284 struct gameserver_client *client = &gameserver.clients[ client_id ];
285
286 if( client->steamid ){
287 vg_warn( "Already authorized this user but another app ticket was sent"
288 " again (%d conn: %u)\n", client_id, msg->m_conn );
289 return;
290 }
291
292 vg_low( "Attempting to verify user\n" );
293
294 if( msg->m_cbSize < sizeof(netmsg_auth) ){
295 vg_error( "Malformed auth ticket, too small (%u)\n", msg->m_conn );
296 return;
297 }
298
299 netmsg_auth *auth = msg->m_pData;
300
301 if( msg->m_cbSize < sizeof(netmsg_auth)+auth->ticket_length ||
302 auth->ticket_length > 1024 ){
303 vg_error( "Malformed auth ticket, ticket_length incorrect (%u)\n",
304 auth->ticket_length );
305 return;
306 }
307
308 u8 decrypted[1024];
309 u32 ticket_len = 1024;
310
311 int success = SteamEncryptedAppTicket_BDecryptTicket(
312 auth->ticket, auth->ticket_length, decrypted,
313 &ticket_len, gameserver.app_symmetric_key,
314 k_nSteamEncryptedAppTicketSymmetricKeyLen );
315
316 if( !success ){
317 vg_error( "Failed to decrypt users ticket (client %u)\n", msg->m_conn );
318 vg_error( " ticket length: %u\n", auth->ticket_length );
319 remove_client( client_id );
320 return;
321 }
322
323 if( SteamEncryptedAppTicket_GetTicketIssueTime( decrypted, ticket_len )){
324 RTime32 ctime = time(NULL),
325 tickettime = SteamEncryptedAppTicket_GetTicketIssueTime(
326 decrypted, ticket_len ),
327 expiretime = tickettime + 24*3*60*60;
328
329 if( ctime > expiretime ){
330 vg_error( "Ticket expired (client %u)\n", msg->m_conn );
331 remove_client( client_id );
332 return;
333 }
334 }
335
336 CSteamID steamid;
337 SteamEncryptedAppTicket_GetTicketSteamID( decrypted, ticket_len, &steamid );
338 vg_success( "User is authenticated! steamid %lu (%u)\n",
339 steamid.m_unAll64Bits, msg->m_conn );
340
341 client->steamid = steamid.m_unAll64Bits;
342 gameserver_player_join( client_id );
343 }
344
345 /*
346 * Player updates sent to us
347 * -----------------------------------------------------------------------------
348 */
349
350 static int packet_minsize( SteamNetworkingMessage_t *msg, u32 size ){
351 if( msg->m_cbSize < size ) {
352 vg_error( "Invalid packet size (must be at least %u)\n", size );
353 return 0;
354 }
355 else{
356 return 1;
357 }
358 }
359
360 struct db_set_username_thread_data {
361 u64 steamid;
362 char username[ NETWORK_USERNAME_MAX ];
363 };
364
365 static void gameserver_update_db_username( db_request *db_req ){
366 struct db_set_username_thread_data *inf = (void *)db_req->data;
367
368 if( inf->steamid == k_steamid_max )
369 return;
370
371 int admin = 0;
372 if( inf->steamid == 76561198072130043 )
373 admin = 2;
374
375 db_updateuser( inf->steamid, inf->username, admin );
376 }
377
378 static void gameserver_rx_200_300( SteamNetworkingMessage_t *msg ){
379 netmsg_blank *tmp = msg->m_pData;
380
381 int client_id = gameserver_conid( msg->m_conn );
382 if( client_id == -1 ) return;
383
384 struct gameserver_client *client = &gameserver.clients[ client_id ];
385
386 if( tmp->inetmsg_id == k_inetmsg_playerusername ){
387 if( !packet_minsize( msg, sizeof(netmsg_playerusername)+1 ))
388 return;
389
390 netmsg_playerusername *src = msg->m_pData;
391
392 u32 name_len = network_msgstring( src->name, msg->m_cbSize,
393 sizeof(netmsg_playerusername),
394 client->username,
395 NETWORK_USERNAME_MAX );
396
397 /* update other users about this change */
398 netmsg_playerusername *prop = alloca(sizeof(netmsg_playerusername)+
399 NETWORK_USERNAME_MAX );
400
401 prop->inetmsg_id = k_inetmsg_playerusername;
402 prop->index = client_id;
403 u32 chs = vg_strncpy( client->username, prop->name, NETWORK_USERNAME_MAX,
404 k_strncpy_always_add_null );
405
406 vg_info( "client #%d changed name to: %s\n", client_id, prop->name );
407
408 u32 propsize = sizeof(netmsg_playerusername) + chs + 1;
409 gameserver_send_to_all( client_id, prop, propsize,
410 k_nSteamNetworkingSend_Reliable );
411
412 /* update database about this */
413 db_request *call = db_alloc_request(
414 sizeof(struct db_set_username_thread_data) );
415 struct db_set_username_thread_data *inf = (void *)call->data;
416 inf->steamid = client->steamid;
417 vg_strncpy( client->username, inf->username,
418 sizeof(inf->username), k_strncpy_always_add_null );
419 call->handler = gameserver_update_db_username;
420 db_send_request( call );
421 }
422 else if( tmp->inetmsg_id == k_inetmsg_playerframe ){
423 /* propogate */
424 netmsg_playerframe *frame = alloca(msg->m_cbSize);
425 memcpy( frame, msg->m_pData, msg->m_cbSize );
426 frame->client = client_id;
427 gameserver_send_to_all( client_id, frame, msg->m_cbSize,
428 k_nSteamNetworkingSend_Unreliable );
429 }
430 else if( tmp->inetmsg_id == k_inetmsg_playeritem ){
431 netmsg_playeritem *item = msg->m_pData;
432
433 /* record */
434
435 if( item->type_index >= k_netmsg_playeritem_max ){
436 vg_warn( "Client #%d invalid equip type %u\n",
437 client_id, (u32)item->type_index );
438 return;
439 }
440
441 char *dest = client->items[ item->type_index ];
442
443 network_msgstring( item->uid, msg->m_cbSize, sizeof(netmsg_playeritem),
444 dest, ADDON_UID_MAX );
445
446 vg_info( "Client #%d equiped: [%s] %s\n",
447 client_id,
448 (const char *[]){[k_netmsg_playeritem_board]="board",
449 [k_netmsg_playeritem_player]="player",
450 [k_netmsg_playeritem_world0]="world0",
451 [k_netmsg_playeritem_world1]="world1"
452 }[item->type_index], item->uid );
453
454 /* propogate */
455 netmsg_playeritem *prop = alloca(msg->m_cbSize);
456 memcpy( prop, msg->m_pData, msg->m_cbSize );
457 prop->client = client_id;
458 gameserver_send_to_all( client_id, prop, msg->m_cbSize,
459 k_nSteamNetworkingSend_Reliable );
460 }
461 else {
462 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
463 tmp->inetmsg_id );
464 }
465 }
466
467 static void gameserver_request_respond( enum request_status status,
468 netmsg_request *res, vg_msg *body,
469 SteamNetworkingMessage_t *msg ){
470 int client_id = gameserver_conid( msg->m_conn );
471 u32 len = 0;
472 if( body ){
473 len = body->cur.co;
474 vg_low( "[%d#%d] Response: %d\n", client_id, (i32)res->id, status );
475 vg_msg_print( body, len );
476 }
477
478 res->status = status;
479
480 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
481 hSteamNetworkingSockets, msg->m_conn,
482 res, sizeof(netmsg_request) + len,
483 k_nSteamNetworkingSend_Reliable, NULL );
484 SteamAPI_SteamNetworkingMessage_t_Release( msg );
485 }
486
487 struct user_request_thread_data {
488 SteamNetworkingMessage_t *msg;
489 };
490
491 static u32 gameserver_get_current_week(void){
492 return time(NULL) / (7*24*60*60);
493 }
494
495 static enum request_status gameserver_cat_table(
496 vg_msg *msg,
497 const char *mod, const char *route, u32 week, const char *alias )
498 {
499 char table_name[ DB_TABLE_UID_MAX ];
500 if( !db_get_highscore_table_name( mod, route, week, table_name ) )
501 return k_request_status_out_of_memory;
502
503 char buf[512];
504 vg_str q;
505 vg_strnull( &q, buf, 512 );
506 vg_strcat( &q, "SELECT * FROM \"" );
507 vg_strcat( &q, table_name );
508 vg_strcat( &q, "\" ORDER BY time DESC LIMIT 10;" );
509 if( !vg_strgood(&q) )
510 return k_request_status_out_of_memory;
511
512 sqlite3_stmt *stmt = db_stmt( q.buffer );
513 if( !stmt )
514 return k_request_status_database_error;
515
516 vg_msg_frame( msg, alias );
517 for( u32 i=0; i<10; i ++ ){
518 int fc = sqlite3_step( stmt );
519
520 if( fc == SQLITE_ROW ){
521 i32 time = sqlite3_column_int( stmt, 1 );
522 i64 steamid_i64 = sqlite3_column_int64( stmt, 0 );
523 u64 steamid = *((u64 *)&steamid_i64);
524
525 if( steamid == k_steamid_max )
526 continue;
527
528 vg_msg_frame( msg, "" );
529 vg_msg_wkvu32( msg, "time", time );
530 vg_msg_wkvu64( msg, "steamid", steamid );
531
532 char username[32];
533 if( db_getuserinfo( steamid, username, sizeof(username), NULL ) )
534 vg_msg_wkvstr( msg, "username", username );
535 vg_msg_end_frame( msg );
536 }
537 else if( fc == SQLITE_DONE ){
538 break;
539 }
540 else {
541 log_sqlite3( fc );
542 break;
543 }
544 }
545
546 sqlite3_finalize( stmt );
547 vg_msg_end_frame( msg );
548 return k_request_status_ok;
549 }
550
551 static void gameserver_process_user_request( db_request *db_req ){
552 struct user_request_thread_data *inf = (void *)db_req->data;
553 SteamNetworkingMessage_t *msg = inf->msg;
554
555 int client_id = gameserver_conid( msg->m_conn );
556 if( client_id == -1 ){
557 SteamAPI_SteamNetworkingMessage_t_Release( msg );
558 return;
559 }
560
561 struct gameserver_client *client = &gameserver.clients[ client_id ];
562
563 netmsg_request *req = (netmsg_request *)msg->m_pData;
564 vg_msg data;
565 vg_msg_init( &data, req->q, msg->m_cbSize - sizeof(netmsg_request) );
566
567 /* create response packet */
568 netmsg_request *res = alloca( sizeof(netmsg_request) + 512 );
569 res->inetmsg_id = k_inetmsg_response;
570 res->id = req->id;
571 vg_msg body;
572 vg_msg_init( &body, res->q, 512 );
573
574 const char *endpoint = vg_msg_getkvstr( &data, "endpoint" );
575
576 if( !endpoint ){
577 gameserver_request_respond( k_request_status_invalid_endpoint,
578 res, NULL, msg );
579 return;
580 }
581
582 if( !strcmp( endpoint, "scoreboard" ) ){
583 const char *mod = vg_msg_getkvstr( &data, "mod" );
584 const char *route = vg_msg_getkvstr( &data, "route" );
585 u32 week = vg_msg_getkvu32( &data, "week", 0 );
586
587 if( week == NETWORK_LEADERBOARD_CURRENT_WEEK ){
588 gameserver_cat_table( &body, mod, route,
589 gameserver_get_current_week(), "rows_weekly" );
590 }
591 else if( week == NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK ){
592 gameserver_cat_table( &body, mod, route, 0, "rows" );
593 gameserver_cat_table( &body, mod, route,
594 gameserver_get_current_week(), "rows_weekly" );
595 }
596 else
597 gameserver_cat_table( &body, mod, route, week, "rows" );
598
599 if( body.error != k_vg_msg_error_OK ){
600 gameserver_request_respond( k_request_status_out_of_memory,
601 res, NULL, msg );
602 return;
603 }
604
605 gameserver_request_respond( k_request_status_ok, res, &body, msg );
606 }
607 else if( !strcmp( endpoint, "setlap" ) ){
608 if( client->steamid == k_steamid_max ){
609 gameserver_request_respond( k_request_status_unauthorized,
610 res, NULL, msg );
611 return;
612 }
613
614 const char *mod = vg_msg_getkvstr( &data, "mod" );
615 const char *route = vg_msg_getkvstr( &data, "route" );
616
617 char weekly_table[ DB_TABLE_UID_MAX ],
618 alltime_table[ DB_TABLE_UID_MAX ];
619
620 u32 week = gameserver_get_current_week();
621
622 if( !db_get_highscore_table_name( mod, route, 0, alltime_table ) ||
623 !db_get_highscore_table_name( mod, route, week, weekly_table ) ){
624 gameserver_request_respond( k_request_status_out_of_memory,
625 res, NULL, msg );
626 return;
627 }
628
629 i32 centiseconds = vg_msg_getkvi32( &data, "time", -1 );
630 if( centiseconds < 5*100 ){
631 gameserver_request_respond( k_request_status_client_error,
632 res, NULL, msg );
633 return;
634 }
635
636 db_writeusertime( alltime_table, client->steamid, centiseconds, 1 );
637 db_writeusertime( weekly_table, client->steamid, centiseconds, 1 );
638
639 gameserver_request_respond( k_request_status_ok, res, NULL, msg );
640 }
641 else{
642 gameserver_request_respond( k_request_status_invalid_endpoint,
643 res, NULL, msg );
644 }
645 }
646
647 static void gameserver_rx_300_400( SteamNetworkingMessage_t *msg ){
648 netmsg_blank *tmp = msg->m_pData;
649
650 int client_id = gameserver_conid( msg->m_conn );
651 if( client_id == -1 ){
652 SteamAPI_SteamNetworkingMessage_t_Release( msg );
653 return;
654 }
655
656 if( tmp->inetmsg_id == k_inetmsg_request ){
657 if( !packet_minsize( msg, sizeof(netmsg_request)+1 ))
658 return;
659
660 db_request *call = db_alloc_request(
661 sizeof(struct user_request_thread_data) );
662 struct user_request_thread_data *inf = (void *)call->data;
663 inf->msg = msg;
664 call->handler = gameserver_process_user_request;
665 db_send_request( call );
666 }
667 else {
668 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
669 tmp->inetmsg_id );
670 SteamAPI_SteamNetworkingMessage_t_Release( msg );
671 }
672 }
673
674 static void poll_connections(void){
675 SteamNetworkingMessage_t *messages[32];
676 int len;
677
678 while(1){
679 len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
680 hSteamNetworkingSockets,
681 gameserver.client_group, messages, vg_list_size(messages) );
682
683 if( len <= 0 )
684 return;
685
686 for( int i=0; i<len; i++ ){
687 SteamNetworkingMessage_t *msg = messages[i];
688
689 if( msg->m_cbSize < sizeof(netmsg_blank) ){
690 vg_warn( "Discarding message (too small: %d)\n",
691 msg->m_cbSize );
692 continue;
693 }
694
695 netmsg_blank *tmp = msg->m_pData;
696
697 if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) ){
698 gameserver_rx_200_300( msg );
699 SteamAPI_SteamNetworkingMessage_t_Release( msg );
700 }
701 else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) ){
702 gameserver_rx_300_400( msg );
703 }
704 else{
705 if( tmp->inetmsg_id == k_inetmsg_auth )
706 gameserver_rx_auth( msg );
707 else {
708 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
709 tmp->inetmsg_id );
710 }
711 SteamAPI_SteamNetworkingMessage_t_Release( msg );
712 }
713 }
714 }
715 }
716
717 static u64 seconds_to_server_ticks( double s ){
718 return s / 0.01;
719 }
720
721 static void test_runner( db_request *req ){
722 vg_warn( "RUNNER\n" );
723 char table[DB_TABLE_UID_MAX];
724 if( db_get_highscore_table_name( "sr002-local-mp_mtzero",
725 "Coastal Run", 0, table ) ){
726 if( db_writeusertime( table, 76561198072130043, 232, 1 ) ){
727 vg_success( "Written time\n" );
728 i32 v = db_readusertime( table, 76561198072130043 );
729 vg_success( "Returned time: %u\n", v );
730 }
731 }
732 }
733
734 int main( int argc, char *argv[] ){
735 signal( SIGINT, inthandler );
736 signal( SIGQUIT, inthandler );
737 signal( SIGPIPE, SIG_IGN );
738
739 char *arg;
740 while( vg_argp( argc, argv ) ){
741 if( vg_long_opt( "noauth" ) )
742 gameserver.auth_mode = eServerModeNoAuthentication;
743
744 /* TODO: Options to override, ammend, remove etc */
745 }
746
747 vg_set_mem_quota( 80*1024*1024 );
748 vg_alloc_quota();
749
750 db_init();
751 db_request *req = db_alloc_request(0);
752 if( req ){
753 req->handler = test_runner;
754 db_send_request(req);
755 }
756
757 monitor_start_server(); /* UNIX socket monitor */
758
759 /* steamworks init
760 * --------------------------------------------------------------- */
761 steamworks_ensure_txt( "2103940" );
762 if( gameserver.auth_mode == eServerModeAuthentication ){
763 if( !vg_load_steam_symetric_key( "application_key",
764 gameserver.app_symmetric_key )){
765 return 0;
766 }
767 }
768 else{
769 vg_warn( "Running without user authentication.\n" );
770 }
771
772 if( !SteamGameServer_Init( 0, NETWORK_PORT, NETWORK_PORT+1,
773 gameserver.auth_mode, "1.0.0.0" ) ){
774 vg_error( "SteamGameServer_Init failed\n" );
775 return 0;
776 }
777
778 void *hSteamGameServer = SteamAPI_SteamGameServer();
779 SteamAPI_ISteamGameServer_LogOnAnonymous( hSteamGameServer );
780
781 SteamAPI_ManualDispatch_Init();
782 HSteamPipe hsteampipe = SteamGameServer_GetHSteamPipe();
783 hSteamNetworkingSockets =
784 SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
785
786 steam_register_callback( k_iSteamNetAuthenticationStatus, on_auth_status );
787 steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack,
788 on_connect_status );
789
790 vg_success( "Steamworks API running\n" );
791 steamworks_event_loop( hsteampipe );
792
793 /*
794 * Create a listener
795 */
796 HSteamListenSocket listener;
797 SteamNetworkingIPAddr localAddr;
798 SteamAPI_SteamNetworkingIPAddr_Clear( &localAddr );
799 localAddr.m_port = NETWORK_PORT;
800
801 listener = SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
802 hSteamNetworkingSockets, &localAddr, 0, NULL );
803 gameserver.client_group = SteamAPI_ISteamNetworkingSockets_CreatePollGroup(
804 hSteamNetworkingSockets );
805
806 u64 server_ticks = 8000,
807 last_record_save = 8000,
808 last_scoreboard_gen = 0,
809 last_monitor_heartbeat = 0;
810
811 while( !sig_stop ){
812 monitor_event_loop();
813 steamworks_event_loop( hsteampipe );
814 poll_connections();
815
816 usleep(10000);
817 server_ticks ++;
818
819 if( server_ticks >
820 (last_monitor_heartbeat + seconds_to_server_ticks(10.0))){
821 last_monitor_heartbeat = server_ticks;
822 monitor_heartbeat();
823 }
824
825 if( db_killed() )
826 break;
827 }
828
829 SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets,
830 gameserver.client_group );
831 SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
832 hSteamNetworkingSockets, listener );
833
834 vg_info( "Shutting down\n..." );
835 SteamGameServer_Shutdown();
836 db_kill();
837 db_free();
838
839 return 0;
840 }