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