2023-09-07 11:17:05 +02:00
//=============================================================================//
2022-02-19 02:31:16 +01:00
//
2024-01-01 16:46:16 +01:00
// Purpose: Implementation of the pylon client.
2022-02-19 02:31:16 +01:00
//
// $NoKeywords: $
2023-09-07 11:17:05 +02:00
//=============================================================================//
2022-02-19 02:31:16 +01:00
# include <core/stdafx.h>
2022-04-09 16:16:40 +02:00
# include <tier1/cvar.h>
2023-01-26 20:06:48 +01:00
# include <tier2/curlutils.h>
2022-02-19 02:31:16 +01:00
# include <networksystem/pylon.h>
2022-08-28 17:40:03 +02:00
# include <engine/server/server.h>
2022-02-19 02:31:16 +01:00
2024-02-24 02:15:09 +01:00
//-----------------------------------------------------------------------------
// Console variables
//-----------------------------------------------------------------------------
2024-02-25 23:56:49 +01:00
ConVar pylon_matchmaking_hostname ( " pylon_matchmaking_hostname " , " ms.r5reloaded.com " , FCVAR_RELEASE | FCVAR_ACCESSIBLE_FROM_THREADS , " Holds the pylon matchmaking hostname " ) ;
ConVar pylon_host_update_interval ( " pylon_host_update_interval " , " 5 " , FCVAR_RELEASE | FCVAR_ACCESSIBLE_FROM_THREADS , " Length of time in seconds between each status update interval to master server " , true , 5.f , false , 0.f ) ;
ConVar pylon_showdebuginfo ( " pylon_showdebuginfo " , " 0 " , FCVAR_RELEASE | FCVAR_ACCESSIBLE_FROM_THREADS , " Shows debug output for pylon " ) ;
2024-02-24 02:15:09 +01:00
2023-09-07 11:17:05 +02:00
//-----------------------------------------------------------------------------
// Purpose: checks if the server listing fields are valid.
// Input : &value -
// Output : true on success, false on failure.
//-----------------------------------------------------------------------------
static bool IsServerListingValid ( const rapidjson : : Value & value )
{
if ( value . HasMember ( " name " ) & & value [ " name " ] . IsString ( ) & &
value . HasMember ( " description " ) & & value [ " description " ] . IsString ( ) & &
2023-10-15 10:47:19 +02:00
value . HasMember ( " hidden " ) & & value [ " hidden " ] . IsBool ( ) & &
2023-09-07 11:17:05 +02:00
value . HasMember ( " map " ) & & value [ " map " ] . IsString ( ) & &
value . HasMember ( " playlist " ) & & value [ " playlist " ] . IsString ( ) & &
value . HasMember ( " ip " ) & & value [ " ip " ] . IsString ( ) & &
2023-10-15 10:47:19 +02:00
value . HasMember ( " port " ) & & value [ " port " ] . IsInt ( ) & &
2023-09-07 11:17:05 +02:00
value . HasMember ( " key " ) & & value [ " key " ] . IsString ( ) & &
2023-10-15 10:47:19 +02:00
value . HasMember ( " checksum " ) & & value [ " checksum " ] . IsUint ( ) & &
2024-03-16 11:55:20 +01:00
value . HasMember ( " numPlayers " ) & & value [ " numPlayers " ] . IsInt ( ) & &
2023-10-15 10:47:19 +02:00
value . HasMember ( " maxPlayers " ) & & value [ " maxPlayers " ] . IsInt ( ) )
2023-09-07 11:17:05 +02:00
{
return true ;
}
return false ;
}
2022-07-01 10:29:27 +02:00
//-----------------------------------------------------------------------------
2023-04-24 01:55:37 +02:00
// Purpose: gets a vector of hosted servers.
// Input : &outMessage -
2024-03-10 01:57:04 +01:00
// Output : true on success, false on failure.
2022-07-01 10:29:27 +02:00
//-----------------------------------------------------------------------------
2024-03-10 01:57:04 +01:00
bool CPylon : : GetServerList ( vector < NetGameServer_t > & outServerList , string & outMessage ) const
2022-07-01 10:29:27 +02:00
{
2023-09-07 11:17:05 +02:00
rapidjson : : Document requestJson ;
requestJson . SetObject ( ) ;
requestJson . AddMember ( " version " , SDK_VERSION , requestJson . GetAllocator ( ) ) ;
2022-07-01 10:29:27 +02:00
2023-09-07 11:17:05 +02:00
rapidjson : : StringBuffer stringBuffer ;
JSON_DocumentToBufferDeserialize ( requestJson , stringBuffer ) ;
rapidjson : : Document responseJson ;
2023-01-26 20:06:48 +01:00
CURLINFO status ;
2023-04-24 00:32:27 +02:00
2023-04-25 22:51:06 +02:00
if ( ! SendRequest ( " /servers " , requestJson , responseJson ,
2023-04-25 22:57:06 +02:00
outMessage , status , " server list error " ) )
2022-07-01 10:29:27 +02:00
{
2024-03-10 01:57:04 +01:00
return false ;
2022-07-01 10:29:27 +02:00
}
2023-09-07 11:17:05 +02:00
if ( ! responseJson . HasMember ( " servers " ) )
2022-07-01 10:29:27 +02:00
{
2023-04-25 22:51:06 +02:00
outMessage = Format ( " Invalid response with status: %d " , int ( status ) ) ;
2024-03-10 01:57:04 +01:00
return false ;
2022-08-28 17:12:58 +02:00
}
2023-04-25 00:45:39 +02:00
2023-09-07 11:17:05 +02:00
const rapidjson : : Value & servers = responseJson [ " servers " ] ;
for ( rapidjson : : Value : : ConstValueIterator itr = servers . Begin ( ) ;
itr ! = servers . End ( ) ; + + itr )
2022-08-28 17:12:58 +02:00
{
2023-09-07 11:17:05 +02:00
const rapidjson : : Value & obj = * itr ;
if ( ! IsServerListingValid ( obj ) )
2023-04-29 14:58:46 +02:00
{
2023-09-07 11:17:05 +02:00
// Missing details; skip this server listing.
continue ;
2023-04-29 14:58:46 +02:00
}
2023-09-07 11:17:05 +02:00
2024-03-10 01:57:04 +01:00
outServerList . push_back (
2023-09-07 11:17:05 +02:00
NetGameServer_t
{
obj [ " name " ] . GetString ( ) ,
obj [ " description " ] . GetString ( ) ,
2023-10-15 10:47:19 +02:00
obj [ " hidden " ] . GetBool ( ) ,
2023-09-07 11:17:05 +02:00
obj [ " map " ] . GetString ( ) ,
obj [ " playlist " ] . GetString ( ) ,
obj [ " ip " ] . GetString ( ) ,
2023-10-15 10:47:19 +02:00
obj [ " port " ] . GetInt ( ) ,
2023-09-07 11:17:05 +02:00
obj [ " key " ] . GetString ( ) ,
2023-10-15 10:47:19 +02:00
obj [ " checksum " ] . GetUint ( ) ,
2023-09-07 11:17:05 +02:00
SDK_VERSION ,
2024-03-16 11:55:20 +01:00
obj [ " numPlayers " ] . GetInt ( ) ,
2023-10-15 10:47:19 +02:00
obj [ " maxPlayers " ] . GetInt ( ) ,
2023-09-07 11:17:05 +02:00
- 1 ,
}
) ;
2022-07-01 10:29:27 +02:00
}
2024-03-10 01:57:04 +01:00
return true ;
2022-07-01 10:29:27 +02:00
}
//-----------------------------------------------------------------------------
2022-08-30 12:07:09 +02:00
// Purpose: Gets the server by token string.
2023-04-24 01:55:37 +02:00
// Input : &outGameServer -
// &outMessage -
// &token -
2022-07-01 10:29:27 +02:00
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
2023-04-24 01:55:37 +02:00
bool CPylon : : GetServerByToken ( NetGameServer_t & outGameServer ,
string & outMessage , const string & token ) const
2022-07-01 10:29:27 +02:00
{
2023-09-07 11:17:05 +02:00
rapidjson : : Document requestJson ;
requestJson . SetObject ( ) ;
2023-01-26 21:11:32 +01:00
2023-09-07 11:17:05 +02:00
rapidjson : : Document : : AllocatorType & allocator = requestJson . GetAllocator ( ) ;
2024-04-05 16:26:18 +02:00
requestJson . AddMember ( " version " , rapidjson : : Value ( SDK_VERSION , requestJson . GetAllocator ( ) ) , allocator ) ;
2023-09-07 11:17:05 +02:00
requestJson . AddMember ( " token " , rapidjson : : Value ( token . c_str ( ) , requestJson . GetAllocator ( ) ) , allocator ) ;
rapidjson : : Document responseJson ;
2023-01-26 20:06:48 +01:00
CURLINFO status ;
2023-04-24 00:32:27 +02:00
2023-04-26 23:31:16 +02:00
if ( ! SendRequest ( " /server/byToken " , requestJson , responseJson ,
outMessage , status , " server not found " ) )
2023-01-26 20:06:48 +01:00
{
return false ;
}
2022-08-27 18:57:56 +02:00
2023-09-07 11:17:05 +02:00
if ( ! responseJson . HasMember ( " server " ) )
2022-07-01 10:29:27 +02:00
{
2023-04-26 23:31:16 +02:00
outMessage = Format ( " Invalid response with status: %d " , int ( status ) ) ;
2023-04-25 00:45:39 +02:00
return false ;
}
2023-02-04 19:18:18 +01:00
2023-09-07 11:17:05 +02:00
const rapidjson : : Value & serverJson = responseJson [ " server " ] ;
2022-07-01 10:29:27 +02:00
2023-09-07 11:17:05 +02:00
if ( ! IsServerListingValid ( serverJson ) )
2022-08-28 17:12:58 +02:00
{
2023-09-07 11:17:05 +02:00
outMessage = Format ( " Invalid server listing data! " ) ;
return false ;
2023-04-29 14:58:46 +02:00
}
2022-07-01 10:29:27 +02:00
2023-09-07 11:17:05 +02:00
outGameServer = NetGameServer_t
{
serverJson [ " name " ] . GetString ( ) ,
serverJson [ " description " ] . GetString ( ) ,
2023-10-15 10:47:19 +02:00
serverJson [ " hidden " ] . GetBool ( ) ,
2023-09-07 11:17:05 +02:00
serverJson [ " map " ] . GetString ( ) ,
serverJson [ " playlist " ] . GetString ( ) ,
serverJson [ " ip " ] . GetString ( ) ,
2023-10-15 10:47:19 +02:00
serverJson [ " port " ] . GetInt ( ) ,
2023-09-07 11:17:05 +02:00
serverJson [ " key " ] . GetString ( ) ,
2023-10-15 10:47:19 +02:00
serverJson [ " checksum " ] . GetUint ( ) ,
2023-09-07 11:17:05 +02:00
SDK_VERSION ,
2024-03-16 11:55:20 +01:00
serverJson [ " numPlayers " ] . GetInt ( ) ,
2023-10-15 10:47:19 +02:00
serverJson [ " maxPlayers " ] . GetInt ( ) ,
2023-09-07 11:17:05 +02:00
- 1 ,
} ;
return true ;
2022-07-01 10:29:27 +02:00
}
//-----------------------------------------------------------------------------
2022-08-30 12:07:09 +02:00
// Purpose: Sends host server POST request.
2023-04-24 01:55:37 +02:00
// Input : &outMessage -
// &outToken -
2023-01-26 20:06:48 +01:00
// &netGameServer -
2022-07-01 10:29:27 +02:00
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
2023-10-15 10:40:46 +02:00
bool CPylon : : PostServerHost ( string & outMessage , string & outToken , string & outHostIp , const NetGameServer_t & netGameServer ) const
2022-07-01 10:29:27 +02:00
{
2023-09-07 11:17:05 +02:00
rapidjson : : Document requestJson ;
requestJson . SetObject ( ) ;
rapidjson : : Document : : AllocatorType & allocator = requestJson . GetAllocator ( ) ;
2024-01-24 23:40:58 +01:00
requestJson . AddMember ( " name " , rapidjson : : Value ( netGameServer . name . c_str ( ) , allocator ) , allocator ) ;
requestJson . AddMember ( " description " , rapidjson : : Value ( netGameServer . description . c_str ( ) , allocator ) , allocator ) ;
requestJson . AddMember ( " hidden " , netGameServer . hidden , allocator ) ;
requestJson . AddMember ( " map " , rapidjson : : Value ( netGameServer . map . c_str ( ) , allocator ) , allocator ) ;
requestJson . AddMember ( " playlist " , rapidjson : : Value ( netGameServer . playlist . c_str ( ) , allocator ) , allocator ) ;
requestJson . AddMember ( " ip " , rapidjson : : Value ( netGameServer . address . c_str ( ) , allocator ) , allocator ) ;
requestJson . AddMember ( " port " , netGameServer . port , allocator ) ;
requestJson . AddMember ( " key " , rapidjson : : Value ( netGameServer . netKey . c_str ( ) , allocator ) , allocator ) ;
requestJson . AddMember ( " checksum " , netGameServer . checksum , allocator ) ;
requestJson . AddMember ( " version " , rapidjson : : Value ( netGameServer . versionId . c_str ( ) , allocator ) , allocator ) ;
2024-03-16 11:55:20 +01:00
requestJson . AddMember ( " numPlayers " , netGameServer . numPlayers , allocator ) ;
2024-01-24 23:40:58 +01:00
requestJson . AddMember ( " maxPlayers " , netGameServer . maxPlayers , allocator ) ;
requestJson . AddMember ( " timeStamp " , netGameServer . timeStamp , allocator ) ;
2023-09-07 11:17:05 +02:00
rapidjson : : Document responseJson ;
2023-01-26 20:06:48 +01:00
CURLINFO status ;
2022-07-01 10:29:27 +02:00
2023-09-07 11:17:05 +02:00
if ( ! SendRequest ( " /servers/add " , requestJson , responseJson , outMessage , status , " server host error " ) )
2022-07-01 10:29:27 +02:00
{
2023-04-24 01:55:37 +02:00
return false ;
2022-07-01 10:29:27 +02:00
}
2024-01-24 23:40:58 +01:00
if ( netGameServer . hidden )
2022-08-28 17:12:58 +02:00
{
2023-09-07 11:17:05 +02:00
if ( ! responseJson . HasMember ( " token " ) | | ! responseJson [ " token " ] . IsString ( ) )
2023-04-29 13:15:18 +02:00
{
outMessage = Format ( " Invalid response with status: %d " , int ( status ) ) ;
outToken . clear ( ) ;
return false ;
}
2023-09-07 11:17:05 +02:00
outToken = responseJson [ " token " ] . GetString ( ) ;
2022-07-01 10:29:27 +02:00
}
2023-10-15 10:40:46 +02:00
if ( responseJson . HasMember ( " ip " ) & & responseJson [ " ip " ] . IsString ( ) & &
responseJson . HasMember ( " port " ) & & responseJson [ " port " ] . IsInt ( ) )
{
outHostIp = Format ( " [%s]:%i " ,
responseJson [ " ip " ] . GetString ( ) , responseJson [ " port " ] . GetInt ( ) ) ;
}
2023-04-25 00:45:39 +02:00
return true ;
2022-07-01 10:29:27 +02:00
}
2023-04-28 23:47:58 +02:00
//-----------------------------------------------------------------------------
// Purpose: Checks a list of clients for their banned status.
// Input : &inBannedVec -
// &outBannedVec -
// Output : True on success, false otherwise.
//-----------------------------------------------------------------------------
2023-09-07 11:17:05 +02:00
bool CPylon : : GetBannedList ( const CBanSystem : : BannedList_t & inBannedVec , CBanSystem : : BannedList_t & outBannedVec ) const
2023-04-28 23:47:58 +02:00
{
2023-09-07 11:17:05 +02:00
rapidjson : : Document requestJson ;
2024-01-07 15:24:40 +01:00
requestJson . SetObject ( ) ;
rapidjson : : Value playersArray ( rapidjson : : kArrayType ) ;
2023-09-07 11:17:05 +02:00
rapidjson : : Document : : AllocatorType & allocator = requestJson . GetAllocator ( ) ;
2023-04-28 23:47:58 +02:00
2023-08-31 00:16:25 +02:00
FOR_EACH_VEC ( inBannedVec , i )
2023-04-28 23:47:58 +02:00
{
2023-08-31 00:16:25 +02:00
const CBanSystem : : Banned_t & banned = inBannedVec [ i ] ;
2023-09-07 11:17:05 +02:00
rapidjson : : Value player ( rapidjson : : kObjectType ) ;
player . AddMember ( " id " , banned . m_NucleusID , allocator ) ;
player . AddMember ( " ip " , rapidjson : : Value ( banned . m_Address . String ( ) , allocator ) , allocator ) ;
2024-01-07 15:24:40 +01:00
playersArray . PushBack ( player , allocator ) ;
2023-04-28 23:47:58 +02:00
}
2024-01-07 15:24:40 +01:00
requestJson . AddMember ( " players " , playersArray , allocator ) ;
2023-09-07 11:17:05 +02:00
rapidjson : : Document responseJson ;
2023-04-28 23:47:58 +02:00
string outMessage ;
CURLINFO status ;
2023-09-07 11:17:05 +02:00
if ( ! SendRequest ( " /banlist/bulkCheck " , requestJson , responseJson , outMessage , status , " banned bulk check error " ) )
2023-04-28 23:47:58 +02:00
{
return false ;
}
2023-09-07 11:17:05 +02:00
if ( ! responseJson . HasMember ( " bannedPlayers " ) | | ! responseJson [ " bannedPlayers " ] . IsArray ( ) )
2023-04-28 23:47:58 +02:00
{
outMessage = Format ( " Invalid response with status: %d " , int ( status ) ) ;
return false ;
}
2023-09-07 11:17:05 +02:00
const rapidjson : : Value & bannedPlayers = responseJson [ " bannedPlayers " ] ;
for ( const rapidjson : : Value & obj : bannedPlayers . GetArray ( ) )
2023-04-28 23:47:58 +02:00
{
2023-09-07 11:17:05 +02:00
CBanSystem : : Banned_t banned (
obj . HasMember ( " reason " ) ? obj [ " reason " ] . GetString ( ) : " #DISCONNECT_BANNED " ,
obj . HasMember ( " id " ) & & obj [ " id " ] . IsUint64 ( ) ? obj [ " id " ] . GetUint64 ( ) : NucleusID_t ( NULL )
) ;
outBannedVec . AddToTail ( banned ) ;
2023-04-28 23:47:58 +02:00
}
2023-09-07 11:17:05 +02:00
return true ;
2023-04-28 23:47:58 +02:00
}
2022-07-01 10:29:27 +02:00
//-----------------------------------------------------------------------------
// Purpose: Checks if client is banned on the comp server.
2023-04-24 01:55:37 +02:00
// Input : &ipAddress -
// nucleusId -
// &outReason - <- contains banned reason if any.
2023-04-28 23:47:58 +02:00
// Output : True if banned, false if not banned.
2022-07-01 10:29:27 +02:00
//-----------------------------------------------------------------------------
2023-09-07 11:17:05 +02:00
bool CPylon : : CheckForBan ( const string & ipAddress , const uint64_t nucleusId , const string & personaName , string & outReason ) const
2022-07-01 10:29:27 +02:00
{
2023-09-07 11:17:05 +02:00
rapidjson : : Document requestJson ;
requestJson . SetObject ( ) ;
2022-07-01 10:29:27 +02:00
2023-09-07 11:17:05 +02:00
rapidjson : : Document : : AllocatorType & allocator = requestJson . GetAllocator ( ) ;
requestJson . AddMember ( " name " , rapidjson : : Value ( personaName . c_str ( ) , allocator ) , allocator ) ;
requestJson . AddMember ( " id " , nucleusId , allocator ) ;
requestJson . AddMember ( " ip " , rapidjson : : Value ( ipAddress . c_str ( ) , allocator ) , allocator ) ;
rapidjson : : Document responseJson ;
2023-04-24 01:55:37 +02:00
string outMessage ;
2023-01-26 20:06:48 +01:00
CURLINFO status ;
2023-09-07 11:17:05 +02:00
if ( ! SendRequest ( " /banlist/isBanned " , requestJson , responseJson , outMessage , status , " banned check error " ) )
2023-01-26 20:06:48 +01:00
{
return false ;
}
2022-08-27 18:57:56 +02:00
2023-09-07 11:17:05 +02:00
if ( responseJson . HasMember ( " banned " ) & & responseJson [ " banned " ] . IsBool ( ) )
2023-04-25 00:45:39 +02:00
{
2023-09-07 11:17:05 +02:00
if ( responseJson [ " banned " ] . GetBool ( ) )
2023-04-29 14:58:46 +02:00
{
2023-09-07 11:17:05 +02:00
outReason = responseJson . HasMember ( " reason " ) ? responseJson [ " reason " ] . GetString ( ) : " #DISCONNECT_BANNED " ;
2023-04-29 14:58:46 +02:00
return true ;
}
}
2023-04-25 00:45:39 +02:00
return false ;
}
2023-10-20 19:30:07 +02:00
//-----------------------------------------------------------------------------
// Purpose: authenticate for 'this' particular connection.
// Input : nucleusId -
// *ipAddress -
// *authCode -
// &outToken -
// &outMessage -
// Output : true on success, false on failure.
//-----------------------------------------------------------------------------
bool CPylon : : AuthForConnection ( const uint64_t nucleusId , const char * ipAddress ,
const char * authCode , string & outToken , string & outMessage ) const
2023-10-15 10:40:46 +02:00
{
rapidjson : : Document requestJson ;
requestJson . SetObject ( ) ;
rapidjson : : Document : : AllocatorType & allocator = requestJson . GetAllocator ( ) ;
requestJson . AddMember ( " id " , nucleusId , allocator ) ;
requestJson . AddMember ( " ip " , rapidjson : : Value ( ipAddress , allocator ) , allocator ) ;
requestJson . AddMember ( " code " , rapidjson : : Value ( authCode , allocator ) , allocator ) ;
rapidjson : : Document responseJson ;
CURLINFO status ;
if ( ! SendRequest ( " /client/authenticate " , requestJson , responseJson , outMessage , status , " origin auth error " ) )
{
return false ;
}
if ( responseJson . HasMember ( " token " ) & & responseJson [ " token " ] . IsString ( ) )
{
outToken = responseJson [ " token " ] . GetString ( ) ;
return true ;
}
return false ;
}
2023-10-20 19:30:07 +02:00
//-----------------------------------------------------------------------------
// Purpose: checks if the EULA response fields are valid.
// Input : &doc -
// Output : true on success, false on failure.
//-----------------------------------------------------------------------------
2024-04-05 16:26:18 +02:00
static bool ValidateEULAData ( const rapidjson : : Document & doc )
{
if ( ! doc . HasMember ( " data " ) | | ! doc [ " data " ] . IsObject ( ) )
return false ;
const rapidjson : : Value & data = doc [ " data " ] ;
if ( ! data . HasMember ( " version " ) | | ! data [ " version " ] . IsInt ( ) )
return false ;
if ( ! data . HasMember ( " lang " ) | | ! data [ " lang " ] . IsString ( ) )
return false ;
if ( ! data . HasMember ( " contents " ) | | ! data [ " contents " ] . IsString ( ) )
return false ;
return true ;
}
2023-10-20 19:30:07 +02:00
//-----------------------------------------------------------------------------
// Purpose: checks if the accepted EULA is up to date.
// Output : true on success, false on failure.
//-----------------------------------------------------------------------------
2024-04-05 16:26:18 +02:00
static bool IsEULAUpToDate ( )
{
return ( eula_version_accepted - > GetInt ( ) = = eula_version - > GetInt ( ) ) ;
}
2023-10-20 19:30:07 +02:00
//-----------------------------------------------------------------------------
// Purpose: Gets the EULA from master server.
2023-10-25 23:29:37 +02:00
// Input : &outData -
// &outMessage -
2023-10-20 19:30:07 +02:00
// Output : True on success, false on failure.
//-----------------------------------------------------------------------------
2023-10-25 23:29:37 +02:00
bool CPylon : : GetEULA ( MSEulaData_t & outData , string & outMessage ) const
2024-04-05 16:26:18 +02:00
{
rapidjson : : Document requestJson ;
requestJson . SetObject ( ) ;
rapidjson : : Document responseJson ;
CURLINFO status ;
if ( ! SendRequest ( " /eula " , requestJson , responseJson , outMessage , status , " eula fetch error " , false ) )
{
return false ;
}
if ( ! ValidateEULAData ( responseJson ) )
{
return false ;
}
const rapidjson : : Value & data = responseJson [ " data " ] ;
outData . version = data [ " version " ] . GetInt ( ) ;
outData . language = data [ " lang " ] . GetString ( ) ;
outData . contents = data [ " contents " ] . GetString ( ) ;
return true ;
}
2023-04-25 00:45:39 +02:00
//-----------------------------------------------------------------------------
// Purpose: Sends request to Pylon Master Server.
// Input : *endpoint -
2023-04-29 14:58:46 +02:00
// &requestJson -
// &responseJson -
2023-04-25 00:45:39 +02:00
// &outMessage -
// &status -
2024-04-05 16:26:18 +02:00
// checkEula -
2023-04-25 00:45:39 +02:00
// Output : True on success, false on failure.
//-----------------------------------------------------------------------------
2023-09-07 11:17:05 +02:00
bool CPylon : : SendRequest ( const char * endpoint , const rapidjson : : Document & requestJson ,
2024-04-05 16:26:18 +02:00
rapidjson : : Document & responseJson , string & outMessage , CURLINFO & status ,
const char * errorText , const bool checkEula ) const
2023-04-25 00:45:39 +02:00
{
2023-10-21 19:16:52 +02:00
if ( ! IsDedicated ( ) & & ! IsEULAUpToDate ( ) & & checkEula )
2024-04-05 16:26:18 +02:00
{
outMessage = " EULA not accepted " ;
return false ;
}
2023-09-07 11:17:05 +02:00
rapidjson : : StringBuffer stringBuffer ;
JSON_DocumentToBufferDeserialize ( requestJson , stringBuffer ) ;
2023-04-25 00:45:39 +02:00
2023-09-07 11:17:05 +02:00
string responseBody ;
if ( ! QueryServer ( endpoint , stringBuffer . GetString ( ) , responseBody , outMessage , status ) )
2023-04-22 22:00:20 +02:00
{
return false ;
}
2023-09-07 11:17:05 +02:00
if ( status = = 200 ) // STATUS_OK
2022-07-01 10:29:27 +02:00
{
2023-09-07 11:17:05 +02:00
responseJson . Parse ( responseBody . c_str ( ) ) ;
if ( responseJson . HasParseError ( ) )
{
2023-09-07 22:46:12 +02:00
Warning ( eDLL_T : : ENGINE , " %s: JSON parse error at position %zu: %s \n " , __FUNCTION__ ,
2023-09-07 11:17:05 +02:00
responseJson . GetErrorOffset ( ) , rapidjson : : GetParseError_En ( responseJson . GetParseError ( ) ) ) ;
return false ;
}
2023-09-07 20:15:22 +01:00
if ( ! responseJson . IsObject ( ) )
{
2023-09-07 22:46:12 +02:00
Warning ( eDLL_T : : ENGINE , " %s: JSON root was not an object \n " , __FUNCTION__ ) ;
2023-09-07 20:15:22 +01:00
return false ;
}
2024-02-24 02:15:09 +01:00
if ( pylon_showdebuginfo . GetBool ( ) )
2023-04-22 22:00:20 +02:00
{
2023-04-25 00:45:39 +02:00
LogBody ( responseJson ) ;
2023-09-07 11:17:05 +02:00
}
2023-01-26 21:11:32 +01:00
2023-09-07 11:17:05 +02:00
if ( responseJson . HasMember ( " success " ) & &
responseJson [ " success " ] . IsBool ( ) & &
responseJson [ " success " ] . GetBool ( ) )
{
return true ;
2023-04-25 00:45:39 +02:00
}
else
{
2023-09-07 11:17:05 +02:00
ExtractError ( responseJson , outMessage , status ) ;
2023-04-25 00:45:39 +02:00
return false ;
2022-07-01 10:29:27 +02:00
}
}
2023-09-07 11:17:05 +02:00
else
2022-08-28 17:12:58 +02:00
{
2023-09-07 11:17:05 +02:00
ExtractError ( responseBody , outMessage , status , errorText ) ;
2023-04-25 00:45:39 +02:00
return false ;
2022-08-28 17:12:58 +02:00
}
2022-07-01 10:29:27 +02:00
}
2023-01-26 20:06:48 +01:00
//-----------------------------------------------------------------------------
// Purpose: Sends query to master server.
2023-04-25 00:45:39 +02:00
// Input : *endpoint -
// *request -
2023-04-24 01:55:37 +02:00
// &outResponse -
2023-04-29 14:58:46 +02:00
// &outMessage - <- contains an error message on failure.
2023-04-24 01:55:37 +02:00
// &outStatus -
2023-04-25 00:45:39 +02:00
// Output : True on success, false on failure.
2023-04-24 01:55:37 +02:00
//-----------------------------------------------------------------------------
2023-04-25 00:45:39 +02:00
bool CPylon : : QueryServer ( const char * endpoint , const char * request ,
2023-04-24 01:55:37 +02:00
string & outResponse , string & outMessage , CURLINFO & outStatus ) const
{
2024-02-24 02:15:09 +01:00
const bool showDebug = pylon_showdebuginfo . GetBool ( ) ;
const char * hostName = pylon_matchmaking_hostname . GetString ( ) ;
2023-04-24 01:55:37 +02:00
if ( showDebug )
{
2023-08-21 19:12:29 +02:00
Msg ( eDLL_T : : ENGINE , " Sending request to '%s' with endpoint '%s': \n %s \n " ,
2023-04-25 00:45:39 +02:00
hostName , endpoint , request ) ;
2023-04-24 01:55:37 +02:00
}
string finalUrl ;
2023-04-25 00:45:39 +02:00
CURLFormatUrl ( finalUrl , hostName , endpoint ) ;
2024-03-10 01:57:04 +01:00
finalUrl + = Format ( " ?language=%s " , this - > GetLanguage ( ) . c_str ( ) ) ;
2023-08-17 20:02:42 +01:00
2023-07-28 14:47:20 +02:00
CURLParams params ;
params . writeFunction = CURLWriteStringCallback ;
2024-02-24 02:15:09 +01:00
params . timeout = curl_timeout . GetInt ( ) ;
params . verifyPeer = ssl_verify_peer . GetBool ( ) ;
params . verbose = curl_debug . GetBool ( ) ;
2023-07-28 14:47:20 +02:00
2023-01-26 20:06:48 +01:00
curl_slist * sList = nullptr ;
2023-07-28 14:47:20 +02:00
CURL * curl = CURLInitRequest ( finalUrl . c_str ( ) , request , outResponse , sList , params ) ;
2023-01-26 20:06:48 +01:00
if ( ! curl )
{
return false ;
}
CURLcode res = CURLSubmitRequest ( curl , sList ) ;
2023-06-02 00:05:23 +02:00
if ( ! CURLHandleError ( curl , res , outMessage ,
! IsDedicated ( /* Errors are already shown for dedicated! */ ) ) )
2023-01-26 20:06:48 +01:00
{
return false ;
}
outStatus = CURLRetrieveInfo ( curl ) ;
2023-04-25 00:45:39 +02:00
if ( showDebug )
{
2023-08-21 19:12:29 +02:00
Msg ( eDLL_T : : ENGINE , " Host '%s' replied with status: '%d' \n " ,
2023-04-25 00:45:39 +02:00
hostName , outStatus ) ;
}
2023-01-26 20:06:48 +01:00
return true ;
}
2023-04-24 00:32:27 +02:00
//-----------------------------------------------------------------------------
2023-04-24 01:55:37 +02:00
// Purpose: Extracts the error from the result json.
// Input : &resultJson -
2023-04-24 00:32:27 +02:00
// &outMessage -
2023-04-24 01:55:37 +02:00
// status -
// *errorText -
2023-04-24 00:32:27 +02:00
//-----------------------------------------------------------------------------
2023-09-07 11:17:05 +02:00
void CPylon : : ExtractError ( const rapidjson : : Document & resultJson , string & outMessage ,
2023-04-24 01:55:37 +02:00
CURLINFO status , const char * errorText ) const
2023-04-24 00:32:27 +02:00
{
2023-09-07 20:15:22 +01:00
if ( resultJson . IsObject ( ) & & resultJson . HasMember ( " error " ) & &
2023-09-07 11:17:05 +02:00
resultJson [ " error " ] . IsString ( ) )
2023-04-24 00:32:27 +02:00
{
2023-09-07 11:17:05 +02:00
outMessage = resultJson [ " error " ] . GetString ( ) ;
2023-04-24 00:32:27 +02:00
}
else
{
if ( ! errorText )
{
2023-04-25 22:57:06 +02:00
errorText = " unknown error " ;
2023-04-24 00:32:27 +02:00
}
2023-04-26 23:31:16 +02:00
outMessage = Format ( " Failed with status: %d (%s) " ,
int ( status ) , errorText ) ;
2023-04-24 00:32:27 +02:00
}
}
//-----------------------------------------------------------------------------
// Purpose: Extracts the error from the response buffer.
2023-04-24 01:55:37 +02:00
// Input : &response -
2023-04-24 00:32:27 +02:00
// &outMessage -
2023-04-24 01:55:37 +02:00
// status -
// *errorText -
2023-04-24 00:32:27 +02:00
//-----------------------------------------------------------------------------
2023-04-24 01:55:37 +02:00
void CPylon : : ExtractError ( const string & response , string & outMessage ,
CURLINFO status , const char * errorText ) const
2023-04-24 00:32:27 +02:00
{
2023-04-24 01:55:37 +02:00
if ( ! response . empty ( ) )
2023-04-24 00:32:27 +02:00
{
2023-09-07 11:17:05 +02:00
rapidjson : : Document resultBody ;
resultBody . Parse ( response . c_str ( ) ) ;
2023-04-24 00:32:27 +02:00
ExtractError ( resultBody , outMessage , status , errorText ) ;
}
else if ( status )
{
2023-04-25 22:57:06 +02:00
outMessage = Format ( " Failed server query: %d " , int ( status ) ) ;
2023-04-24 00:32:27 +02:00
}
else
{
2023-04-25 22:57:06 +02:00
outMessage = Format ( " Failed to reach server: %s " ,
2023-04-25 22:51:06 +02:00
" connection timed out " ) ;
2023-04-24 00:32:27 +02:00
}
}
2023-04-25 00:45:39 +02:00
//-----------------------------------------------------------------------------
// Purpose: Logs the response body if debug is enabled.
// Input : &responseJson -
//-----------------------------------------------------------------------------
2023-09-07 11:17:05 +02:00
void CPylon : : LogBody ( const rapidjson : : Document & responseJson ) const
2023-04-25 00:45:39 +02:00
{
2023-09-07 11:17:05 +02:00
rapidjson : : StringBuffer stringBuffer ;
JSON_DocumentToBufferDeserialize ( responseJson , stringBuffer ) ;
Msg ( eDLL_T : : ENGINE , " \n %s \n " , stringBuffer . GetString ( ) ) ;
2023-04-25 00:45:39 +02:00
}
2022-07-01 10:29:27 +02:00
///////////////////////////////////////////////////////////////////////////////
2024-01-21 21:29:23 +01:00
CPylon g_MasterServer ;