vg_msg api change
[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->cur.co;
410 vg_success( "[%d#%d] Response: %d\n", client_id, (i32)res->id, status );
411 vg_msg_print( body, len );
412 }
413 else{
414 vg_warn( "[%d#%d] Response: %d\n", client_id, (i32)res->id, status );
415 }
416
417 res->status = status;
418
419 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
420 hSteamNetworkingSockets, msg->m_conn,
421 res, sizeof(netmsg_request) + len,
422 k_nSteamNetworkingSend_Reliable, NULL );
423 SteamAPI_SteamNetworkingMessage_t_Release( msg );
424 }
425
426 struct user_request_thread_data {
427 SteamNetworkingMessage_t *msg;
428 };
429
430 static void gameserver_process_user_request( db_request *db_req ){
431 struct user_request_thread_data *inf = (void *)db_req->data;
432 SteamNetworkingMessage_t *msg = inf->msg;
433
434 int client_id = gameserver_client_index( msg->m_conn );
435 if( client_id == -1 ){
436 SteamAPI_SteamNetworkingMessage_t_Release( msg );
437 return;
438 }
439
440 netmsg_request *req = (netmsg_request *)msg->m_pData;
441 vg_msg data;
442 vg_msg_init( &data, req->q, msg->m_cbSize - sizeof(netmsg_request) );
443
444 /* create response packet */
445 netmsg_request *res = alloca( sizeof(netmsg_request) + 512 );
446 res->inetmsg_id = k_inetmsg_response;
447 res->id = req->id;
448 vg_msg body;
449 vg_msg_init( &body, res->q, 512 );
450
451 const char *endpoint = vg_msg_getkvstr( &data, "endpoint" );
452
453 if( !endpoint ){
454 gameserver_request_respond( k_request_status_invalid_endpoint,
455 res, NULL, msg );
456 return;
457 }
458
459 if( !strcmp( endpoint, "scoreboard" ) ){
460 const char *mod = vg_msg_getkvstr( &data, "mod" );
461 const char *route = vg_msg_getkvstr( &data, "route" );
462 u32 week = vg_msg_getkvu32( &data, "week", 0 );
463
464 char table_name[ DB_TABLE_UID_MAX ];
465 if( !db_get_highscore_table_name( mod, route, week, table_name ) ){
466 gameserver_request_respond( k_request_status_out_of_memory,
467 res, NULL, msg );
468 return;
469 }
470
471 char buf[512];
472 vg_str q;
473 vg_strnull( &q, buf, 512 );
474 vg_strcat( &q, "SELECT * FROM \"" );
475 vg_strcat( &q, table_name );
476 vg_strcat( &q, "\" ORDER BY time DESC LIMIT 10;" );
477 if( !vg_strgood(&q) ) {
478 gameserver_request_respond( k_request_status_out_of_memory,
479 res, NULL, msg );
480 return;
481 }
482
483 sqlite3_stmt *stmt = db_stmt( q.buffer );
484
485 if( !stmt ){
486 gameserver_request_respond( k_request_status_database_error,
487 res, NULL, msg );
488 return;
489 }
490
491 vg_msg_frame( &body, "rows" );
492 for( u32 i=0; i<10; i ++ ){
493 int fc = sqlite3_step( stmt );
494
495 if( fc == SQLITE_ROW ){
496 i32 time = sqlite3_column_int( stmt, 1 );
497 i64 steamid_i64 = sqlite3_column_int64( stmt, 0 );
498 u64 steamid = *((u64 *)&steamid_i64);
499
500 vg_msg_frame( &body, "" );
501 vg_msg_wkvu32( &body, "time", time );
502 vg_msg_wkvu64( &body, "steamid", steamid );
503
504 char username[32];
505 if( db_getuserinfo( steamid, username, sizeof(username), NULL ) ){
506 vg_msg_wkvstr( &body, "username", username );
507 }
508
509 vg_msg_end_frame( &body );
510 }
511 else if( fc == SQLITE_DONE ){
512 break;
513 }
514 else {
515 log_sqlite3( fc );
516 break;
517 }
518 }
519
520 sqlite3_finalize( stmt );
521 vg_msg_end_frame( &body );
522
523 if( body.error != k_vg_msg_error_OK ){
524 gameserver_request_respond( k_request_status_out_of_memory,
525 res, NULL, msg );
526 return;
527 }
528
529 gameserver_request_respond( k_request_status_ok, res, &body, msg );
530 }
531 else{
532 gameserver_request_respond( k_request_status_invalid_endpoint,
533 res, NULL, msg );
534 }
535 }
536
537 static void gameserver_rx_300_400( SteamNetworkingMessage_t *msg ){
538 netmsg_blank *tmp = msg->m_pData;
539
540 int client_id = gameserver_client_index( msg->m_conn );
541 if( client_id == -1 ){
542 SteamAPI_SteamNetworkingMessage_t_Release( msg );
543 return;
544 }
545
546 if( tmp->inetmsg_id == k_inetmsg_request ){
547 if( !packet_minsize( msg, sizeof(netmsg_request)+1 ))
548 return;
549
550 db_request *call = db_alloc_request(
551 sizeof(struct user_request_thread_data) );
552 struct user_request_thread_data *inf = (void *)call->data;
553 inf->msg = msg;
554 call->handler = gameserver_process_user_request;
555 db_send_request( call );
556 }
557 else {
558 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
559 tmp->inetmsg_id );
560 SteamAPI_SteamNetworkingMessage_t_Release( msg );
561 }
562 }
563
564 static void poll_connections(void){
565 SteamNetworkingMessage_t *messages[32];
566 int len;
567
568 while(1){
569 len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
570 hSteamNetworkingSockets,
571 gameserver.client_group, messages, vg_list_size(messages) );
572
573 if( len <= 0 )
574 return;
575
576 for( int i=0; i<len; i++ ){
577 SteamNetworkingMessage_t *msg = messages[i];
578
579 if( msg->m_cbSize < sizeof(netmsg_blank) ){
580 vg_warn( "Discarding message (too small: %d)\n",
581 msg->m_cbSize );
582 continue;
583 }
584
585 netmsg_blank *tmp = msg->m_pData;
586
587 if( (tmp->inetmsg_id >= 200) && (tmp->inetmsg_id < 300) ){
588 gameserver_rx_200_300( msg );
589 SteamAPI_SteamNetworkingMessage_t_Release( msg );
590 }
591 else if( (tmp->inetmsg_id >= 300) && (tmp->inetmsg_id < 400) ){
592 gameserver_rx_300_400( msg );
593 }
594 else{
595 if( tmp->inetmsg_id == k_inetmsg_auth )
596 gameserver_rx_auth( msg );
597 else {
598 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
599 tmp->inetmsg_id );
600 }
601 SteamAPI_SteamNetworkingMessage_t_Release( msg );
602 }
603 }
604 }
605 }
606
607 static u64 seconds_to_server_ticks( double s ){
608 return s / 0.01;
609 }
610
611 static void test_runner( db_request *req ){
612 vg_warn( "RUNNER\n" );
613 char table[DB_TABLE_UID_MAX];
614 if( db_get_highscore_table_name( "sr002-local-mp_mtzero",
615 "Coastal Run", 0, table ) ){
616 if( db_writeusertime( table, 76561198072130043, 232, 1 ) ){
617 vg_success( "Written time\n" );
618 i32 v = db_readusertime( table, 76561198072130043 );
619 vg_success( "Returned time: %u\n", v );
620 }
621 }
622 }
623
624 int main( int argc, char *argv[] ){
625 signal( SIGINT, inthandler );
626 signal( SIGQUIT, inthandler );
627 signal( SIGPIPE, SIG_IGN );
628
629 char *arg;
630 while( vg_argp( argc, argv ) ){
631 if( vg_long_opt( "noauth" ) )
632 gameserver.auth_mode = eServerModeNoAuthentication;
633
634 /* TODO: Options to override, ammend, remove etc */
635 }
636
637 vg_set_mem_quota( 80*1024*1024 );
638 vg_alloc_quota();
639
640 db_init();
641 db_request *req = db_alloc_request(0);
642 if( req ){
643 req->handler = test_runner;
644 db_send_request(req);
645 }
646
647 monitor_start_server(); /* UNIX socket monitor */
648
649 /* steamworks init
650 * --------------------------------------------------------------- */
651 steamworks_ensure_txt( "2103940" );
652 if( gameserver.auth_mode == eServerModeAuthentication ){
653 if( !vg_load_steam_symetric_key( "application_key",
654 gameserver.app_symmetric_key )){
655 return 0;
656 }
657 }
658 else{
659 vg_warn( "Running without user authentication.\n" );
660 }
661
662 if( !SteamGameServer_Init( 0, 27400, 27401,
663 gameserver.auth_mode, "1.0.0.0" ) ){
664 vg_error( "SteamGameServer_Init failed\n" );
665 return 0;
666 }
667
668 void *hSteamGameServer = SteamAPI_SteamGameServer();
669 SteamAPI_ISteamGameServer_LogOnAnonymous( hSteamGameServer );
670
671 SteamAPI_ManualDispatch_Init();
672 HSteamPipe hsteampipe = SteamGameServer_GetHSteamPipe();
673 hSteamNetworkingSockets =
674 SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
675
676 steam_register_callback( k_iSteamNetAuthenticationStatus, on_auth_status );
677 steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack,
678 on_connect_status );
679
680 vg_success( "Steamworks API running\n" );
681 steamworks_event_loop( hsteampipe );
682
683 /*
684 * Create a listener
685 */
686 HSteamListenSocket listener;
687 SteamNetworkingIPAddr localAddr;
688 SteamAPI_SteamNetworkingIPAddr_Clear( &localAddr );
689 localAddr.m_port = 27402;
690
691 listener = SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
692 hSteamNetworkingSockets, &localAddr, 0, NULL );
693 gameserver.client_group = SteamAPI_ISteamNetworkingSockets_CreatePollGroup(
694 hSteamNetworkingSockets );
695
696 u64 server_ticks = 8000,
697 last_record_save = 8000,
698 last_scoreboard_gen = 0,
699 last_monitor_heartbeat = 0;
700
701 while( !sig_stop ){
702 monitor_event_loop();
703 steamworks_event_loop( hsteampipe );
704 poll_connections();
705
706 usleep(10000);
707 server_ticks ++;
708
709 if( server_ticks >
710 (last_monitor_heartbeat + seconds_to_server_ticks(10.0))){
711 last_monitor_heartbeat = server_ticks;
712 monitor_heartbeat();
713 }
714
715 if( db_killed() )
716 break;
717 }
718
719 SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets,
720 gameserver.client_group );
721 SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
722 hSteamNetworkingSockets, listener );
723
724 vg_info( "Shutting down\n..." );
725 SteamGameServer_Shutdown();
726 db_kill();
727 db_free();
728
729 return 0;
730 }