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