gameserver monitor initial work
[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 static void inthandler( int signum ) {
13 sig_stop = 1;
14 }
15
16 #include "gameserver.h"
17 #include "highscores.c"
18 #include "servermonitor_server.c"
19 #include "vg/vg_opt.h"
20
21 static const u64 k_connection_unauthorized = 0xffffffffffffffff;
22
23 static u64_steamid get_connection_authsteamid( SteamNetworkingMessage_t *msg ){
24 i64 userdata = SteamAPI_ISteamNetworkingSockets_GetConnectionUserData(
25 hSteamNetworkingSockets, msg->m_conn );
26
27 return *((u64_steamid *)&userdata);
28 }
29
30 static void set_connection_authsteamid(HSteamNetConnection con, u64_steamid id){
31 i64 userdata = *((i64 *)&id);
32
33 SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
34 hSteamNetworkingSockets, con, userdata );
35 }
36
37 static void new_client_connecting( HSteamNetConnection client ){
38 EResult accept_status = SteamAPI_ISteamNetworkingSockets_AcceptConnection(
39 hSteamNetworkingSockets, client );
40
41 if( accept_status == k_EResultOK ){
42 vg_success( "Accepted client (id: %u)\n", client );
43 SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup(
44 hSteamNetworkingSockets,
45 client, gameserver.client_group );
46
47 /* Just to be sure */
48 set_connection_authsteamid( client, -1 );
49 }
50 else{
51 vg_warn( "Error accepting client (id: %u)\n", client );
52 }
53 }
54
55 static void on_auth_status( CallbackMsg_t *msg ){
56 SteamNetAuthenticationStatus_t *info = (void *)msg->m_pubParam;
57 vg_info( " Authentication availibility: %s\n",
58 string_ESteamNetworkingAvailability(info->m_eAvail) );
59 vg_info( " %s\n", info->m_debugMsg );
60 }
61
62 static void on_connect_status( CallbackMsg_t *msg ){
63 SteamNetConnectionStatusChangedCallback_t *info = (void *)msg->m_pubParam;
64 vg_info( " Connection status changed for %lu\n", info->m_hConn );
65
66 vg_info( " %s -> %s\n",
67 string_ESteamNetworkingConnectionState(info->m_eOldState),
68 string_ESteamNetworkingConnectionState(info->m_info.m_eState) );
69
70 if( info->m_info.m_eState==k_ESteamNetworkingConnectionState_Connecting ){
71 new_client_connecting( info->m_hConn );
72 }
73 }
74
75 static void on_inet_auth( SteamNetworkingMessage_t *msg ){
76 if( gameserver.auth_mode != eServerModeAuthentication ){
77 vg_error( "Running server without authentication. "
78 "Connection %u tried to authenticate.\n", msg->m_conn );
79 return;
80 }
81
82 if( get_connection_authsteamid( msg ) != k_connection_unauthorized ){
83 vg_warn( "Already authorized this user but app ticket was sent"
84 " again (%u)\n", msg->m_conn );
85 return;
86 }
87
88 vg_low( "Attempting to verify user\n" );
89
90 if( msg->m_cbSize < sizeof(netmsg_auth) ){
91 vg_error( "Malformed auth ticket, too small (%u)\n", msg->m_conn );
92 return;
93 }
94
95 netmsg_auth *auth = msg->m_pData;
96
97 if( msg->m_cbSize < sizeof(netmsg_auth)+auth->ticket_length ||
98 auth->ticket_length > 1024 ){
99 vg_error( "Malformed auth ticket, ticket_length incorrect (%u)\n",
100 auth->ticket_length );
101 return;
102 }
103
104 u8 decrypted[1024];
105 u32 ticket_len = 1024;
106
107 int success = SteamEncryptedAppTicket_BDecryptTicket(
108 auth->ticket, auth->ticket_length, decrypted,
109 &ticket_len, gameserver.app_symmetric_key,
110 k_nSteamEncryptedAppTicketSymmetricKeyLen );
111
112 if( !success ){
113 vg_error( "Failed to decrypt users ticket (client %u)\n", msg->m_conn );
114 vg_error( " ticket length: %u\n", auth->ticket_length );
115
116 SteamAPI_ISteamNetworkingSockets_CloseConnection(
117 hSteamNetworkingSockets,
118 msg->m_conn, 0, NULL, 1 );
119 return;
120 }
121
122 if( SteamEncryptedAppTicket_GetTicketIssueTime( decrypted, ticket_len )){
123 RTime32 ctime = time(NULL),
124 tickettime = SteamEncryptedAppTicket_GetTicketIssueTime(
125 decrypted, ticket_len ),
126 expiretime = tickettime + 24*3*60*60;
127
128 if( ctime > expiretime ){
129 vg_error( "Ticket expired (client %u)\n", msg->m_conn );
130
131 /* TODO: Send expired information */
132 SteamAPI_ISteamNetworkingSockets_CloseConnection(
133 hSteamNetworkingSockets,
134 msg->m_conn, 0, NULL, 1 );
135 return;
136 }
137 }
138
139 CSteamID steamid;
140 SteamEncryptedAppTicket_GetTicketSteamID( decrypted, ticket_len, &steamid );
141 vg_success( "User is authenticated! steamid %lu (%u)\n",
142 steamid.m_unAll64Bits, msg->m_conn );
143
144 set_connection_authsteamid( msg->m_conn, steamid.m_unAll64Bits );
145 }
146
147 static int inet_require_auth( SteamNetworkingMessage_t *msg ){
148 if( gameserver.auth_mode == eServerModeNoAuthentication )
149 return 1;
150
151 if( get_connection_authsteamid( msg ) == k_connection_unauthorized ){
152 vg_warn( "Unauthorized request! Disconnecting client: %u\n",
153 msg->m_conn );
154
155 SteamAPI_ISteamNetworkingSockets_CloseConnection(
156 hSteamNetworkingSockets,
157 msg->m_conn, 0, NULL, 1 );
158
159 return 0;
160 }
161 else return 1;
162 }
163
164 static void on_inet_score_request( SteamNetworkingMessage_t *msg ){
165 if( !inet_require_auth(msg) ) return;
166
167 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
168 hSteamNetworkingSockets, msg->m_conn,
169 &scoreboard_client_data, sizeof(netmsg_scoreboard),
170 k_nSteamNetworkingSend_Reliable, NULL );
171 }
172
173 static void on_inet_set_nickname( SteamNetworkingMessage_t *msg ){
174 if(!inet_require_auth(msg)) return;
175
176 u64_steamid steamid = get_connection_authsteamid(msg);
177 netmsg_set_nickname *setnick = msg->m_pData;
178 if( msg->m_cbSize < sizeof(netmsg_set_nickname) ){
179 vg_warn( "Invalid nickname request from client: %u, steamid: %lu\n",
180 msg->m_conn, steamid );
181 return;
182 }
183
184 highscore_set_user_nickname( steamid, setnick->nickname );
185 }
186
187 static void on_inet_set_score( SteamNetworkingMessage_t *msg ){
188 if(!inet_require_auth(msg)) return;
189
190 u64_steamid steamid = get_connection_authsteamid(msg);
191
192 if( msg->m_cbSize < sizeof(netmsg_set_score) ){
193 vg_warn( "Invalid set score post from client: %u, steamid: %lu\n",
194 msg->m_conn, steamid );
195 return;
196 }
197
198 netmsg_set_score *info = msg->m_pData;
199
200 if( msg->m_cbSize < sizeof(netmsg_set_score) +
201 sizeof(struct netmsg_score_record)*info->record_count ){
202 vg_warn( "Malformed set score post from client: %u, steamid: %lu\n",
203 msg->m_conn, steamid );
204 return;
205 }
206
207 for( int i=0; i<info->record_count; i++ ){
208 highscore_record temp;
209 temp.trackid = info->records[i].trackid;
210 temp.datetime = time(NULL);
211 temp.playerid = steamid;
212 temp.points = info->records[i].points;
213 temp.time = info->records[i].time;
214
215 highscores_push_record( &temp );
216 }
217 }
218
219 static void on_inet_playerframe( SteamNetworkingMessage_t *msg ){
220 if( msg->m_cbSize < sizeof(netmsg_playerframe) ){
221 return;
222 }
223
224
225 netmsg_playerframe *info = msg->m_pData;
226 vg_info( "... @: %.2f %.2f %.2f\n",
227 //msg->m_identityPeer,
228 info->pos_temp[0], info->pos_temp[1], info->pos_temp[2] );
229 }
230
231 static void poll_connections(void){
232 SteamNetworkingMessage_t *messages[32];
233 int len;
234
235 while(1){
236 len = SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
237 hSteamNetworkingSockets,
238 gameserver.client_group, messages, vg_list_size(messages) );
239
240 if( len <= 0 )
241 return;
242
243 for( int i=0; i<len; i++ ){
244 SteamNetworkingMessage_t *msg = messages[i];
245
246 if( msg->m_cbSize < sizeof(netmsg_blank) ){
247 vg_warn( "Discarding message (too small: %d)\n",
248 msg->m_cbSize );
249 continue;
250 }
251
252 netmsg_blank *tmp = msg->m_pData;
253
254 if( tmp->inetmsg_id == k_inetmsg_auth )
255 on_inet_auth( msg );
256 else if( tmp->inetmsg_id == k_inetmsg_scores_request )
257 on_inet_score_request( msg );
258 else if( tmp->inetmsg_id == k_inetmsg_set_nickname )
259 on_inet_set_nickname( msg );
260 else if( tmp->inetmsg_id == k_inetmsg_set_score )
261 on_inet_set_score( msg );
262 else if( tmp->inetmsg_id == k_inetmsg_playerframe )
263 on_inet_playerframe( msg );
264 else {
265 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
266 tmp->inetmsg_id );
267 }
268
269 SteamAPI_SteamNetworkingMessage_t_Release( msg );
270 }
271 }
272 }
273
274 static u64 seconds_to_server_ticks( double s ){
275 return s / 0.01;
276 }
277
278 static void generate_boards(void){
279 FILE *fp = fopen( "www/html/srhighscores.txt", "w" );
280
281 if( !fp ){
282 vg_error( "Can't write boards to www/html/srhighscores.txt\n" );
283 return;
284 }
285
286 for( int i=0; i<vg_list_size(track_infos); i++ ){
287 struct netmsg_board *board = &scoreboard_client_data.boards[i];
288
289 highscores_board_generate( board->data, i, 10 );
290 highscores_board_printf( fp, board->data, 10 );
291 }
292
293 fclose( fp );
294 }
295
296 int main( int argc, char *argv[] ){
297 signal( SIGINT, inthandler );
298 signal( SIGQUIT, inthandler );
299 signal( SIGPIPE, SIG_IGN );
300
301 char *arg;
302 while( vg_argp( argc, argv ) ){
303 if( vg_long_opt( "noauth" ) )
304 gameserver.auth_mode = eServerModeNoAuthentication;
305 }
306
307 /* TODO: Options to override, ammend, remove etc */
308
309 vg_set_mem_quota( 80*1024*1024 );
310 vg_alloc_quota();
311
312 highscores_init( 250000, 10000 );
313
314 if( !highscores_read() )
315 highscores_create_db();
316
317 steamworks_ensure_txt( "2103940" );
318
319 if( gameserver.auth_mode == eServerModeAuthentication ){
320 if( !vg_load_steam_symetric_key( "application_key",
321 gameserver.app_symmetric_key )){
322 return 0;
323 }
324 }
325 else{
326 vg_warn( "Running without user authentication.\n" );
327 }
328
329 if( !SteamGameServer_Init( 0, 27400, 27401,
330 gameserver.auth_mode, "1.0.0.0" ) ){
331 vg_error( "SteamGameServer_Init failed\n" );
332 return 0;
333 }
334
335 void *hSteamGameServer = SteamAPI_SteamGameServer();
336 SteamAPI_ISteamGameServer_LogOnAnonymous( hSteamGameServer );
337
338 SteamAPI_ManualDispatch_Init();
339 HSteamPipe hsteampipe = SteamGameServer_GetHSteamPipe();
340
341 //hSteamHTTP = SteamAPI_SteamGameServerHTTP();
342 hSteamNetworkingSockets =
343 SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
344
345 /*
346 * Server code
347 */
348
349 steam_register_callback( k_iSteamNetAuthenticationStatus, on_auth_status );
350 steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack,
351 on_connect_status );
352
353 vg_success( "Steamworks API running\n" );
354 steamworks_event_loop( hsteampipe );
355
356 /*
357 * Create a listener
358 */
359
360 HSteamListenSocket listener;
361 SteamNetworkingIPAddr localAddr;
362 SteamAPI_SteamNetworkingIPAddr_Clear( &localAddr );
363 localAddr.m_port = 27402;
364
365 listener = SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
366 hSteamNetworkingSockets, &localAddr, 0, NULL );
367 gameserver.client_group = SteamAPI_ISteamNetworkingSockets_CreatePollGroup(
368 hSteamNetworkingSockets );
369
370 u64 server_ticks = 8000,
371 last_record_save = 8000,
372 last_scoreboard_gen = 0,
373 last_monitor_heartbeat = 0;
374
375 generate_boards();
376 monitor_start_server();
377
378 while( !sig_stop ){
379 monitor_event_loop();
380 steamworks_event_loop( hsteampipe );
381 poll_connections();
382
383 usleep(10000);
384 server_ticks ++;
385
386 if( server_ticks >
387 (last_monitor_heartbeat + seconds_to_server_ticks(10.0))){
388 last_monitor_heartbeat = server_ticks;
389 monitor_heartbeat();
390 }
391
392 if( server_ticks > last_scoreboard_gen + seconds_to_server_ticks(60.0) ){
393 last_scoreboard_gen = server_ticks;
394 generate_boards();
395 }
396
397 if( server_ticks > last_record_save + seconds_to_server_ticks(10.0*60.0)){
398 last_record_save = server_ticks;
399 highscores_serialize_all();
400 }
401 }
402
403 highscores_serialize_all();
404
405 SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets,
406 gameserver.client_group );
407 SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
408 hSteamNetworkingSockets, listener );
409
410 vg_info( "Shutting down\n..." );
411 SteamGameServer_Shutdown();
412
413 return 0;
414 }