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