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