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