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