2 * Copyright (C) 2021-2022 Mt.ZERO Software, Harry Godden - All Rights Reserved
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/ ...
11 #define _DEFAULT_SOURCE
16 volatile sig_atomic_t sig_stop
;
18 static void inthandler( int signum
) {
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"
32 #include "highscores.c"
34 static const u64 k_connection_unauthorized
= 0xffffffffffffffff;
36 static void *hSteamHTTP
,
37 *hSteamNetworkingSockets
;
39 static u8 steam_symetric_key
[ k_nSteamEncryptedAppTicketSymmetricKeyLen
];
40 static HSteamNetPollGroup client_pollgroup
;
43 static void recieve_http( void *callresult
, void *context
)
45 HTTPRequestCompleted_t
*result
= callresult
;
47 HTTPRequestHandle request
= result
->m_hRequest
;
50 SteamAPI_ISteamHTTP_GetHTTPResponseBodySize( hSteamHTTP
, request
, &size
);
52 u8
*buffer
= vg_alloc( size
);
53 SteamAPI_ISteamHTTP_GetHTTPResponseBodyData(
54 hSteamHTTP
, request
, buffer
, size
);
56 buffer
[size
-1] = '\0';
57 vg_info( "%s\n", (char *)buffer
);
60 SteamAPI_ISteamHTTP_ReleaseHTTPRequest( hSteamHTTP
, result
->m_hRequest
);
64 static u64_steamid
get_connection_authsteamid( SteamNetworkingMessage_t
*msg
)
66 i64 userdata
= SteamAPI_ISteamNetworkingSockets_GetConnectionUserData(
67 hSteamNetworkingSockets
, msg
->m_conn
);
69 return *((u64_steamid
*)&userdata
);
72 static void set_connection_authsteamid(HSteamNetConnection con
, u64_steamid id
)
74 i64 userdata
= *((i64
*)&id
);
76 SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
77 hSteamNetworkingSockets
, con
, userdata
);
80 static void new_client_connecting( HSteamNetConnection client
)
82 EResult accept_status
= SteamAPI_ISteamNetworkingSockets_AcceptConnection(
83 hSteamNetworkingSockets
, client
);
85 if( accept_status
== k_EResultOK
)
87 vg_success( "Accepted client (id: %u)\n", client
);
88 SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup(
89 hSteamNetworkingSockets
,
90 client
, client_pollgroup
);
93 set_connection_authsteamid( client
, -1 );
97 vg_warn( "Error accepting client (id: %u)\n", client
);
101 static void on_auth_status( CallbackMsg_t
*msg
)
103 SteamNetAuthenticationStatus_t
*info
= (void *)msg
->m_pubParam
;
104 vg_info( " Authentication availibility: %s\n",
105 string_ESteamNetworkingAvailability(info
->m_eAvail
) );
106 vg_info( " %s\n", info
->m_debugMsg
);
109 static void on_connect_status( CallbackMsg_t
*msg
)
111 SteamNetConnectionStatusChangedCallback_t
*info
= (void *)msg
->m_pubParam
;
112 vg_info( " Connection status changed for %lu\n", info
->m_hConn
);
114 vg_info( " %s -> %s\n",
115 string_ESteamNetworkingConnectionState(info
->m_eOldState
),
116 string_ESteamNetworkingConnectionState(info
->m_info
.m_eState
) );
118 if( info
->m_info
.m_eState
==k_ESteamNetworkingConnectionState_Connecting
)
120 new_client_connecting( info
->m_hConn
);
124 static void on_inet_auth( SteamNetworkingMessage_t
*msg
)
126 if( get_connection_authsteamid( msg
) != k_connection_unauthorized
)
128 vg_warn( "Already authorized this user but app ticket was sent"
129 " again (%u)\n", msg
->m_conn
);
133 vg_low( "Attempting to verify user\n" );
135 if( msg
->m_cbSize
< sizeof(netmsg_auth
) )
137 vg_error( "Malformed auth ticket, too small (%u)\n", msg
->m_conn
);
141 netmsg_auth
*auth
= msg
->m_pData
;
143 if( msg
->m_cbSize
< sizeof(netmsg_auth
)+auth
->ticket_length
||
144 auth
->ticket_length
> 1024 )
146 vg_error( "Malformed auth ticket, ticket_length incorrect (%u)\n",
147 auth
->ticket_length
);
152 u32 ticket_len
= 1024;
154 int success
= SteamEncryptedAppTicket_BDecryptTicket(
155 auth
->ticket
, auth
->ticket_length
, decrypted
,
156 &ticket_len
, steam_symetric_key
,
157 k_nSteamEncryptedAppTicketSymmetricKeyLen
);
161 vg_error( "Failed to decrypt users ticket (client %u)\n", msg
->m_conn
);
162 vg_error( " ticket length: %u\n", auth
->ticket_length
);
164 SteamAPI_ISteamNetworkingSockets_CloseConnection(
165 hSteamNetworkingSockets
,
166 msg
->m_conn
, 0, NULL
, 1 );
170 if( SteamEncryptedAppTicket_GetTicketIssueTime( decrypted
, ticket_len
))
172 RTime32 ctime
= time(NULL
),
173 tickettime
= SteamEncryptedAppTicket_GetTicketIssueTime(
174 decrypted
, ticket_len
),
175 expiretime
= tickettime
+ 24*3*60*60;
177 if( ctime
> expiretime
)
179 vg_error( "Ticket expired (client %u)\n", msg
->m_conn
);
181 /* TODO: Send expired information */
182 SteamAPI_ISteamNetworkingSockets_CloseConnection(
183 hSteamNetworkingSockets
,
184 msg
->m_conn
, 0, NULL
, 1 );
190 SteamEncryptedAppTicket_GetTicketSteamID( decrypted
, ticket_len
, &steamid
);
191 vg_success( "User is authenticated! steamid %lu (%u)\n",
192 steamid
.m_unAll64Bits
, msg
->m_conn
);
194 set_connection_authsteamid( msg
->m_conn
, steamid
.m_unAll64Bits
);
197 static int inet_require_auth( SteamNetworkingMessage_t
*msg
)
199 if( get_connection_authsteamid( msg
) == k_connection_unauthorized
)
201 vg_warn( "Unauthorized request! Disconnecting client: %u\n",
204 SteamAPI_ISteamNetworkingSockets_CloseConnection(
205 hSteamNetworkingSockets
,
206 msg
->m_conn
, 0, NULL
, 1 );
213 static void on_inet_score_request( SteamNetworkingMessage_t
*msg
)
215 if( !inet_require_auth(msg
) ) return;
217 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
218 hSteamNetworkingSockets
, msg
->m_conn
,
219 &scoreboard_client_data
, sizeof(netmsg_scoreboard
),
220 k_nSteamNetworkingSend_Reliable
, NULL
);
223 static void on_inet_set_nickname( SteamNetworkingMessage_t
*msg
)
225 if(!inet_require_auth(msg
)) return;
227 u64_steamid steamid
= get_connection_authsteamid(msg
);
228 netmsg_set_nickname
*setnick
= msg
->m_pData
;
229 if( msg
->m_cbSize
< sizeof(netmsg_set_nickname
) )
231 vg_warn( "Invalid nickname request from client: %u, steamid: %lu\n",
232 msg
->m_conn
, steamid
);
236 highscore_set_user_nickname( steamid
, setnick
->nickname
);
239 static void on_inet_set_score( SteamNetworkingMessage_t
*msg
)
241 if(!inet_require_auth(msg
)) return;
243 u64_steamid steamid
= get_connection_authsteamid(msg
);
245 if( msg
->m_cbSize
< sizeof(netmsg_set_score
) )
247 vg_warn( "Invalid set score post from client: %u, steamid: %lu\n",
248 msg
->m_conn
, steamid
);
252 netmsg_set_score
*info
= msg
->m_pData
;
254 if( msg
->m_cbSize
< sizeof(netmsg_set_score
) +
255 sizeof(struct netmsg_score_record
)*info
->record_count
)
257 vg_warn( "Malformed set score post from client: %u, steamid: %lu\n",
258 msg
->m_conn
, steamid
);
262 for( int i
=0; i
<info
->record_count
; i
++ )
264 highscore_record temp
;
265 temp
.trackid
= info
->records
[i
].trackid
;
266 temp
.datetime
= time(NULL
);
267 temp
.playerid
= steamid
;
268 temp
.points
= info
->records
[i
].points
;
269 temp
.time
= info
->records
[i
].time
;
271 highscores_push_record( &temp
);
275 static void poll_connections(void)
277 SteamNetworkingMessage_t
*messages
[32];
281 len
= SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
282 hSteamNetworkingSockets
,
283 client_pollgroup
, messages
, vg_list_size(messages
) );
288 for( int i
=0; i
<len
; i
++ ){
289 SteamNetworkingMessage_t
*msg
= messages
[i
];
291 if( msg
->m_cbSize
< sizeof(netmsg_blank
) ){
292 vg_warn( "Discarding message (too small: %d)\n",
297 netmsg_blank
*tmp
= msg
->m_pData
;
299 if( tmp
->inetmsg_id
== k_inetmsg_auth
)
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
);
308 vg_warn( "Unknown inetmsg_id recieved from client. (%u)\n",
312 SteamAPI_SteamNetworkingMessage_t_Release( msg
);
317 static u64
seconds_to_server_ticks( double s
)
322 static void generate_boards(void)
324 FILE *fp
= fopen( "www/html/srhighscores.txt", "w" );
326 for( int i
=0; i
<vg_list_size(track_infos
); i
++ ){
327 struct netmsg_board
*board
= &scoreboard_client_data
.boards
[i
];
329 highscores_board_generate( board
->data
, i
, 10 );
330 highscores_board_printf( fp
, board
->data
, 10 );
336 int main( int argc
, char *argv
[] )
338 signal( SIGINT
, inthandler
);
339 signal( SIGQUIT
, inthandler
);
341 /* TODO: Options to override, ammend, remove etc */
343 vg_set_mem_quota( 80*1024*1024 );
346 highscores_init( 250000, 10000 );
348 if( !highscores_read() )
349 highscores_create_db();
351 steamworks_ensure_txt( "2103940" );
352 if( !vg_load_steam_symetric_key( "application_key", steam_symetric_key
) )
355 if( !SteamGameServer_Init( 0, 27400, 27401, eServerModeAuthentication
,
358 vg_error( "SteamGameServer_Init failed\n" );
362 void *hSteamGameServer
= SteamAPI_SteamGameServer();
363 SteamAPI_ISteamGameServer_LogOnAnonymous( hSteamGameServer
);
365 SteamAPI_ManualDispatch_Init();
366 HSteamPipe hsteampipe
= SteamGameServer_GetHSteamPipe();
368 //hSteamHTTP = SteamAPI_SteamGameServerHTTP();
369 hSteamNetworkingSockets
=
370 SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
376 steam_register_callback( k_iSteamNetAuthenticationStatus
, on_auth_status
);
377 steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack
,
380 vg_success( "Steamworks API running\n" );
381 steamworks_event_loop( hsteampipe
);
387 HSteamListenSocket listener
;
388 SteamNetworkingIPAddr localAddr
;
389 SteamAPI_SteamNetworkingIPAddr_Clear( &localAddr
);
390 localAddr
.m_port
= 27402;
392 listener
= SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
393 hSteamNetworkingSockets
, &localAddr
, 0, NULL
);
394 client_pollgroup
= SteamAPI_ISteamNetworkingSockets_CreatePollGroup(
395 hSteamNetworkingSockets
);
398 HTTPRequestHandle test_req
= SteamAPI_ISteamHTTP_CreateHTTPRequest(
399 hSteamHTTP
, k_EHTTPMethodGET
,
400 "https://www.harrygodden.com/hello.txt" );
402 vg_steam_async_call
*call1
= vg_alloc_async_steam_api_call();
403 call1
->userdata
= NULL
;
404 call1
->p_handler
= recieve_http
;
405 SteamAPI_ISteamHTTP_SendHTTPRequest( hSteamHTTP
, test_req
, &call1
->id
);
408 u64 server_ticks
= 8000,
409 last_record_save
= 8000,
410 last_scoreboard_gen
= 0;
416 steamworks_event_loop( hsteampipe
);
422 if(server_ticks
> last_scoreboard_gen
+ seconds_to_server_ticks(1.0*60.0))
424 last_scoreboard_gen
= server_ticks
;
428 if(server_ticks
> last_record_save
+ seconds_to_server_ticks( 10.0*60.0 ))
430 last_record_save
= server_ticks
;
431 highscores_serialize_all();
435 highscores_serialize_all();
437 SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets
,
439 SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
440 hSteamNetworkingSockets
, listener
);
442 vg_info( "Shutting down\n..." );
443 SteamGameServer_Shutdown();