1 // Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved
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/ ...
9 #define _DEFAULT_SOURCE
13 volatile sig_atomic_t sig_stop
;
15 void inthandler( int signum
)
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"
30 *hSteamNetworkingSockets
;
32 u8 steam_symetric_key
[ k_nSteamEncryptedAppTicketSymmetricKeyLen
];
33 HSteamNetPollGroup client_pollgroup
;
35 static void recieve_http( void *callresult
, void *context
)
37 HTTPRequestCompleted_t
*result
= callresult
;
39 HTTPRequestHandle request
= result
->m_hRequest
;
42 SteamAPI_ISteamHTTP_GetHTTPResponseBodySize( hSteamHTTP
, request
, &size
);
44 u8
*buffer
= malloc( size
);
45 SteamAPI_ISteamHTTP_GetHTTPResponseBodyData(
46 hSteamHTTP
, request
, buffer
, size
);
48 buffer
[size
-1] = '\0';
49 vg_info( "%s\n", (char *)buffer
);
52 SteamAPI_ISteamHTTP_ReleaseHTTPRequest( hSteamHTTP
, result
->m_hRequest
);
55 static u64_steamid
get_connection_authsteamid( SteamNetworkingMessage_t
*msg
)
57 i64 userdata
= SteamAPI_ISteamNetworkingSockets_GetConnectionUserData(
58 hSteamNetworkingSockets
, msg
->m_conn
);
60 return *((u64_steamid
*)&userdata
);
63 static void set_connection_authsteamid(HSteamNetConnection con
, u64_steamid id
)
65 i64 userdata
= *((i64
*)&id
);
67 SteamAPI_ISteamNetworkingSockets_SetConnectionUserData(
68 hSteamNetworkingSockets
, con
, userdata
);
71 static void new_client_connecting( HSteamNetConnection client
)
73 EResult accept_status
= SteamAPI_ISteamNetworkingSockets_AcceptConnection(
74 hSteamNetworkingSockets
, client
);
76 if( accept_status
== k_EResultOK
)
78 vg_success( "Accepted client (id: %u)\n", client
);
79 SteamAPI_ISteamNetworkingSockets_SetConnectionPollGroup(
80 hSteamNetworkingSockets
,
81 client
, client_pollgroup
);
83 set_connection_authsteamid( client
, 0 );
87 vg_warn( "Error accepting client (id: %u)\n", client
);
91 static void on_auth_status( CallbackMsg_t
*msg
)
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
);
99 static void on_connect_status( CallbackMsg_t
*msg
)
101 SteamNetConnectionStatusChangedCallback_t
*info
= (void *)msg
->m_pubParam
;
102 vg_info( " Connection status changed for %lu\n", info
->m_hConn
);
104 vg_info( " %s -> %s\n",
105 string_ESteamNetworkingConnectionState(info
->m_info
.m_eState
),
106 string_ESteamNetworkingConnectionState(info
->m_eOldState
) );
108 if( info
->m_info
.m_eState
==k_ESteamNetworkingConnectionState_Connecting
)
110 new_client_connecting( info
->m_hConn
);
114 static void on_inet_auth( SteamNetworkingMessage_t
*msg
)
116 if( get_connection_authsteamid( msg
) )
118 vg_warn( "Already authorized this user but app ticket was sent"
119 " again (%u)\n", msg
->m_conn
);
123 vg_log( "Attempting to verify user\n" );
125 if( msg
->m_cbSize
< sizeof(netmsg_auth
) )
127 vg_error( "Malformed auth ticket, too small (%u)\n", msg
->m_conn
);
131 netmsg_auth
*auth
= msg
->m_pData
;
133 if( msg
->m_cbSize
< sizeof(netmsg_auth
)+auth
->ticket_length
||
134 auth
->ticket_length
> 1024 )
136 vg_error( "Malformed auth ticket, ticket_length incorrect (%u)\n",
137 auth
->ticket_length
);
144 int success
= SteamEncryptedAppTicket_BDecryptTicket(
145 auth
->ticket
, auth
->ticket_length
, decrypted
,
146 &ticket_len
, steam_symetric_key
,
147 k_nSteamEncryptedAppTicketSymmetricKeyLen
);
151 vg_error( "Failed to decrypt users ticket (client %u)\n", msg
->m_conn
);
153 SteamAPI_ISteamNetworkingSockets_CloseConnection(
154 hSteamNetworkingSockets
,
155 msg
->m_conn
, 0, NULL
, 0 );
159 if( SteamEncryptedAppTicket_GetTicketIssueTime( decrypted
, ticket_len
))
161 RTime32 ctime
= time(NULL
),
162 tickettime
= SteamEncryptedAppTicket_GetTicketIssueTime(
163 decrypted
, ticket_len
),
164 expiretime
= tickettime
+ 24*3*60*60;
166 if( ctime
> expiretime
)
168 vg_error( "Ticket expired (client %u)\n", msg
->m_conn
);
170 /* TODO: Send expired information */
171 SteamAPI_ISteamNetworkingSockets_CloseConnection(
172 hSteamNetworkingSockets
,
173 msg
->m_conn
, 0, NULL
, 0 );
179 SteamEncryptedAppTicket_GetTicketSteamID( decrypted
, ticket_len
, &steamid
);
180 vg_success( "User is authenticated! steamid %lu (%u)\n",
181 steamid
.m_unAll64Bits
, msg
->m_conn
);
183 set_connection_authsteamid( msg
->m_conn
, steamid
.m_unAll64Bits
);
186 static int inet_require_auth( SteamNetworkingMessage_t
*msg
)
188 if( !get_connection_authsteamid( msg
) )
190 vg_warn( "Unauthorized request! Disconnecting client: %u\n",
193 SteamAPI_ISteamNetworkingSockets_CloseConnection(
194 hSteamNetworkingSockets
,
195 msg
->m_conn
, 0, NULL
, 0 );
202 static void on_inet_score_request( SteamNetworkingMessage_t
*msg
)
205 if( get_connection_authsteamid( msg
) )
207 vg_log( "Recieved score request, sending records. (id: %u)\n",
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
);
215 return_info
->inetmsg_id
= k_inetmsg_scores_info
;
216 return_info
->record_count
= 0;
218 SteamAPI_ISteamNetworkingSockets_SendMessageToConnection(
219 hSteamNetworkingSockets
, msg
->m_conn
,
220 return_info
, data_size
,
221 k_nSteamNetworkingSend_Reliable
, NULL
);
225 vg_warn( "Unauthorized request! Disconnecting client: %u\n",
228 SteamAPI_ISteamNetworkingSockets_CloseConnection(
229 hSteamNetworkingSockets
,
230 msg
->m_conn
, 0, NULL
, 0 );
235 static void on_inet_set_nickname( SteamNetworkingMessage_t
*msg
)
237 if(!inet_require_auth(msg
)) return;
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
) )
243 vg_warn( "Invalid nickname request from client: %u, steamid: %lu\n",
244 msg
->m_conn
, steamid
);
248 highscore_set_user_nickname( steamid
, setnick
->nickname
);
251 static void on_inet_set_score( SteamNetworkingMessage_t
*msg
)
253 if(!inet_require_auth(msg
)) return;
255 u64_steamid steamid
= get_connection_authsteamid(msg
);
257 if( msg
->m_cbSize
< sizeof(netmsg_set_score
) )
259 vg_warn( "Invalid set score post from client: %u, steamid: %lu\n",
260 msg
->m_conn
, steamid
);
264 netmsg_set_score
*info
= msg
->m_pData
;
266 if( msg
->m_cbSize
< sizeof(netmsg_set_score
) +
267 sizeof(struct netmsg_score_record
)*info
->record_count
)
269 vg_warn( "Malformed set score post from client: %u, steamid: %lu\n",
270 msg
->m_conn
, steamid
);
274 for( int i
=0; i
<info
->record_count
; i
++ )
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
;
283 highscores_push_record( &temp
);
287 static void poll_connections(void)
289 SteamNetworkingMessage_t
*messages
[32];
294 len
= SteamAPI_ISteamNetworkingSockets_ReceiveMessagesOnPollGroup(
295 hSteamNetworkingSockets
,
296 client_pollgroup
, messages
, vg_list_size(messages
) );
301 for( int i
=0; i
<len
; i
++ )
303 SteamNetworkingMessage_t
*msg
= messages
[i
];
305 if( msg
->m_cbSize
< sizeof(netmsg_blank
) )
307 vg_warn( "Discarding message (too small: %d)\n",
312 netmsg_blank
*tmp
= msg
->m_pData
;
314 if( tmp
->inetmsg_id
== k_inetmsg_auth
)
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
);
321 SteamAPI_SteamNetworkingMessage_t_Release( msg
);
326 u64
seconds_to_server_ticks( double s
)
331 int main( int argc
, char *argv
[] )
333 signal( SIGINT
, inthandler
);
334 signal( SIGQUIT
, inthandler
);
336 highscores_init( 250000, 10000 );
338 steamworks_ensure_txt( "2103940" );
339 if( !vg_load_steam_symetric_key( "application_key", steam_symetric_key
) )
342 if( !SteamGameServer_Init( 0, 27400, 27401, eServerModeAuthentication
,
345 vg_error( "SteamGameServer_Init failed\n" );
349 void *hSteamGameServer
= SteamAPI_SteamGameServer();
350 SteamAPI_ISteamGameServer_LogOnAnonymous( hSteamGameServer
);
352 SteamAPI_ManualDispatch_Init();
353 HSteamPipe hsteampipe
= SteamGameServer_GetHSteamPipe();
355 //hSteamHTTP = SteamAPI_SteamGameServerHTTP();
356 hSteamNetworkingSockets
=
357 SteamAPI_SteamGameServerNetworkingSockets_SteamAPI();
363 steam_register_callback( k_iSteamNetAuthenticationStatus
, on_auth_status
);
364 steam_register_callback( k_iSteamNetConnectionStatusChangedCallBack
,
367 vg_success( "Steamworks API running\n" );
368 steamworks_event_loop( hsteampipe
);
374 HSteamListenSocket listener
;
375 SteamNetworkingIPAddr localAddr
;
376 SteamAPI_SteamNetworkingIPAddr_Clear( &localAddr
);
377 localAddr
.m_port
= 27402;
379 listener
= SteamAPI_ISteamNetworkingSockets_CreateListenSocketIP(
380 hSteamNetworkingSockets
, &localAddr
, 0, NULL
);
381 client_pollgroup
= SteamAPI_ISteamNetworkingSockets_CreatePollGroup(
382 hSteamNetworkingSockets
);
385 HTTPRequestHandle test_req
= SteamAPI_ISteamHTTP_CreateHTTPRequest(
386 hSteamHTTP
, k_EHTTPMethodGET
,
387 "https://www.harrygodden.com/hello.txt" );
389 steam_async
*call1
= steam_new_async();
391 call1
->p_handler
= recieve_http
;
392 SteamAPI_ISteamHTTP_SendHTTPRequest( hSteamHTTP
, test_req
, &call1
->id
);
395 u64 server_ticks
= 8000,
396 last_record_save
= 8000;
400 steamworks_event_loop( hsteampipe
);
406 if(last_record_save
+server_ticks
> seconds_to_server_ticks( 10.0*60.0 ))
408 last_record_save
= server_ticks
;
409 highscores_serialize_all();
413 highscores_serialize_all();
416 SteamAPI_ISteamNetworkingSockets_DestroyPollGroup( hSteamNetworkingSockets
,
418 SteamAPI_ISteamNetworkingSockets_CloseListenSocket(
419 hSteamNetworkingSockets
, listener
);
421 vg_info( "Shutting down\n..." );
422 SteamGameServer_Shutdown();