send chat messages to server
[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 if( tmp->inetmsg_id == k_inetmsg_chat ){
462 netmsg_chat *chat = msg->m_pData,
463 *prop = alloca( sizeof(netmsg_chat) + NETWORK_MAX_CHAT );
464 prop->inetmsg_id = k_inetmsg_chat;
465 prop->client = client_id;
466
467 u32 l = network_msgstring( chat->msg, msg->m_cbSize, sizeof(netmsg_chat),
468 prop->msg, NETWORK_MAX_CHAT );
469 vg_info( "[%d]: %s\n", client_id, prop->msg );
470
471 gameserver_send_to_all( client_id, prop, sizeof(netmsg_chat)+l,
472 k_nSteamNetworkingSend_Reliable );
473 }
474 else {
475 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
476 tmp->inetmsg_id );
477 }
478 }
479
480 static void gameserver_request_respond( enum request_status status,
481 netmsg_request *res, vg_msg *body,
482 SteamNetworkingMessage_t *msg ){
483 int client_id = gameserver_conid( msg->m_conn );
484 u32 len = 0;
485 if( body ){
486 len = body->cur.co;
487 vg_low( "[%d#%d] Response: %d\n", client_id, (i32)res->id, status );
488 vg_msg_print( body, len );
489 }
490
491 res->status = status;
492
493 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
494 hSteamNetworkingSockets, msg->m_conn,
495 res, sizeof(netmsg_request) + len,
496 k_nSteamNetworkingSend_Reliable, NULL );
497 SteamAPI_SteamNetworkingMessage_t_Release( msg );
498 }
499
500 struct user_request_thread_data {
501 SteamNetworkingMessage_t *msg;
502 };
503
504 static u32 gameserver_get_current_week(void){
505 return time(NULL) / (7*24*60*60);
506 }
507
508 static enum request_status gameserver_cat_table(
509 vg_msg *msg,
510 const char *mod, const char *route, u32 week, const char *alias )
511 {
512 char table_name[ DB_TABLE_UID_MAX ];
513 if( !db_get_highscore_table_name( mod, route, week, table_name ) )
514 return k_request_status_out_of_memory;
515
516 char buf[512];
517 vg_str q;
518 vg_strnull( &q, buf, 512 );
519 vg_strcat( &q, "SELECT * FROM \"" );
520 vg_strcat( &q, table_name );
521 vg_strcat( &q, "\" ORDER BY time DESC LIMIT 10;" );
522 if( !vg_strgood(&q) )
523 return k_request_status_out_of_memory;
524
525 sqlite3_stmt *stmt = db_stmt( q.buffer );
526 if( !stmt )
527 return k_request_status_database_error;
528
529 vg_msg_frame( msg, alias );
530 for( u32 i=0; i<10; i ++ ){
531 int fc = sqlite3_step( stmt );
532
533 if( fc == SQLITE_ROW ){
534 i32 time = sqlite3_column_int( stmt, 1 );
535 i64 steamid_i64 = sqlite3_column_int64( stmt, 0 );
536 u64 steamid = *((u64 *)&steamid_i64);
537
538 if( steamid == k_steamid_max )
539 continue;
540
541 vg_msg_frame( msg, "" );
542 vg_msg_wkvu32( msg, "time", time );
543 vg_msg_wkvu64( msg, "steamid", steamid );
544
545 char username[32];
546 if( db_getuserinfo( steamid, username, sizeof(username), NULL ) )
547 vg_msg_wkvstr( msg, "username", username );
548 vg_msg_end_frame( msg );
549 }
550 else if( fc == SQLITE_DONE ){
551 break;
552 }
553 else {
554 log_sqlite3( fc );
555 break;
556 }
557 }
558
559 sqlite3_finalize( stmt );
560 vg_msg_end_frame( msg );
561 return k_request_status_ok;
562 }
563
564 static void gameserver_process_user_request( db_request *db_req ){
565 struct user_request_thread_data *inf = (void *)db_req->data;
566 SteamNetworkingMessage_t *msg = inf->msg;
567
568 int client_id = gameserver_conid( msg->m_conn );
569 if( client_id == -1 ){
570 SteamAPI_SteamNetworkingMessage_t_Release( msg );
571 return;
572 }
573
574 struct gameserver_client *client = &gameserver.clients[ client_id ];
575
576 netmsg_request *req = (netmsg_request *)msg->m_pData;
577 vg_msg data;
578 vg_msg_init( &data, req->q, msg->m_cbSize - sizeof(netmsg_request) );
579
580 /* create response packet */
581 netmsg_request *res = alloca( sizeof(netmsg_request) + 512 );
582 res->inetmsg_id = k_inetmsg_response;
583 res->id = req->id;
584 vg_msg body;
585 vg_msg_init( &body, res->q, 512 );
586
587 const char *endpoint = vg_msg_getkvstr( &data, "endpoint" );
588
589 if( !endpoint ){
590 gameserver_request_respond( k_request_status_invalid_endpoint,
591 res, NULL, msg );
592 return;
593 }
594
595 if( !strcmp( endpoint, "scoreboard" ) ){
596 const char *mod = vg_msg_getkvstr( &data, "mod" );
597 const char *route = vg_msg_getkvstr( &data, "route" );
598 u32 week = vg_msg_getkvu32( &data, "week", 0 );
599
600 if( week == NETWORK_LEADERBOARD_CURRENT_WEEK ){
601 gameserver_cat_table( &body, mod, route,
602 gameserver_get_current_week(), "rows_weekly" );
603 }
604 else if( week == NETWORK_LEADERBOARD_ALLTIME_AND_CURRENT_WEEK ){
605 gameserver_cat_table( &body, mod, route, 0, "rows" );
606 gameserver_cat_table( &body, mod, route,
607 gameserver_get_current_week(), "rows_weekly" );
608 }
609 else
610 gameserver_cat_table( &body, mod, route, week, "rows" );
611
612 if( body.error != k_vg_msg_error_OK ){
613 gameserver_request_respond( k_request_status_out_of_memory,
614 res, NULL, msg );
615 return;
616 }
617
618 gameserver_request_respond( k_request_status_ok, res, &body, msg );
619 }
620 else if( !strcmp( endpoint, "setlap" ) ){
621 if( client->steamid == k_steamid_max ){
622 gameserver_request_respond( k_request_status_unauthorized,
623 res, NULL, msg );
624 return;
625 }
626
627 const char *mod = vg_msg_getkvstr( &data, "mod" );
628 const char *route = vg_msg_getkvstr( &data, "route" );
629
630 char weekly_table[ DB_TABLE_UID_MAX ],
631 alltime_table[ DB_TABLE_UID_MAX ];
632
633 u32 week = gameserver_get_current_week();
634
635 if( !db_get_highscore_table_name( mod, route, 0, alltime_table ) ||
636 !db_get_highscore_table_name( mod, route, week, weekly_table ) ){
637 gameserver_request_respond( k_request_status_out_of_memory,
638 res, NULL, msg );
639 return;
640 }
641
642 i32 centiseconds = vg_msg_getkvi32( &data, "time", -1 );
643 if( centiseconds < 5*100 ){
644 gameserver_request_respond( k_request_status_client_error,
645 res, NULL, msg );
646 return;
647 }
648
649 db_writeusertime( alltime_table, client->steamid, centiseconds, 1 );
650 db_writeusertime( weekly_table, client->steamid, centiseconds, 1 );
651
652 gameserver_request_respond( k_request_status_ok, res, NULL, msg );
653 }
654 else{
655 gameserver_request_respond( k_request_status_invalid_endpoint,
656 res, NULL, msg );
657 }
658 }
659
660 static void gameserver_rx_300_400( SteamNetworkingMessage_t *msg ){
661 netmsg_blank *tmp = msg->m_pData;
662
663 int client_id = gameserver_conid( msg->m_conn );
664 if( client_id == -1 ){
665 SteamAPI_SteamNetworkingMessage_t_Release( msg );
666 return;
667 }
668
669 if( tmp->inetmsg_id == k_inetmsg_request ){
670 if( !packet_minsize( msg, sizeof(netmsg_request)+1 ))
671 return;
672
673 db_request *call = db_alloc_request(
674 sizeof(struct user_request_thread_data) );
675 struct user_request_thread_data *inf = (void *)call->data;
676 inf->msg = msg;
677 call->handler = gameserver_process_user_request;
678 db_send_request( call );
679 }
680 else {
681 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
682 tmp->inetmsg_id );
683 SteamAPI_SteamNetworkingMessage_t_Release( msg );
684 }
685 }
686
687 static void poll_connections(void){
688 SteamNetworkingMessage_t *messages[32];
689 int len;
690
691 while(1){
692 len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
693 hSteamNetworkingSockets,
694 gameserver.client_group, messages, vg_list_size(messages) );
695
696 if( len <= 0 )
697 return;
698
699 for( int i=0; i<len; i++ ){
700 SteamNetworkingMessage_t *msg = messages[i];
701
702 if( msg->m_cbSize < sizeof(netmsg_blank) ){
703 vg_warn( "Discarding message (too small: %d)\n",
704 msg->m_cbSize );
705 continue;
706 }
707
708 netmsg_blank *tmp = msg->m_pData;
709
710 if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) ){
711 gameserver_rx_200_300( msg );
712 SteamAPI_SteamNetworkingMessage_t_Release( msg );
713 }
714 else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) ){
715 gameserver_rx_300_400( msg );
716 }
717 else{
718 if( tmp->inetmsg_id == k_inetmsg_auth )
719 gameserver_rx_auth( msg );
720 else {
721 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
722 tmp->inetmsg_id );
723 }
724 SteamAPI_SteamNetworkingMessage_t_Release( msg );
725 }
726 }
727 }
728 }
729
730 static u64 seconds_to_server_ticks( double s ){
731 return s / 0.01;
732 }
733
734 static void test_runner( db_request *req ){
735 vg_warn( "RUNNER\n" );
736 char table[DB_TABLE_UID_MAX];
737 if( db_get_highscore_table_name( "sr002-local-mp_mtzero",
738 "Coastal Run", 0, table ) ){
739 if( db_writeusertime( table, 76561198072130043, 232, 1 ) ){
740 vg_success( "Written time\n" );
741 i32 v = db_readusertime( table, 76561198072130043 );
742 vg_success( "Returned time: %u\n", v );
743 }
744 }
745 }
746
747 int main( int argc, char *argv[] ){
748 signal( SIGINT, inthandler );
749 signal( SIGQUIT, inthandler );
750 signal( SIGPIPE, SIG_IGN );
751
752 char *arg;
753 while( vg_argp( argc, argv ) ){
754 if( vg_long_opt( "noauth" ) )
755 gameserver.auth_mode = eServerModeNoAuthentication;
756
757 /* TODO: Options to override, ammend, remove etc */
758 }
759
760 vg_set_mem_quota( 80*1024*1024 );
761 vg_alloc_quota();
762
763 db_init();
764 db_request *req = db_alloc_request(0);
765 if( req ){
766 req->handler = test_runner;
767 db_send_request(req);
768 }
769
770 monitor_start_server(); /* UNIX socket monitor */
771
772 /* steamworks init
773 * --------------------------------------------------------------- */
774 steamworks_ensure_txt( "2103940" );
775 if( gameserver.auth_mode == eServerModeAuthentication ){
776 if( !vg_load_steam_symetric_key( "application_key",
777 gameserver.app_symmetric_key )){
778 return 0;
779 }
780 }
781 else{
782 vg_warn( "Running without user authentication.\n" );
783 }
784
785 if( !SteamGameServer_Init( 0, NETWORK_PORT, NETWORK_PORT+1,
786 gameserver.auth_mode, "1.0.0.0" ) ){
787 vg_error( "SteamGameServer_Init failed\n" );
788 return 0;
789 }
790
791 void *hSteamGameServer = SteamAPI_SteamGameServer();
792 SteamAPI_ISteamGameServer_LogOnAnonymous( hSteamGameServer );
793
794 SteamAPI_ManualDispatch_Init();
795 HSteamPipe hsteampipe = SteamGameServer_GetHSteamPipe();
796 hSteamNetworkingSockets =
797 SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
798
799 steam_register_callback( k_iSteamNetAuthenticationStatus, on_auth_status );
800 steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack,
801 on_connect_status );
802
803 vg_success( "Steamworks API running\n" );
804 steamworks_event_loop( hsteampipe );
805
806 /*
807 * Create a listener
808 */
809 HSteamListenSocket listener;
810 SteamNetworkingIPAddr localAddr;
811 SteamAPI_SteamNetworkingIPAddr_Clear( &localAddr );
812 localAddr.m_port = NETWORK_PORT;
813
814 listener = SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
815 hSteamNetworkingSockets, &localAddr, 0, NULL );
816 gameserver.client_group = SteamAPI_ISteamNetworkingSockets_CreatePollGroup(
817 hSteamNetworkingSockets );
818
819 u64 server_ticks = 8000,
820 last_record_save = 8000,
821 last_scoreboard_gen = 0,
822 last_monitor_heartbeat = 0;
823
824 while( !sig_stop ){
825 monitor_event_loop();
826 steamworks_event_loop( hsteampipe );
827 poll_connections();
828
829 usleep(10000);
830 server_ticks ++;
831
832 if( server_ticks >
833 (last_monitor_heartbeat + seconds_to_server_ticks(10.0))){
834 last_monitor_heartbeat = server_ticks;
835 monitor_heartbeat();
836 }
837
838 if( db_killed() )
839 break;
840 }
841
842 SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets,
843 gameserver.client_group );
844 SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
845 hSteamNetworkingSockets, listener );
846
847 vg_info( "Shutting down\n..." );
848 SteamGameServer_Shutdown();
849 db_kill();
850 db_free();
851
852 return 0;
853 }