2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
2021-04-17 14:05:24 +00:00
# include "serverbrowser.h"
# include "serverbrowser_http.h"
# include "serverbrowser_ping_cache.h"
2018-07-11 20:46:04 +00:00
# include <algorithm>
2021-05-12 16:04:49 +00:00
# include <limits.h>
2010-05-29 07:25:38 +00:00
2019-04-06 00:46:56 +00:00
# include <base/hash_ctxt.h>
2011-01-19 14:54:50 +00:00
# include <base/math.h>
2010-05-29 07:25:38 +00:00
# include <base/system.h>
2011-03-23 12:06:35 +00:00
2010-05-29 07:25:38 +00:00
# include <engine/shared/config.h>
2020-01-01 19:07:04 +00:00
# include <engine/shared/json.h>
2010-05-29 07:25:38 +00:00
# include <engine/shared/memheap.h>
2011-03-23 12:06:35 +00:00
# include <engine/shared/network.h>
# include <engine/shared/protocol.h>
2018-07-11 20:46:04 +00:00
# include <engine/shared/serverinfo.h>
2010-05-29 07:25:38 +00:00
# include <engine/config.h>
2011-03-23 12:06:35 +00:00
# include <engine/console.h>
2018-07-11 20:46:04 +00:00
# include <engine/engine.h>
2011-03-23 12:06:35 +00:00
# include <engine/friends.h>
2021-09-13 19:04:21 +00:00
# include <engine/serverbrowser.h>
2014-09-13 14:36:25 +00:00
# include <engine/storage.h>
2010-05-29 07:25:38 +00:00
# include <mastersrv/mastersrv.h>
2014-09-18 14:13:06 +00:00
# include <engine/external/json-parser/json.h>
2010-05-29 07:25:38 +00:00
class SortWrap
{
typedef bool ( CServerBrowser : : * SortFunc ) ( int , int ) const ;
SortFunc m_pfnSort ;
CServerBrowser * m_pThis ;
2020-09-26 19:41:58 +00:00
2010-05-29 07:25:38 +00:00
public :
2020-09-26 19:41:58 +00:00
SortWrap ( CServerBrowser * t , SortFunc f ) :
m_pfnSort ( f ) , m_pThis ( t ) { }
2011-11-29 21:34:47 +00:00
bool operator ( ) ( int a , int b ) { return ( g_Config . m_BrSortOrder ? ( m_pThis - > * m_pfnSort ) ( b , a ) : ( m_pThis - > * m_pfnSort ) ( a , b ) ) ; }
2010-05-29 07:25:38 +00:00
} ;
CServerBrowser : : CServerBrowser ( )
{
m_ppServerlist = 0 ;
m_pSortedServerlist = 0 ;
m_NumFavoriteServers = 0 ;
mem_zero ( m_aServerlistIp , sizeof ( m_aServerlistIp ) ) ;
m_pFirstReqServer = 0 ; // request list
m_pLastReqServer = 0 ;
m_NumRequests = 0 ;
m_NumSortedServers = 0 ;
m_NumSortedServersCapacity = 0 ;
m_NumServers = 0 ;
m_NumServerCapacity = 0 ;
m_Sorthash = 0 ;
m_aFilterString [ 0 ] = 0 ;
m_aFilterGametypeString [ 0 ] = 0 ;
m_ServerlistType = 0 ;
m_BroadcastTime = 0 ;
2017-10-14 19:58:23 +00:00
secure_random_fill ( m_aTokenSeed , sizeof ( m_aTokenSeed ) ) ;
2017-08-30 19:34:01 +00:00
2017-09-03 15:36:51 +00:00
m_pDDNetInfo = 0 ;
2020-12-22 10:13:51 +00:00
m_SortOnNextUpdate = false ;
2017-09-03 15:36:51 +00:00
}
CServerBrowser : : ~ CServerBrowser ( )
{
2020-10-13 16:05:59 +00:00
if ( m_ppServerlist )
free ( m_ppServerlist ) ;
if ( m_pSortedServerlist )
free ( m_pSortedServerlist ) ;
2017-09-03 15:36:51 +00:00
if ( m_pDDNetInfo )
json_value_free ( m_pDDNetInfo ) ;
2018-07-11 20:46:04 +00:00
delete m_pHttp ;
m_pHttp = nullptr ;
2021-04-17 14:05:24 +00:00
delete m_pPingCache ;
m_pPingCache = nullptr ;
2010-05-29 07:25:38 +00:00
}
void CServerBrowser : : SetBaseInfo ( class CNetClient * pClient , const char * pNetVersion )
{
m_pNetClient = pClient ;
str_copy ( m_aNetVersion , pNetVersion , sizeof ( m_aNetVersion ) ) ;
2010-08-17 22:06:00 +00:00
m_pConsole = Kernel ( ) - > RequestInterface < IConsole > ( ) ;
2018-07-11 20:46:04 +00:00
m_pEngine = Kernel ( ) - > RequestInterface < IEngine > ( ) ;
2011-03-23 12:06:35 +00:00
m_pFriends = Kernel ( ) - > RequestInterface < IFriends > ( ) ;
2021-04-17 14:05:24 +00:00
m_pStorage = Kernel ( ) - > RequestInterface < IStorage > ( ) ;
2021-01-10 12:47:07 +00:00
IConfigManager * pConfigManager = Kernel ( ) - > RequestInterface < IConfigManager > ( ) ;
if ( pConfigManager )
pConfigManager - > RegisterCallback ( ConfigSaveCallback , this ) ;
2021-04-17 14:05:24 +00:00
m_pPingCache = CreateServerBrowserPingCache ( m_pConsole , m_pStorage ) ;
2021-04-21 14:54:42 +00:00
RegisterCommands ( ) ;
}
2021-06-02 22:27:00 +00:00
void CServerBrowser : : OnInit ( )
{
m_pHttp = CreateServerBrowserHttp ( m_pEngine , m_pConsole , m_pStorage , g_Config . m_BrCachedBestServerinfoUrl ) ;
}
2021-04-21 14:54:42 +00:00
void CServerBrowser : : RegisterCommands ( )
{
m_pConsole - > Register ( " leak_ip_address_to_all_servers " , " " , CFGFLAG_CLIENT , Con_LeakIpAddress , this , " Leaks your IP address to all servers by pinging each of them, also acquiring the latency in the process " ) ;
}
void CServerBrowser : : Con_LeakIpAddress ( IConsole : : IResult * pResult , void * pUserData )
{
CServerBrowser * pThis = ( CServerBrowser * ) pUserData ;
std : : vector < int > aSortedServers ;
// Sort servers by IP address, ignoring port.
class CAddrComparer
{
public :
CServerBrowser * m_pThis ;
bool operator ( ) ( int i , int j )
{
NETADDR Addr1 = m_pThis - > m_ppServerlist [ i ] - > m_Addr ;
NETADDR Addr2 = m_pThis - > m_ppServerlist [ j ] - > m_Addr ;
Addr1 . port = 0 ;
Addr2 . port = 0 ;
return net_addr_comp ( & Addr1 , & Addr2 ) < 0 ;
}
} ;
2021-05-13 23:25:59 +00:00
aSortedServers . reserve ( pThis - > m_NumServers ) ;
2021-04-21 14:54:42 +00:00
for ( int i = 0 ; i < pThis - > m_NumServers ; i + + )
{
aSortedServers . push_back ( i ) ;
}
std : : sort ( aSortedServers . begin ( ) , aSortedServers . end ( ) , CAddrComparer { pThis } ) ;
// Group the servers into those with same IP address (but differing
// port).
NETADDR Addr ;
int Start = - 1 ;
for ( int i = 0 ; i < = ( int ) aSortedServers . size ( ) ; i + + )
{
NETADDR NextAddr ;
if ( i < ( int ) aSortedServers . size ( ) )
{
NextAddr = pThis - > m_ppServerlist [ aSortedServers [ i ] ] - > m_Addr ;
NextAddr . port = 0 ;
}
bool New = Start = = - 1 | | i = = ( int ) aSortedServers . size ( ) | | net_addr_comp ( & Addr , & NextAddr ) ! = 0 ;
if ( Start ! = - 1 & & New )
{
int Chosen = Start + secure_rand_below ( i - Start ) ;
CServerEntry * pChosen = pThis - > m_ppServerlist [ aSortedServers [ Chosen ] ] ;
pChosen - > m_RequestIgnoreInfo = true ;
pThis - > QueueRequest ( pChosen ) ;
char aAddr [ NETADDR_MAXSTRSIZE ] ;
net_addr_str ( & pChosen - > m_Addr , aAddr , sizeof ( aAddr ) , true ) ;
dbg_msg ( " serverbrowse/dbg " , " queuing ping request for %s " , aAddr ) ;
}
if ( i < ( int ) aSortedServers . size ( ) & & New )
{
Start = i ;
Addr = NextAddr ;
}
}
2010-05-29 07:25:38 +00:00
}
const CServerInfo * CServerBrowser : : SortedGet ( int Index ) const
{
if ( Index < 0 | | Index > = m_NumSortedServers )
return 0 ;
return & m_ppServerlist [ m_pSortedServerlist [ Index ] ] - > m_Info ;
}
2017-10-14 19:58:23 +00:00
int CServerBrowser : : GenerateToken ( const NETADDR & Addr ) const
{
2019-04-06 00:47:00 +00:00
SHA256_CTX Sha256 ;
sha256_init ( & Sha256 ) ;
sha256_update ( & Sha256 , m_aTokenSeed , sizeof ( m_aTokenSeed ) ) ;
sha256_update ( & Sha256 , ( unsigned char * ) & Addr , sizeof ( Addr ) ) ;
SHA256_DIGEST Digest = sha256_finish ( & Sha256 ) ;
2019-04-06 00:46:56 +00:00
return ( Digest . data [ 0 ] < < 16 ) | ( Digest . data [ 1 ] < < 8 ) | Digest . data [ 2 ] ;
2017-10-14 19:58:23 +00:00
}
int CServerBrowser : : GetBasicToken ( int Token )
{
return Token & 0xff ;
}
int CServerBrowser : : GetExtraToken ( int Token )
{
return Token > > 8 ;
}
2010-05-29 07:25:38 +00:00
bool CServerBrowser : : SortCompareName ( int Index1 , int Index2 ) const
{
CServerEntry * a = m_ppServerlist [ Index1 ] ;
CServerEntry * b = m_ppServerlist [ Index2 ] ;
2010-08-12 17:29:59 +00:00
// make sure empty entries are listed last
2011-04-13 18:37:12 +00:00
return ( a - > m_GotInfo & & b - > m_GotInfo ) | | ( ! a - > m_GotInfo & & ! b - > m_GotInfo ) ? str_comp ( a - > m_Info . m_aName , b - > m_Info . m_aName ) < 0 :
2022-01-22 16:34:23 +00:00
a - > m_GotInfo ! = 0 ;
2010-05-29 07:25:38 +00:00
}
bool CServerBrowser : : SortCompareMap ( int Index1 , int Index2 ) const
{
CServerEntry * a = m_ppServerlist [ Index1 ] ;
CServerEntry * b = m_ppServerlist [ Index2 ] ;
return str_comp ( a - > m_Info . m_aMap , b - > m_Info . m_aMap ) < 0 ;
}
bool CServerBrowser : : SortComparePing ( int Index1 , int Index2 ) const
{
CServerEntry * a = m_ppServerlist [ Index1 ] ;
CServerEntry * b = m_ppServerlist [ Index2 ] ;
return a - > m_Info . m_Latency < b - > m_Info . m_Latency ;
}
bool CServerBrowser : : SortCompareGametype ( int Index1 , int Index2 ) const
{
CServerEntry * a = m_ppServerlist [ Index1 ] ;
CServerEntry * b = m_ppServerlist [ Index2 ] ;
return str_comp ( a - > m_Info . m_aGameType , b - > m_Info . m_aGameType ) < 0 ;
}
bool CServerBrowser : : SortCompareNumPlayers ( int Index1 , int Index2 ) const
{
CServerEntry * a = m_ppServerlist [ Index1 ] ;
CServerEntry * b = m_ppServerlist [ Index2 ] ;
2020-08-25 00:26:36 +00:00
return a - > m_Info . m_NumFilteredPlayers > b - > m_Info . m_NumFilteredPlayers ;
2010-05-29 07:25:38 +00:00
}
2011-03-20 14:33:49 +00:00
bool CServerBrowser : : SortCompareNumClients ( int Index1 , int Index2 ) const
{
CServerEntry * a = m_ppServerlist [ Index1 ] ;
CServerEntry * b = m_ppServerlist [ Index2 ] ;
2020-08-25 00:26:36 +00:00
return a - > m_Info . m_NumClients > b - > m_Info . m_NumClients ;
2011-03-20 14:33:49 +00:00
}
2020-08-24 21:23:37 +00:00
bool CServerBrowser : : SortCompareNumPlayersAndPing ( int Index1 , int Index2 ) const
{
CServerEntry * a = m_ppServerlist [ Index1 ] ;
CServerEntry * b = m_ppServerlist [ Index2 ] ;
if ( a - > m_Info . m_NumFilteredPlayers = = b - > m_Info . m_NumFilteredPlayers )
return a - > m_Info . m_Latency > b - > m_Info . m_Latency ;
2020-10-23 23:17:03 +00:00
else if ( a - > m_Info . m_NumFilteredPlayers = = 0 | | b - > m_Info . m_NumFilteredPlayers = = 0 | | a - > m_Info . m_Latency / 100 = = b - > m_Info . m_Latency / 100 )
return a - > m_Info . m_NumFilteredPlayers < b - > m_Info . m_NumFilteredPlayers ;
2020-08-24 21:23:37 +00:00
else
2020-10-23 23:17:03 +00:00
return a - > m_Info . m_Latency > b - > m_Info . m_Latency ;
2020-08-24 21:23:37 +00:00
}
2010-05-29 07:25:38 +00:00
void CServerBrowser : : Filter ( )
{
int i = 0 , p = 0 ;
m_NumSortedServers = 0 ;
// allocate the sorted list
if ( m_NumSortedServersCapacity < m_NumServers )
{
if ( m_pSortedServerlist )
2018-04-09 09:56:39 +00:00
free ( m_pSortedServerlist ) ;
2010-05-29 07:25:38 +00:00
m_NumSortedServersCapacity = m_NumServers ;
2018-04-09 09:56:39 +00:00
m_pSortedServerlist = ( int * ) calloc ( m_NumSortedServersCapacity , sizeof ( int ) ) ;
2010-05-29 07:25:38 +00:00
}
// filter the servers
for ( i = 0 ; i < m_NumServers ; i + + )
{
int Filtered = 0 ;
2018-10-29 21:03:57 +00:00
if ( g_Config . m_BrFilterEmpty & & m_ppServerlist [ i ] - > m_Info . m_NumFilteredPlayers = = 0 )
2011-06-26 15:10:13 +00:00
Filtered = 1 ;
2018-08-21 07:03:29 +00:00
else if ( g_Config . m_BrFilterFull & & Players ( m_ppServerlist [ i ] - > m_Info ) = = Max ( m_ppServerlist [ i ] - > m_Info ) )
2011-06-26 15:10:13 +00:00
Filtered = 1 ;
2020-09-26 19:41:58 +00:00
else if ( g_Config . m_BrFilterPw & & m_ppServerlist [ i ] - > m_Info . m_Flags & SERVER_FLAG_PASSWORD )
2011-06-26 15:10:13 +00:00
Filtered = 1 ;
else if ( g_Config . m_BrFilterServerAddress [ 0 ] & & ! str_find_nocase ( m_ppServerlist [ i ] - > m_Info . m_aAddress , g_Config . m_BrFilterServerAddress ) )
Filtered = 1 ;
else if ( g_Config . m_BrFilterGametypeStrict & & g_Config . m_BrFilterGametype [ 0 ] & & str_comp_nocase ( m_ppServerlist [ i ] - > m_Info . m_aGameType , g_Config . m_BrFilterGametype ) )
Filtered = 1 ;
2021-06-28 20:51:14 +00:00
else if ( ! g_Config . m_BrFilterGametypeStrict & & g_Config . m_BrFilterGametype [ 0 ] & & ! str_utf8_find_nocase ( m_ppServerlist [ i ] - > m_Info . m_aGameType , g_Config . m_BrFilterGametype ) )
2011-06-26 15:10:13 +00:00
Filtered = 1 ;
2019-05-11 11:45:41 +00:00
else if ( g_Config . m_BrFilterUnfinishedMap & & m_ppServerlist [ i ] - > m_Info . m_HasRank = = 1 )
2017-08-30 19:34:01 +00:00
Filtered = 1 ;
2011-06-29 20:27:32 +00:00
else
2011-06-26 15:10:13 +00:00
{
2011-06-29 20:27:32 +00:00
if ( g_Config . m_BrFilterCountry )
2011-06-26 15:10:13 +00:00
{
2011-06-29 20:27:32 +00:00
Filtered = 1 ;
// match against player country
2020-10-18 16:41:18 +00:00
for ( p = 0 ; p < minimum ( m_ppServerlist [ i ] - > m_Info . m_NumClients , ( int ) MAX_CLIENTS ) ; p + + )
2011-06-29 20:27:32 +00:00
{
if ( m_ppServerlist [ i ] - > m_Info . m_aClients [ p ] . m_Country = = g_Config . m_BrFilterCountryIndex )
{
Filtered = 0 ;
break ;
}
}
2011-06-26 15:10:13 +00:00
}
2011-03-18 18:03:13 +00:00
2011-06-29 20:27:32 +00:00
if ( ! Filtered & & g_Config . m_BrFilterString [ 0 ] ! = 0 )
2011-06-26 15:10:13 +00:00
{
2016-08-06 22:52:00 +00:00
int MatchFound = 0 ;
2011-06-29 20:27:32 +00:00
m_ppServerlist [ i ] - > m_Info . m_QuickSearchHit = 0 ;
2016-08-06 22:52:00 +00:00
// match against server name
2021-06-28 20:51:14 +00:00
if ( str_utf8_find_nocase ( m_ppServerlist [ i ] - > m_Info . m_aName , g_Config . m_BrFilterString ) )
2011-03-23 12:06:35 +00:00
{
2016-08-06 22:52:00 +00:00
MatchFound = 1 ;
m_ppServerlist [ i ] - > m_Info . m_QuickSearchHit | = IServerBrowser : : QUICK_SERVERNAME ;
}
2016-08-02 20:16:02 +00:00
2016-08-06 22:52:00 +00:00
// match against players
2020-10-18 16:41:18 +00:00
for ( p = 0 ; p < minimum ( m_ppServerlist [ i ] - > m_Info . m_NumClients , ( int ) MAX_CLIENTS ) ; p + + )
2016-08-06 22:52:00 +00:00
{
2021-06-28 20:51:14 +00:00
if ( str_utf8_find_nocase ( m_ppServerlist [ i ] - > m_Info . m_aClients [ p ] . m_aName , g_Config . m_BrFilterString ) | |
str_utf8_find_nocase ( m_ppServerlist [ i ] - > m_Info . m_aClients [ p ] . m_aClan , g_Config . m_BrFilterString ) )
2016-08-02 20:16:02 +00:00
{
MatchFound = 1 ;
2016-08-06 22:52:00 +00:00
m_ppServerlist [ i ] - > m_Info . m_QuickSearchHit | = IServerBrowser : : QUICK_PLAYER ;
break ;
2016-08-02 20:16:02 +00:00
}
2016-08-06 22:52:00 +00:00
}
2011-06-29 20:27:32 +00:00
2016-08-06 22:52:00 +00:00
// match against map
2021-06-28 20:51:14 +00:00
if ( str_utf8_find_nocase ( m_ppServerlist [ i ] - > m_Info . m_aMap , g_Config . m_BrFilterString ) )
2016-08-06 22:52:00 +00:00
{
MatchFound = 1 ;
m_ppServerlist [ i ] - > m_Info . m_QuickSearchHit | = IServerBrowser : : QUICK_MAPNAME ;
2016-08-02 20:16:02 +00:00
}
2016-08-06 22:52:00 +00:00
if ( ! MatchFound )
Filtered = 1 ;
2011-06-29 20:27:32 +00:00
}
2014-12-10 03:49:16 +00:00
if ( ! Filtered & & g_Config . m_BrExcludeString [ 0 ] ! = 0 )
{
2016-08-06 22:52:00 +00:00
int MatchFound = 0 ;
2014-12-10 03:49:16 +00:00
2016-08-06 22:52:00 +00:00
// match against server name
2021-06-28 20:51:14 +00:00
if ( str_utf8_find_nocase ( m_ppServerlist [ i ] - > m_Info . m_aName , g_Config . m_BrExcludeString ) )
2014-12-10 03:49:16 +00:00
{
2016-08-06 22:52:00 +00:00
MatchFound = 1 ;
}
2014-12-10 03:49:16 +00:00
2016-08-06 22:52:00 +00:00
// match against map
2021-06-28 20:51:14 +00:00
if ( str_utf8_find_nocase ( m_ppServerlist [ i ] - > m_Info . m_aMap , g_Config . m_BrExcludeString ) )
2016-08-06 22:52:00 +00:00
{
MatchFound = 1 ;
2016-08-02 20:16:02 +00:00
}
2019-03-05 09:46:29 +00:00
2017-08-30 21:55:02 +00:00
// match against gametype
2021-06-28 20:51:14 +00:00
if ( str_utf8_find_nocase ( m_ppServerlist [ i ] - > m_Info . m_aGameType , g_Config . m_BrExcludeString ) )
2017-08-30 21:55:02 +00:00
{
MatchFound = 1 ;
}
2016-08-06 22:52:00 +00:00
if ( MatchFound )
Filtered = 1 ;
2014-12-10 03:49:16 +00:00
}
2010-05-29 07:25:38 +00:00
}
if ( Filtered = = 0 )
2011-06-26 15:10:13 +00:00
{
// check for friend
m_ppServerlist [ i ] - > m_Info . m_FriendState = IFriends : : FRIEND_NO ;
2020-10-10 11:02:24 +00:00
for ( p = 0 ; p < minimum ( m_ppServerlist [ i ] - > m_Info . m_NumClients , ( int ) MAX_CLIENTS ) ; p + + )
2011-06-26 15:10:13 +00:00
{
m_ppServerlist [ i ] - > m_Info . m_aClients [ p ] . m_FriendState = m_pFriends - > GetFriendState ( m_ppServerlist [ i ] - > m_Info . m_aClients [ p ] . m_aName ,
m_ppServerlist [ i ] - > m_Info . m_aClients [ p ] . m_aClan ) ;
2019-04-26 19:36:49 +00:00
m_ppServerlist [ i ] - > m_Info . m_FriendState = maximum ( m_ppServerlist [ i ] - > m_Info . m_FriendState , m_ppServerlist [ i ] - > m_Info . m_aClients [ p ] . m_FriendState ) ;
2011-06-26 15:10:13 +00:00
}
if ( ! g_Config . m_BrFilterFriends | | m_ppServerlist [ i ] - > m_Info . m_FriendState ! = IFriends : : FRIEND_NO )
m_pSortedServerlist [ m_NumSortedServers + + ] = i ;
}
2010-05-29 07:25:38 +00:00
}
}
int CServerBrowser : : SortHash ( ) const
{
2020-09-26 19:33:36 +00:00
int i = g_Config . m_BrSort & 0xff ;
i | = g_Config . m_BrFilterEmpty < < 4 ;
i | = g_Config . m_BrFilterFull < < 5 ;
i | = g_Config . m_BrFilterSpectators < < 6 ;
i | = g_Config . m_BrFilterFriends < < 7 ;
i | = g_Config . m_BrFilterPw < < 8 ;
i | = g_Config . m_BrSortOrder < < 9 ;
i | = g_Config . m_BrFilterGametypeStrict < < 12 ;
i | = g_Config . m_BrFilterUnfinishedMap < < 13 ;
i | = g_Config . m_BrFilterCountry < < 14 ;
i | = g_Config . m_BrFilterConnectingPlayers < < 15 ;
2010-05-29 07:25:38 +00:00
return i ;
}
2018-10-29 21:03:57 +00:00
void SetFilteredPlayers ( const CServerInfo & Item )
{
2021-03-19 10:46:00 +00:00
Item . m_NumFilteredPlayers = g_Config . m_BrFilterSpectators ? Item . m_NumPlayers : Item . m_NumClients ;
2020-09-26 19:41:58 +00:00
if ( g_Config . m_BrFilterConnectingPlayers )
2018-10-29 21:03:57 +00:00
{
2020-10-26 13:11:11 +00:00
for ( const auto & Client : Item . m_aClients )
2019-10-31 13:16:35 +00:00
{
2021-03-19 10:46:00 +00:00
if ( ( ! g_Config . m_BrFilterSpectators | | Client . m_Player ) & & str_comp ( Client . m_aName , " (connecting) " ) = = 0 & & Client . m_aClan [ 0 ] = = ' \0 ' )
2019-10-31 13:16:35 +00:00
Item . m_NumFilteredPlayers - - ;
}
2018-10-29 21:03:57 +00:00
}
}
2010-05-29 07:25:38 +00:00
void CServerBrowser : : Sort ( )
{
int i ;
2018-10-29 21:03:57 +00:00
// fill m_NumFilteredPlayers
for ( i = 0 ; i < m_NumServers ; i + + )
{
SetFilteredPlayers ( m_ppServerlist [ i ] - > m_Info ) ;
}
2010-05-29 07:25:38 +00:00
// create filtered list
Filter ( ) ;
// sort
2020-09-26 19:41:58 +00:00
if ( g_Config . m_BrSortOrder = = 2 & & ( g_Config . m_BrSort = = IServerBrowser : : SORT_NUMPLAYERS | | g_Config . m_BrSort = = IServerBrowser : : SORT_PING ) )
std : : stable_sort ( m_pSortedServerlist , m_pSortedServerlist + m_NumSortedServers , SortWrap ( this , & CServerBrowser : : SortCompareNumPlayersAndPing ) ) ;
2020-08-24 21:23:37 +00:00
else if ( g_Config . m_BrSort = = IServerBrowser : : SORT_NAME )
2020-09-26 19:41:58 +00:00
std : : stable_sort ( m_pSortedServerlist , m_pSortedServerlist + m_NumSortedServers , SortWrap ( this , & CServerBrowser : : SortCompareName ) ) ;
2010-05-29 07:25:38 +00:00
else if ( g_Config . m_BrSort = = IServerBrowser : : SORT_PING )
2020-09-26 19:41:58 +00:00
std : : stable_sort ( m_pSortedServerlist , m_pSortedServerlist + m_NumSortedServers , SortWrap ( this , & CServerBrowser : : SortComparePing ) ) ;
2010-05-29 07:25:38 +00:00
else if ( g_Config . m_BrSort = = IServerBrowser : : SORT_MAP )
2020-09-26 19:41:58 +00:00
std : : stable_sort ( m_pSortedServerlist , m_pSortedServerlist + m_NumSortedServers , SortWrap ( this , & CServerBrowser : : SortCompareMap ) ) ;
2010-05-29 07:25:38 +00:00
else if ( g_Config . m_BrSort = = IServerBrowser : : SORT_NUMPLAYERS )
2020-09-26 19:41:58 +00:00
std : : stable_sort ( m_pSortedServerlist , m_pSortedServerlist + m_NumSortedServers , SortWrap ( this , & CServerBrowser : : SortCompareNumPlayers ) ) ;
2010-05-29 07:25:38 +00:00
else if ( g_Config . m_BrSort = = IServerBrowser : : SORT_GAMETYPE )
2020-09-26 19:41:58 +00:00
std : : stable_sort ( m_pSortedServerlist , m_pSortedServerlist + m_NumSortedServers , SortWrap ( this , & CServerBrowser : : SortCompareGametype ) ) ;
2010-05-29 07:25:38 +00:00
str_copy ( m_aFilterGametypeString , g_Config . m_BrFilterGametype , sizeof ( m_aFilterGametypeString ) ) ;
str_copy ( m_aFilterString , g_Config . m_BrFilterString , sizeof ( m_aFilterString ) ) ;
m_Sorthash = SortHash ( ) ;
}
void CServerBrowser : : RemoveRequest ( CServerEntry * pEntry )
{
if ( pEntry - > m_pPrevReq | | pEntry - > m_pNextReq | | m_pFirstReqServer = = pEntry )
{
if ( pEntry - > m_pPrevReq )
pEntry - > m_pPrevReq - > m_pNextReq = pEntry - > m_pNextReq ;
else
m_pFirstReqServer = pEntry - > m_pNextReq ;
if ( pEntry - > m_pNextReq )
pEntry - > m_pNextReq - > m_pPrevReq = pEntry - > m_pPrevReq ;
else
m_pLastReqServer = pEntry - > m_pPrevReq ;
pEntry - > m_pPrevReq = 0 ;
pEntry - > m_pNextReq = 0 ;
m_NumRequests - - ;
}
}
CServerBrowser : : CServerEntry * CServerBrowser : : Find ( const NETADDR & Addr )
{
CServerEntry * pEntry = m_aServerlistIp [ Addr . ip [ 0 ] ] ;
for ( ; pEntry ; pEntry = pEntry - > m_pNextIp )
{
if ( net_addr_comp ( & pEntry - > m_Addr , & Addr ) = = 0 )
return pEntry ;
}
2020-09-26 19:41:58 +00:00
return ( CServerEntry * ) 0 ;
2010-05-29 07:25:38 +00:00
}
void CServerBrowser : : QueueRequest ( CServerEntry * pEntry )
{
// add it to the list of servers that we should request info from
pEntry - > m_pPrevReq = m_pLastReqServer ;
if ( m_pLastReqServer )
m_pLastReqServer - > m_pNextReq = pEntry ;
else
m_pFirstReqServer = pEntry ;
m_pLastReqServer = pEntry ;
2013-12-31 01:34:33 +00:00
pEntry - > m_pNextReq = 0 ;
2010-05-29 07:25:38 +00:00
m_NumRequests + + ;
}
void CServerBrowser : : SetInfo ( CServerEntry * pEntry , const CServerInfo & Info )
{
2019-03-19 06:46:48 +00:00
bool Fav = pEntry - > m_Info . m_Favorite ;
bool Off = pEntry - > m_Info . m_Official ;
2010-05-29 07:25:38 +00:00
pEntry - > m_Info = Info ;
pEntry - > m_Info . m_Favorite = Fav ;
2019-03-19 06:46:48 +00:00
pEntry - > m_Info . m_Official = Off ;
2010-05-29 07:25:38 +00:00
pEntry - > m_Info . m_NetAddr = pEntry - > m_Addr ;
2018-07-11 20:46:04 +00:00
net_addr_str ( & pEntry - > m_Info . m_NetAddr , pEntry - > m_Info . m_aAddress , sizeof ( pEntry - > m_Info . m_aAddress ) , 1 ) ;
2010-05-29 07:25:38 +00:00
2021-05-12 16:04:49 +00:00
class CPlayerScoreNameLess
{
public :
bool operator ( ) ( const CServerInfo : : CClient & p0 , const CServerInfo : : CClient & p1 )
{
if ( p0 . m_Player & & ! p1 . m_Player )
return true ;
if ( ! p0 . m_Player & & p1 . m_Player )
return false ;
int Score0 = p0 . m_Score ;
int Score1 = p1 . m_Score ;
if ( Score0 = = - 9999 )
Score0 = INT_MIN ;
if ( Score1 = = - 9999 )
Score1 = INT_MIN ;
if ( Score0 > Score1 )
return true ;
if ( Score0 < Score1 )
return false ;
return str_comp_nocase ( p0 . m_aName , p1 . m_aName ) < 0 ;
}
} ;
std : : sort ( pEntry - > m_Info . m_aClients , pEntry - > m_Info . m_aClients + Info . m_NumReceivedClients , CPlayerScoreNameLess ( ) ) ;
2010-05-29 07:25:38 +00:00
pEntry - > m_GotInfo = 1 ;
}
2021-04-21 14:54:42 +00:00
void CServerBrowser : : SetLatency ( NETADDR Addr , int Latency )
{
Addr . port = 0 ;
for ( CServerEntry * pEntry = m_aServerlistIp [ Addr . ip [ 0 ] ] ; pEntry ; pEntry = pEntry - > m_pNextIp )
{
NETADDR Other = pEntry - > m_Addr ;
Other . port = 0 ;
2021-06-14 22:06:26 +00:00
if ( net_addr_comp ( & Addr , & Other ) = = 0 & & pEntry - > m_GotInfo )
2021-04-21 14:54:42 +00:00
{
pEntry - > m_Info . m_Latency = Latency ;
pEntry - > m_Info . m_LatencyIsEstimated = false ;
}
}
m_pPingCache - > CachePing ( Addr , Latency ) ;
}
2010-05-29 07:25:38 +00:00
CServerBrowser : : CServerEntry * CServerBrowser : : Add ( const NETADDR & Addr )
{
int Hash = Addr . ip [ 0 ] ;
CServerEntry * pEntry = 0 ;
// create new pEntry
pEntry = ( CServerEntry * ) m_ServerlistHeap . Allocate ( sizeof ( CServerEntry ) ) ;
mem_zero ( pEntry , sizeof ( CServerEntry ) ) ;
// set the info
pEntry - > m_Addr = Addr ;
pEntry - > m_Info . m_NetAddr = Addr ;
pEntry - > m_Info . m_Latency = 999 ;
2017-08-30 19:34:01 +00:00
pEntry - > m_Info . m_HasRank = - 1 ;
2011-12-29 22:36:53 +00:00
net_addr_str ( & Addr , pEntry - > m_Info . m_aAddress , sizeof ( pEntry - > m_Info . m_aAddress ) , true ) ;
2011-03-30 10:08:33 +00:00
str_copy ( pEntry - > m_Info . m_aName , pEntry - > m_Info . m_aAddress , sizeof ( pEntry - > m_Info . m_aName ) ) ;
2010-05-29 07:25:38 +00:00
// check if it's a favorite
2021-05-12 19:06:08 +00:00
pEntry - > m_Info . m_Favorite = IsFavorite ( Addr ) ;
2019-03-19 06:46:48 +00:00
// check if it's an official server
2020-10-26 14:14:07 +00:00
for ( auto & Network : m_aNetworks )
2019-03-19 06:46:48 +00:00
{
2020-10-26 14:14:07 +00:00
for ( int i = 0 ; i < Network . m_NumCountries ; i + + )
2019-03-19 06:46:48 +00:00
{
2020-10-26 14:14:07 +00:00
CNetworkCountry * pCntr = & Network . m_aCountries [ i ] ;
2019-03-24 22:15:38 +00:00
for ( int j = 0 ; j < pCntr - > m_NumServers ; j + + )
2019-03-19 06:46:48 +00:00
{
2019-03-24 22:15:38 +00:00
if ( net_addr_comp ( & Addr , & pCntr - > m_aServers [ j ] ) = = 0 )
{
pEntry - > m_Info . m_Official = true ;
break ;
}
2019-03-19 06:46:48 +00:00
}
}
2010-05-29 07:25:38 +00:00
}
// add to the hash list
pEntry - > m_pNextIp = m_aServerlistIp [ Hash ] ;
m_aServerlistIp [ Hash ] = pEntry ;
if ( m_NumServers = = m_NumServerCapacity )
{
CServerEntry * * ppNewlist ;
m_NumServerCapacity + = 100 ;
2020-11-04 18:12:45 +00:00
ppNewlist = ( CServerEntry * * ) calloc ( m_NumServerCapacity , sizeof ( CServerEntry * ) ) ; // NOLINT(bugprone-sizeof-expression)
2020-10-10 10:17:40 +00:00
if ( m_NumServers > 0 )
2020-11-04 18:12:45 +00:00
mem_copy ( ppNewlist , m_ppServerlist , m_NumServers * sizeof ( CServerEntry * ) ) ; // NOLINT(bugprone-sizeof-expression)
2018-04-09 09:56:39 +00:00
free ( m_ppServerlist ) ;
2010-05-29 07:25:38 +00:00
m_ppServerlist = ppNewlist ;
}
// add to list
m_ppServerlist [ m_NumServers ] = pEntry ;
pEntry - > m_Info . m_ServerIndex = m_NumServers ;
m_NumServers + + ;
return pEntry ;
}
void CServerBrowser : : Set ( const NETADDR & Addr , int Type , int Token , const CServerInfo * pInfo )
{
CServerEntry * pEntry = 0 ;
if ( Type = = IServerBrowser : : SET_MASTER_ADD )
{
if ( m_ServerlistType ! = IServerBrowser : : TYPE_INTERNET )
return ;
if ( ! Find ( Addr ) )
{
pEntry = Add ( Addr ) ;
QueueRequest ( pEntry ) ;
}
}
else if ( Type = = IServerBrowser : : SET_FAV_ADD )
{
if ( m_ServerlistType ! = IServerBrowser : : TYPE_FAVORITES )
return ;
if ( ! Find ( Addr ) )
{
pEntry = Add ( Addr ) ;
QueueRequest ( pEntry ) ;
}
}
2014-09-13 14:36:25 +00:00
else if ( Type = = IServerBrowser : : SET_DDNET_ADD )
{
if ( m_ServerlistType ! = IServerBrowser : : TYPE_DDNET )
return ;
if ( ! Find ( Addr ) )
{
pEntry = Add ( Addr ) ;
QueueRequest ( pEntry ) ;
}
}
2019-03-24 22:15:38 +00:00
else if ( Type = = IServerBrowser : : SET_KOG_ADD )
{
if ( m_ServerlistType ! = IServerBrowser : : TYPE_KOG )
return ;
if ( ! Find ( Addr ) )
{
pEntry = Add ( Addr ) ;
QueueRequest ( pEntry ) ;
}
}
2018-07-11 20:46:04 +00:00
else if ( Type = = IServerBrowser : : SET_HTTPINFO )
{
if ( ! pEntry )
{
pEntry = Add ( Addr ) ;
}
if ( pEntry )
{
SetInfo ( pEntry , * pInfo ) ;
2021-04-21 14:54:42 +00:00
pEntry - > m_RequestIgnoreInfo = true ;
2018-07-11 20:46:04 +00:00
}
}
2010-05-29 07:25:38 +00:00
else if ( Type = = IServerBrowser : : SET_TOKEN )
{
2017-10-14 19:58:23 +00:00
int BasicToken = Token ;
int ExtraToken = 0 ;
2017-03-29 10:56:13 +00:00
if ( pInfo - > m_Type = = SERVERINFO_EXTENDED )
{
2017-10-14 19:58:23 +00:00
BasicToken = Token & 0xff ;
ExtraToken = Token > > 8 ;
2017-03-29 10:56:13 +00:00
}
2010-05-29 07:25:38 +00:00
pEntry = Find ( Addr ) ;
2020-09-26 19:41:58 +00:00
2020-09-02 23:51:33 +00:00
if ( m_ServerlistType = = IServerBrowser : : TYPE_LAN )
{
NETADDR Broadcast ;
mem_zero ( & Broadcast , sizeof ( Broadcast ) ) ;
2020-09-26 19:41:58 +00:00
Broadcast . type = m_pNetClient - > NetType ( ) | NETTYPE_LINK_BROADCAST ;
2020-09-02 23:51:33 +00:00
int Token = GenerateToken ( Broadcast ) ;
bool Drop = false ;
Drop = Drop | | BasicToken ! = GetBasicToken ( Token ) ;
Drop = Drop | | ( pInfo - > m_Type = = SERVERINFO_EXTENDED & & ExtraToken ! = GetExtraToken ( Token ) ) ;
if ( Drop )
{
return ;
}
if ( ! pEntry )
pEntry = Add ( Addr ) ;
}
else
2017-03-29 10:56:13 +00:00
{
2017-10-14 19:58:23 +00:00
if ( ! pEntry )
{
return ;
}
int Token = GenerateToken ( Addr ) ;
bool Drop = false ;
Drop = Drop | | BasicToken ! = GetBasicToken ( Token ) ;
Drop = Drop | | ( pInfo - > m_Type = = SERVERINFO_EXTENDED & & ExtraToken ! = GetExtraToken ( Token ) ) ;
if ( Drop )
2017-03-29 10:56:13 +00:00
{
return ;
}
}
2020-09-26 19:41:58 +00:00
2021-04-23 21:12:16 +00:00
if ( m_ServerlistType = = IServerBrowser : : TYPE_LAN )
2021-04-21 14:54:42 +00:00
{
SetInfo ( pEntry , * pInfo ) ;
2020-09-26 19:41:58 +00:00
pEntry - > m_Info . m_Latency = minimum ( static_cast < int > ( ( time_get ( ) - m_BroadcastTime ) * 1000 / time_freq ( ) ) , 999 ) ;
2021-04-23 21:12:16 +00:00
if ( pInfo - > m_Type = = SERVERINFO_VANILLA & & Is64Player ( pInfo ) )
{
pEntry - > m_Request64Legacy = true ;
// Force a quick update.
RequestImpl64 ( pEntry - > m_Addr , pEntry ) ;
}
}
2020-09-26 19:41:58 +00:00
else if ( pEntry - > m_RequestTime > 0 )
2010-05-29 07:25:38 +00:00
{
2021-04-23 21:12:16 +00:00
if ( ! pEntry - > m_RequestIgnoreInfo )
{
SetInfo ( pEntry , * pInfo ) ;
}
2021-04-21 14:54:42 +00:00
int Latency = minimum ( static_cast < int > ( ( time_get ( ) - pEntry - > m_RequestTime ) * 1000 / time_freq ( ) ) , 999 ) ;
if ( ! pEntry - > m_RequestIgnoreInfo )
{
pEntry - > m_Info . m_Latency = Latency ;
}
else
{
char aAddr [ NETADDR_MAXSTRSIZE ] ;
net_addr_str ( & Addr , aAddr , sizeof ( aAddr ) , true ) ;
dbg_msg ( " serverbrowse/dbg " , " received ping response from %s " , aAddr ) ;
SetLatency ( Addr , Latency ) ;
}
2020-09-02 23:51:33 +00:00
pEntry - > m_RequestTime = - 1 ; // Request has been answered
2021-04-23 21:12:16 +00:00
if ( ! pEntry - > m_RequestIgnoreInfo )
{
if ( pInfo - > m_Type = = SERVERINFO_VANILLA & & Is64Player ( pInfo ) )
{
pEntry - > m_Request64Legacy = true ;
// Force a quick update.
RequestImpl64 ( pEntry - > m_Addr , pEntry ) ;
}
}
2010-05-29 07:25:38 +00:00
}
2020-09-02 23:51:33 +00:00
RemoveRequest ( pEntry ) ;
2010-05-29 07:25:38 +00:00
}
2020-12-22 10:13:51 +00:00
m_SortOnNextUpdate = true ;
2010-05-29 07:25:38 +00:00
}
void CServerBrowser : : Refresh ( int Type )
{
2021-07-08 17:18:01 +00:00
bool ServerListTypeChanged = m_ServerlistType ! = Type ;
int OldServerListType = m_ServerlistType ;
2010-05-29 07:25:38 +00:00
m_ServerlistType = Type ;
2020-09-03 00:03:22 +00:00
secure_random_fill ( m_aTokenSeed , sizeof ( m_aTokenSeed ) ) ;
2010-05-29 07:25:38 +00:00
2021-07-08 17:18:01 +00:00
if ( Type = = IServerBrowser : : TYPE_LAN | | ( ServerListTypeChanged & & OldServerListType = = IServerBrowser : : TYPE_LAN ) )
CleanUp ( ) ;
2010-05-29 07:25:38 +00:00
if ( Type = = IServerBrowser : : TYPE_LAN )
{
2020-09-26 19:41:58 +00:00
unsigned char Buffer [ sizeof ( SERVERBROWSE_GETINFO ) + 1 ] ;
2010-05-29 07:25:38 +00:00
CNetChunk Packet ;
int i ;
2011-03-28 18:11:28 +00:00
/* do the broadcast version */
2010-05-29 07:25:38 +00:00
Packet . m_ClientID = - 1 ;
mem_zero ( & Packet , sizeof ( Packet ) ) ;
2020-09-26 19:41:58 +00:00
Packet . m_Address . type = m_pNetClient - > NetType ( ) | NETTYPE_LINK_BROADCAST ;
Packet . m_Flags = NETSENDFLAG_CONNLESS | NETSENDFLAG_EXTENDED ;
2010-05-29 07:25:38 +00:00
Packet . m_DataSize = sizeof ( Buffer ) ;
Packet . m_pData = Buffer ;
2017-03-29 10:56:13 +00:00
mem_zero ( & Packet . m_aExtraData , sizeof ( Packet . m_aExtraData ) ) ;
2017-10-14 19:58:23 +00:00
int Token = GenerateToken ( Packet . m_Address ) ;
mem_copy ( Buffer , SERVERBROWSE_GETINFO , sizeof ( SERVERBROWSE_GETINFO ) ) ;
Buffer [ sizeof ( SERVERBROWSE_GETINFO ) ] = GetBasicToken ( Token ) ;
Packet . m_aExtraData [ 0 ] = GetExtraToken ( Token ) > > 8 ;
Packet . m_aExtraData [ 1 ] = GetExtraToken ( Token ) & 0xff ;
2010-05-29 07:25:38 +00:00
m_BroadcastTime = time_get ( ) ;
for ( i = 8303 ; i < = 8310 ; i + + )
{
Packet . m_Address . port = i ;
m_pNetClient - > Send ( & Packet ) ;
}
if ( g_Config . m_Debug )
2010-08-17 22:06:00 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " client_srvbrowse " , " broadcasting for servers " ) ;
2010-05-29 07:25:38 +00:00
}
2018-07-11 20:46:04 +00:00
else if ( Type = = IServerBrowser : : TYPE_FAVORITES | | Type = = IServerBrowser : : TYPE_INTERNET | | Type = = IServerBrowser : : TYPE_DDNET | | Type = = IServerBrowser : : TYPE_KOG )
2019-03-24 22:15:38 +00:00
{
2018-07-11 20:46:04 +00:00
m_pHttp - > Refresh ( ) ;
2021-04-21 14:54:42 +00:00
m_pPingCache - > Load ( ) ;
2018-07-11 20:46:04 +00:00
m_RefreshingHttp = true ;
2021-07-08 17:18:01 +00:00
if ( ServerListTypeChanged & & m_pHttp - > NumServers ( ) > 0 )
{
CleanUp ( ) ;
UpdateFromHttp ( ) ;
Sort ( ) ;
}
2019-03-24 22:15:38 +00:00
}
2010-05-29 07:25:38 +00:00
}
2021-06-09 15:29:06 +00:00
void CServerBrowser : : RequestImpl ( const NETADDR & Addr , CServerEntry * pEntry , int * pBasicToken , int * pToken , bool RandomToken ) const
2010-05-29 07:25:38 +00:00
{
2020-09-26 19:41:58 +00:00
unsigned char Buffer [ sizeof ( SERVERBROWSE_GETINFO ) + 1 ] ;
2010-05-29 07:25:38 +00:00
CNetChunk Packet ;
if ( g_Config . m_Debug )
{
2011-03-30 10:08:33 +00:00
char aAddrStr [ NETADDR_MAXSTRSIZE ] ;
2011-12-29 22:36:53 +00:00
net_addr_str ( & Addr , aAddrStr , sizeof ( aAddrStr ) , true ) ;
2010-08-17 22:06:00 +00:00
char aBuf [ 256 ] ;
2020-09-26 19:41:58 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " requesting server info from %s " , aAddrStr ) ;
2010-08-17 22:06:00 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " client_srvbrowse " , aBuf ) ;
2010-05-29 07:25:38 +00:00
}
2017-10-14 19:58:23 +00:00
int Token = GenerateToken ( Addr ) ;
2021-06-09 15:29:06 +00:00
if ( RandomToken )
{
int AvoidBasicToken = GetBasicToken ( Token ) ;
do
{
secure_random_fill ( & Token , sizeof ( Token ) ) ;
Token & = 0xffffff ;
} while ( GetBasicToken ( Token ) = = AvoidBasicToken ) ;
}
2021-04-23 21:12:16 +00:00
if ( pToken )
{
* pToken = Token ;
}
if ( pBasicToken )
{
* pBasicToken = GetBasicToken ( Token ) ;
}
2017-10-14 19:58:23 +00:00
2010-12-26 01:37:22 +00:00
mem_copy ( Buffer , SERVERBROWSE_GETINFO , sizeof ( SERVERBROWSE_GETINFO ) ) ;
2017-10-14 19:58:23 +00:00
Buffer [ sizeof ( SERVERBROWSE_GETINFO ) ] = GetBasicToken ( Token ) ;
2010-05-29 07:25:38 +00:00
Packet . m_ClientID = - 1 ;
Packet . m_Address = Addr ;
2020-09-26 19:41:58 +00:00
Packet . m_Flags = NETSENDFLAG_CONNLESS | NETSENDFLAG_EXTENDED ;
2010-12-26 01:37:22 +00:00
Packet . m_DataSize = sizeof ( Buffer ) ;
Packet . m_pData = Buffer ;
2017-03-29 10:56:13 +00:00
mem_zero ( & Packet . m_aExtraData , sizeof ( Packet . m_aExtraData ) ) ;
2017-10-14 19:58:23 +00:00
Packet . m_aExtraData [ 0 ] = GetExtraToken ( Token ) > > 8 ;
Packet . m_aExtraData [ 1 ] = GetExtraToken ( Token ) & 0xff ;
2011-04-13 18:37:12 +00:00
2010-05-29 07:25:38 +00:00
m_pNetClient - > Send ( & Packet ) ;
if ( pEntry )
pEntry - > m_RequestTime = time_get ( ) ;
}
2014-01-08 05:15:56 +00:00
void CServerBrowser : : RequestImpl64 ( const NETADDR & Addr , CServerEntry * pEntry ) const
{
2020-09-26 19:41:58 +00:00
unsigned char Buffer [ sizeof ( SERVERBROWSE_GETINFO_64_LEGACY ) + 1 ] ;
2014-01-08 05:15:56 +00:00
CNetChunk Packet ;
if ( g_Config . m_Debug )
{
char aAddrStr [ NETADDR_MAXSTRSIZE ] ;
net_addr_str ( & Addr , aAddrStr , sizeof ( aAddrStr ) , true ) ;
char aBuf [ 256 ] ;
2020-09-26 19:41:58 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " requesting server info 64 from %s " , aAddrStr ) ;
2014-01-08 05:15:56 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " client_srvbrowse " , aBuf ) ;
}
2017-03-29 10:56:13 +00:00
mem_copy ( Buffer , SERVERBROWSE_GETINFO_64_LEGACY , sizeof ( SERVERBROWSE_GETINFO_64_LEGACY ) ) ;
2017-10-14 19:58:23 +00:00
Buffer [ sizeof ( SERVERBROWSE_GETINFO_64_LEGACY ) ] = GetBasicToken ( GenerateToken ( Addr ) ) ;
2014-01-08 05:15:56 +00:00
Packet . m_ClientID = - 1 ;
Packet . m_Address = Addr ;
Packet . m_Flags = NETSENDFLAG_CONNLESS ;
Packet . m_DataSize = sizeof ( Buffer ) ;
Packet . m_pData = Buffer ;
m_pNetClient - > Send ( & Packet ) ;
if ( pEntry )
pEntry - > m_RequestTime = time_get ( ) ;
}
2021-06-09 15:29:06 +00:00
void CServerBrowser : : RequestCurrentServer ( const NETADDR & Addr ) const
{
RequestImpl ( Addr , nullptr , nullptr , nullptr , false ) ;
}
void CServerBrowser : : RequestCurrentServerWithRandomToken ( const NETADDR & Addr , int * pBasicToken , int * pToken ) const
2021-04-23 21:12:16 +00:00
{
2021-06-09 15:29:06 +00:00
RequestImpl ( Addr , nullptr , pBasicToken , pToken , true ) ;
2021-04-23 21:12:16 +00:00
}
void CServerBrowser : : SetCurrentServerPing ( const NETADDR & Addr , int Ping )
2010-05-29 07:25:38 +00:00
{
2021-08-20 15:16:18 +00:00
SetLatency ( Addr , minimum ( Ping , 999 ) ) ;
2010-05-29 07:25:38 +00:00
}
2021-04-21 14:54:42 +00:00
void ServerBrowserFillEstimatedLatency ( int OwnLocation , const IServerBrowserPingCache : : CEntry * pEntries , int NumEntries , int * pIndex , NETADDR Addr , CServerInfo * pInfo )
{
Addr . port = 0 ;
while ( * pIndex < NumEntries & & net_addr_comp ( & pEntries [ * pIndex ] . m_Addr , & Addr ) < 0 )
{
* pIndex + = 1 ;
}
if ( * pIndex > = NumEntries | | net_addr_comp ( & pEntries [ * pIndex ] . m_Addr , & Addr ) ! = 0 )
{
pInfo - > m_LatencyIsEstimated = true ;
pInfo - > m_Latency = CServerInfo : : EstimateLatency ( OwnLocation , pInfo - > m_Location ) ;
return ;
}
pInfo - > m_LatencyIsEstimated = false ;
pInfo - > m_Latency = pEntries [ * pIndex ] . m_Ping ;
}
2018-07-11 20:46:04 +00:00
void CServerBrowser : : UpdateFromHttp ( )
2015-07-09 00:08:14 +00:00
{
2021-04-21 14:54:42 +00:00
const IServerBrowserPingCache : : CEntry * pPingEntries ;
int NumPingEntries ;
m_pPingCache - > GetPingCache ( & pPingEntries , & NumPingEntries ) ;
2021-04-24 16:06:42 +00:00
int OwnLocation ;
if ( str_comp ( g_Config . m_BrLocation , " auto " ) = = 0 )
{
OwnLocation = m_OwnLocation ;
}
else
{
if ( CServerInfo : : ParseLocation ( & OwnLocation , g_Config . m_BrLocation ) )
{
char aBuf [ 64 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " cannot parse br_location: '%s' " , g_Config . m_BrLocation ) ;
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " serverbrowse " , aBuf ) ;
}
}
2021-04-21 14:54:42 +00:00
2018-07-11 20:46:04 +00:00
int NumServers = m_pHttp - > NumServers ( ) ;
int NumLegacyServers = m_pHttp - > NumLegacyServers ( ) ;
if ( m_ServerlistType ! = IServerBrowser : : TYPE_INTERNET )
2010-05-29 07:25:38 +00:00
{
2021-05-12 19:06:08 +00:00
class CWantedAddr
{
public :
NETADDR m_Addr ;
bool m_FallbackToPing ;
bool m_Got ;
} ;
std : : vector < CWantedAddr > aWantedAddresses ;
2018-07-11 20:46:04 +00:00
int LegacySetType ;
if ( m_ServerlistType = = IServerBrowser : : TYPE_FAVORITES )
2013-12-31 01:34:33 +00:00
{
2018-07-11 20:46:04 +00:00
for ( int i = 0 ; i < m_NumFavoriteServers ; i + + )
2014-12-14 15:45:18 +00:00
{
2021-05-12 19:06:08 +00:00
aWantedAddresses . push_back ( CWantedAddr { m_aFavoriteServers [ i ] , m_aFavoriteServersAllowPing [ i ] , false } ) ;
2014-12-14 15:45:18 +00:00
}
2018-07-11 20:46:04 +00:00
LegacySetType = IServerBrowser : : SET_FAV_ADD ;
2013-12-31 01:34:33 +00:00
}
2018-07-11 20:46:04 +00:00
else
2020-09-26 19:41:58 +00:00
{
2018-07-11 20:46:04 +00:00
int Network ;
char * pExcludeCountries ;
char * pExcludeTypes ;
switch ( m_ServerlistType )
2015-07-09 00:08:14 +00:00
{
2018-07-11 20:46:04 +00:00
case IServerBrowser : : TYPE_DDNET :
Network = NETWORK_DDNET ;
LegacySetType = IServerBrowser : : SET_DDNET_ADD ;
pExcludeCountries = g_Config . m_BrFilterExcludeCountries ;
pExcludeTypes = g_Config . m_BrFilterExcludeTypes ;
break ;
case IServerBrowser : : TYPE_KOG :
Network = NETWORK_KOG ;
LegacySetType = IServerBrowser : : SET_KOG_ADD ;
pExcludeCountries = g_Config . m_BrFilterExcludeCountriesKoG ;
pExcludeTypes = g_Config . m_BrFilterExcludeTypesKoG ;
break ;
default :
dbg_assert ( 0 , " invalid network " ) ;
return ;
2013-12-31 01:34:33 +00:00
}
2018-07-11 20:46:04 +00:00
// remove unknown elements of exclude list
CountryFilterClean ( Network ) ;
TypeFilterClean ( Network ) ;
2010-05-29 07:25:38 +00:00
2018-07-11 20:46:04 +00:00
int MaxServers = 0 ;
for ( int i = 0 ; i < m_aNetworks [ Network ] . m_NumCountries ; i + + )
{
CNetworkCountry * pCntr = & m_aNetworks [ Network ] . m_aCountries [ i ] ;
MaxServers = maximum ( MaxServers , pCntr - > m_NumServers ) ;
}
2010-05-29 07:25:38 +00:00
2018-07-11 20:46:04 +00:00
for ( int g = 0 ; g < MaxServers ; g + + )
{
for ( int i = 0 ; i < m_aNetworks [ Network ] . m_NumCountries ; i + + )
{
CNetworkCountry * pCntr = & m_aNetworks [ Network ] . m_aCountries [ i ] ;
// check for filter
if ( DDNetFiltered ( pExcludeCountries , pCntr - > m_aName ) )
continue ;
if ( g > = pCntr - > m_NumServers )
continue ;
if ( DDNetFiltered ( pExcludeTypes , pCntr - > m_aTypes [ g ] ) )
continue ;
2021-05-12 19:06:08 +00:00
aWantedAddresses . push_back ( CWantedAddr { pCntr - > m_aServers [ g ] , false , false } ) ;
2018-07-11 20:46:04 +00:00
}
}
2010-05-29 07:25:38 +00:00
}
2018-07-11 20:46:04 +00:00
std : : vector < int > aSortedServers ;
std : : vector < int > aSortedLegacyServers ;
2021-05-13 23:25:59 +00:00
aSortedServers . reserve ( NumServers ) ;
2018-07-11 20:46:04 +00:00
for ( int i = 0 ; i < NumServers ; i + + )
2013-12-31 02:18:37 +00:00
{
2018-07-11 20:46:04 +00:00
aSortedServers . push_back ( i ) ;
2013-12-31 02:18:37 +00:00
}
2021-05-13 23:25:59 +00:00
aSortedLegacyServers . reserve ( NumLegacyServers ) ;
2018-07-11 20:46:04 +00:00
for ( int i = 0 ; i < NumLegacyServers ; i + + )
{
aSortedLegacyServers . push_back ( i ) ;
}
class CWantedAddrComparer
{
public :
2021-05-12 19:06:08 +00:00
bool operator ( ) ( const CWantedAddr & a , const CWantedAddr & b )
2018-07-11 20:46:04 +00:00
{
2021-05-12 19:06:08 +00:00
return net_addr_comp ( & a . m_Addr , & b . m_Addr ) < 0 ;
2018-07-11 20:46:04 +00:00
}
} ;
class CAddrComparer
{
public :
IServerBrowserHttp * m_pHttp ;
bool operator ( ) ( int i , int j )
{
return net_addr_comp ( & m_pHttp - > ServerAddress ( i ) , & m_pHttp - > ServerAddress ( j ) ) < 0 ;
}
} ;
class CLegacyAddrComparer
2020-09-26 19:41:58 +00:00
{
2018-07-11 20:46:04 +00:00
public :
IServerBrowserHttp * m_pHttp ;
bool operator ( ) ( int i , int j )
{
return net_addr_comp ( & m_pHttp - > LegacyServer ( i ) , & m_pHttp - > LegacyServer ( j ) ) < 0 ;
}
} ;
std : : sort ( aWantedAddresses . begin ( ) , aWantedAddresses . end ( ) , CWantedAddrComparer ( ) ) ;
std : : sort ( aSortedServers . begin ( ) , aSortedServers . end ( ) , CAddrComparer { m_pHttp } ) ;
std : : sort ( aSortedLegacyServers . begin ( ) , aSortedLegacyServers . end ( ) , CLegacyAddrComparer { m_pHttp } ) ;
unsigned i = 0 ;
unsigned j = 0 ;
2021-04-21 14:54:42 +00:00
int p = 0 ;
2018-07-11 20:46:04 +00:00
while ( i < aWantedAddresses . size ( ) & & j < aSortedServers . size ( ) )
{
2021-05-12 19:06:08 +00:00
int Cmp = net_addr_comp ( & aWantedAddresses [ i ] . m_Addr , & m_pHttp - > ServerAddress ( aSortedServers [ j ] ) ) ;
2018-07-11 20:46:04 +00:00
if ( Cmp ! = 0 )
{
if ( Cmp < 0 )
{
i + + ;
}
else
{
j + + ;
}
2020-09-26 19:41:58 +00:00
continue ;
2018-07-11 20:46:04 +00:00
}
2021-05-12 19:06:08 +00:00
aWantedAddresses [ i ] . m_Got = true ;
2018-07-11 20:46:04 +00:00
NETADDR Addr ;
CServerInfo Info ;
m_pHttp - > Server ( aSortedServers [ j ] , & Addr , & Info ) ;
2021-04-21 14:54:42 +00:00
ServerBrowserFillEstimatedLatency ( OwnLocation , pPingEntries , NumPingEntries , & p , Addr , & Info ) ;
2018-07-11 20:46:04 +00:00
Info . m_HasRank = HasRank ( Info . m_aMap ) ;
Set ( Addr , IServerBrowser : : SET_HTTPINFO , - 1 , & Info ) ;
i + + ;
j + + ;
}
i = 0 ;
j = 0 ;
while ( i < aWantedAddresses . size ( ) & & j < aSortedLegacyServers . size ( ) )
{
2021-05-12 19:06:08 +00:00
int Cmp = net_addr_comp ( & aWantedAddresses [ i ] . m_Addr , & m_pHttp - > LegacyServer ( aSortedLegacyServers [ j ] ) ) ;
2018-07-11 20:46:04 +00:00
if ( Cmp ! = 0 )
2015-07-09 00:08:14 +00:00
{
2018-07-11 20:46:04 +00:00
if ( Cmp < 0 )
{
i + + ;
}
else
{
j + + ;
}
continue ;
2014-01-03 15:14:41 +00:00
}
2021-05-12 19:06:08 +00:00
aWantedAddresses [ i ] . m_Got = true ;
2018-07-11 20:46:04 +00:00
Set ( m_pHttp - > LegacyServer ( aSortedLegacyServers [ j ] ) , LegacySetType , - 1 , nullptr ) ;
i + + ;
j + + ;
2020-09-26 19:41:58 +00:00
}
2021-05-12 19:06:08 +00:00
for ( const CWantedAddr & Wanted : aWantedAddresses )
{
if ( ! Wanted . m_Got )
{
if ( Wanted . m_FallbackToPing )
{
Set ( Wanted . m_Addr , LegacySetType , - 1 , nullptr ) ;
}
else
{
// Also add favorites we're not allowed to ping.
if ( LegacySetType = = IServerBrowser : : SET_FAV_ADD & & ! Find ( Wanted . m_Addr ) )
{
Add ( Wanted . m_Addr ) ;
}
}
}
}
2018-07-11 20:46:04 +00:00
return ;
}
2021-04-21 14:54:42 +00:00
int p = 0 ;
2018-07-11 20:46:04 +00:00
for ( int i = 0 ; i < NumServers ; i + + )
{
NETADDR Addr ;
CServerInfo Info ;
m_pHttp - > Server ( i , & Addr , & Info ) ;
2021-04-21 14:54:42 +00:00
ServerBrowserFillEstimatedLatency ( OwnLocation , pPingEntries , NumPingEntries , & p , Addr , & Info ) ;
2018-07-11 20:46:04 +00:00
Info . m_HasRank = HasRank ( Info . m_aMap ) ;
Set ( Addr , IServerBrowser : : SET_HTTPINFO , - 1 , & Info ) ;
}
for ( int i = 0 ; i < NumLegacyServers ; i + + )
{
NETADDR Addr = m_pHttp - > LegacyServer ( i ) ;
Set ( Addr , IServerBrowser : : SET_MASTER_ADD , - 1 , nullptr ) ;
2014-01-03 15:14:41 +00:00
}
2018-07-11 20:46:04 +00:00
}
2021-07-08 17:18:01 +00:00
void CServerBrowser : : CleanUp ( )
{
// clear out everything
m_ServerlistHeap . Reset ( ) ;
m_NumServers = 0 ;
m_NumSortedServers = 0 ;
mem_zero ( m_aServerlistIp , sizeof ( m_aServerlistIp ) ) ;
m_pFirstReqServer = 0 ;
m_pLastReqServer = 0 ;
m_NumRequests = 0 ;
m_CurrentMaxRequests = g_Config . m_BrMaxRequests ;
}
2018-07-11 20:46:04 +00:00
void CServerBrowser : : Update ( bool ForceResort )
{
2021-06-23 05:05:49 +00:00
int64_t Timeout = time_freq ( ) ;
int64_t Now = time_get ( ) ;
2018-07-11 20:46:04 +00:00
2021-05-13 00:46:28 +00:00
const char * pHttpBestUrl ;
if ( ! m_pHttp - > GetBestUrl ( & pHttpBestUrl ) & & pHttpBestUrl ! = m_pHttpPrevBestUrl )
{
str_copy ( g_Config . m_BrCachedBestServerinfoUrl , pHttpBestUrl , sizeof ( g_Config . m_BrCachedBestServerinfoUrl ) ) ;
m_pHttpPrevBestUrl = pHttpBestUrl ;
}
2018-07-11 20:46:04 +00:00
m_pHttp - > Update ( ) ;
if ( m_ServerlistType ! = TYPE_LAN & & m_RefreshingHttp & & ! m_pHttp - > IsRefreshing ( ) )
2010-05-29 07:25:38 +00:00
{
2018-07-11 20:46:04 +00:00
m_RefreshingHttp = false ;
2021-07-08 17:18:01 +00:00
CleanUp ( ) ;
2018-07-11 20:46:04 +00:00
UpdateFromHttp ( ) ;
// TODO: move this somewhere else
2021-07-08 17:18:01 +00:00
Sort ( ) ;
2018-07-11 20:46:04 +00:00
return ;
2010-05-29 07:25:38 +00:00
}
2018-07-11 20:46:04 +00:00
CServerEntry * pEntry = m_pFirstReqServer ;
int Count = 0 ;
2010-05-29 07:25:38 +00:00
while ( 1 )
{
if ( ! pEntry ) // no more entries
break ;
2020-09-26 19:41:58 +00:00
if ( pEntry - > m_RequestTime & & pEntry - > m_RequestTime + Timeout < Now )
2013-12-31 01:34:33 +00:00
{
pEntry = pEntry - > m_pNextReq ;
continue ;
}
2020-09-17 15:41:13 +00:00
// no more than 10 concurrent requests
2013-12-31 01:34:33 +00:00
if ( Count = = m_CurrentMaxRequests )
2010-05-29 07:25:38 +00:00
break ;
if ( pEntry - > m_RequestTime = = 0 )
2014-01-08 05:15:56 +00:00
{
2020-09-26 19:41:58 +00:00
if ( pEntry - > m_Request64Legacy )
2014-01-11 20:38:50 +00:00
RequestImpl64 ( pEntry - > m_Addr , pEntry ) ;
2014-01-14 20:40:55 +00:00
else
2021-06-09 15:29:06 +00:00
RequestImpl ( pEntry - > m_Addr , pEntry , nullptr , nullptr , false ) ;
2014-01-08 05:15:56 +00:00
}
2010-05-29 07:25:38 +00:00
Count + + ;
pEntry = pEntry - > m_pNextReq ;
}
2015-07-09 00:08:14 +00:00
2013-12-31 01:34:33 +00:00
if ( m_pFirstReqServer & & Count = = 0 & & m_CurrentMaxRequests > 1 ) //NO More current Server Requests
{
//reset old ones
pEntry = m_pFirstReqServer ;
while ( 1 )
{
if ( ! pEntry ) // no more entries
break ;
2015-07-09 00:08:14 +00:00
pEntry - > m_RequestTime = 0 ;
pEntry = pEntry - > m_pNextReq ;
2013-12-31 01:34:33 +00:00
}
2015-07-09 00:08:14 +00:00
2013-12-31 01:34:33 +00:00
//update max-requests
2020-09-26 19:41:58 +00:00
m_CurrentMaxRequests = m_CurrentMaxRequests / 2 ;
2013-12-31 02:18:37 +00:00
if ( m_CurrentMaxRequests < 1 )
m_CurrentMaxRequests = 1 ;
2013-12-31 01:34:33 +00:00
}
else if ( Count = = 0 & & m_CurrentMaxRequests = = 1 ) //we reached the limit, just release all left requests. IF a server sends us a packet, a new request will be added automatically, so we can delete all
2015-07-09 00:08:14 +00:00
{
2013-12-31 01:34:33 +00:00
pEntry = m_pFirstReqServer ;
while ( 1 )
{
if ( ! pEntry ) // no more entries
2015-07-09 00:08:14 +00:00
break ;
2018-07-11 20:46:04 +00:00
CServerEntry * pNext = pEntry - > m_pNextReq ;
2020-09-26 19:41:58 +00:00
RemoveRequest ( pEntry ) ; //release request
2013-12-31 01:34:33 +00:00
pEntry = pNext ;
2014-01-03 15:14:41 +00:00
}
}
2015-07-09 00:08:14 +00:00
2010-05-29 07:25:38 +00:00
// check if we need to resort
2020-12-22 10:13:51 +00:00
if ( m_Sorthash ! = SortHash ( ) | | ForceResort | | m_SortOnNextUpdate )
{
2010-05-29 07:25:38 +00:00
Sort ( ) ;
2020-12-22 10:13:51 +00:00
m_SortOnNextUpdate = false ;
}
2010-05-29 07:25:38 +00:00
}
2021-05-12 19:06:08 +00:00
int CServerBrowser : : FindFavorite ( const NETADDR & Addr ) const
2010-05-29 07:25:38 +00:00
{
// search for the address
2021-05-12 19:06:08 +00:00
for ( int i = 0 ; i < m_NumFavoriteServers ; i + + )
2010-05-29 07:25:38 +00:00
{
if ( net_addr_comp ( & Addr , & m_aFavoriteServers [ i ] ) = = 0 )
2021-05-12 19:06:08 +00:00
return i ;
2010-05-29 07:25:38 +00:00
}
2021-05-12 19:06:08 +00:00
return - 1 ;
}
bool CServerBrowser : : GotInfo ( const NETADDR & Addr ) const
{
CServerEntry * pEntry = ( ( CServerBrowser * ) this ) - > Find ( Addr ) ;
return pEntry & & pEntry - > m_GotInfo ;
}
bool CServerBrowser : : IsFavorite ( const NETADDR & Addr ) const
{
return FindFavorite ( Addr ) > = 0 ;
}
bool CServerBrowser : : IsFavoritePingAllowed ( const NETADDR & Addr ) const
{
int i = FindFavorite ( Addr ) ;
dbg_assert ( i > = 0 , " invalid favorite " ) ;
return i > = 0 & & m_aFavoriteServersAllowPing [ i ] ;
2010-05-29 07:25:38 +00:00
}
void CServerBrowser : : AddFavorite ( const NETADDR & Addr )
{
CServerEntry * pEntry ;
if ( m_NumFavoriteServers = = MAX_FAVORITES )
return ;
// make sure that we don't already have the server in our list
2021-05-12 19:06:08 +00:00
if ( IsFavorite ( Addr ) )
2010-05-29 07:25:38 +00:00
{
2021-05-12 19:06:08 +00:00
return ;
2010-05-29 07:25:38 +00:00
}
// add the server to the list
2021-05-12 19:06:08 +00:00
m_aFavoriteServers [ m_NumFavoriteServers ] = Addr ;
m_aFavoriteServersAllowPing [ m_NumFavoriteServers ] = false ;
m_NumFavoriteServers + + ;
2010-05-29 07:25:38 +00:00
pEntry = Find ( Addr ) ;
if ( pEntry )
2019-03-19 06:46:48 +00:00
pEntry - > m_Info . m_Favorite = true ;
2010-05-29 07:25:38 +00:00
2011-04-13 18:37:12 +00:00
if ( g_Config . m_Debug )
2010-08-17 22:06:00 +00:00
{
2011-03-30 10:08:33 +00:00
char aAddrStr [ NETADDR_MAXSTRSIZE ] ;
2011-12-29 22:36:53 +00:00
net_addr_str ( & Addr , aAddrStr , sizeof ( aAddrStr ) , true ) ;
2010-08-17 22:06:00 +00:00
char aBuf [ 256 ] ;
2011-03-30 10:08:33 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " added fav, %s " , aAddrStr ) ;
2010-08-17 22:06:00 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_DEBUG , " client_srvbrowse " , aBuf ) ;
}
2010-05-29 07:25:38 +00:00
}
2021-05-12 19:06:08 +00:00
void CServerBrowser : : FavoriteAllowPing ( const NETADDR & Addr , bool AllowPing )
2010-05-29 07:25:38 +00:00
{
2021-05-12 19:06:08 +00:00
int i = FindFavorite ( Addr ) ;
dbg_assert ( i > = 0 , " invalid favorite " ) ;
m_aFavoriteServersAllowPing [ i ] = AllowPing ;
}
2010-05-29 07:25:38 +00:00
2021-05-12 19:06:08 +00:00
void CServerBrowser : : RemoveFavorite ( const NETADDR & Addr )
{
int i = FindFavorite ( Addr ) ;
if ( i < 0 )
2010-05-29 07:25:38 +00:00
{
2021-05-12 19:06:08 +00:00
return ;
2010-05-29 07:25:38 +00:00
}
2021-05-12 19:06:08 +00:00
mem_move ( & m_aFavoriteServers [ i ] , & m_aFavoriteServers [ i + 1 ] , sizeof ( NETADDR ) * ( m_NumFavoriteServers - ( i + 1 ) ) ) ;
mem_move ( & m_aFavoriteServersAllowPing [ i ] , & m_aFavoriteServersAllowPing [ i + 1 ] , sizeof ( bool ) * ( m_NumFavoriteServers - ( i + 1 ) ) ) ;
m_NumFavoriteServers - - ;
CServerEntry * pEntry = Find ( Addr ) ;
if ( pEntry )
pEntry - > m_Info . m_Favorite = false ;
2010-05-29 07:25:38 +00:00
}
2017-08-30 19:34:01 +00:00
void CServerBrowser : : LoadDDNetServers ( )
2014-09-13 14:36:25 +00:00
{
2020-09-26 19:41:58 +00:00
if ( ! m_pDDNetInfo )
2017-09-03 15:36:51 +00:00
return ;
2014-09-18 14:13:06 +00:00
// reset servers / countries
2020-09-26 19:41:58 +00:00
for ( int Network = 0 ; Network < NUM_NETWORKS ; Network + + )
2017-09-03 15:36:51 +00:00
{
2019-03-24 22:15:38 +00:00
CNetwork * pNet = & m_aNetworks [ Network ] ;
2015-07-09 00:08:14 +00:00
2019-03-24 22:15:38 +00:00
// parse JSON
const json_value * pServers = json_object_get ( m_pDDNetInfo , Network = = NETWORK_DDNET ? " servers " : " servers-kog " ) ;
2014-09-13 14:36:25 +00:00
2020-09-26 19:41:58 +00:00
if ( ! pServers | | pServers - > type ! = json_array )
2019-03-24 22:15:38 +00:00
return ;
2014-09-13 14:36:25 +00:00
2019-03-24 22:15:38 +00:00
pNet - > m_NumCountries = 0 ;
pNet - > m_NumTypes = 0 ;
2017-09-03 15:36:51 +00:00
2020-09-26 19:41:58 +00:00
for ( int i = 0 ; i < json_array_length ( pServers ) & & pNet - > m_NumCountries < MAX_COUNTRIES ; i + + )
2014-09-13 14:36:25 +00:00
{
2019-03-24 22:15:38 +00:00
// pSrv - { name, flagId, servers }
const json_value * pSrv = json_array_get ( pServers , i ) ;
const json_value * pTypes = json_object_get ( pSrv , " servers " ) ;
const json_value * pName = json_object_get ( pSrv , " name " ) ;
const json_value * pFlagID = json_object_get ( pSrv , " flagId " ) ;
2017-07-08 11:38:27 +00:00
2020-09-26 19:41:58 +00:00
if ( pSrv - > type ! = json_object | | pTypes - > type ! = json_object | | pName - > type ! = json_string | | pFlagID - > type ! = json_integer )
2014-09-13 14:36:25 +00:00
{
2017-07-08 11:38:27 +00:00
dbg_msg ( " client_srvbrowse " , " invalid attributes " ) ;
continue ;
}
2014-09-19 21:52:09 +00:00
2019-03-24 22:15:38 +00:00
// build structure
CNetworkCountry * pCntr = & pNet - > m_aCountries [ pNet - > m_NumCountries ] ;
pCntr - > Reset ( ) ;
str_copy ( pCntr - > m_aName , json_string_get ( pName ) , sizeof ( pCntr - > m_aName ) ) ;
pCntr - > m_FlagID = json_int_get ( pFlagID ) ;
// add country
2020-09-26 19:41:58 +00:00
for ( unsigned int t = 0 ; t < pTypes - > u . object . length ; t + + )
2017-07-08 11:38:27 +00:00
{
2019-03-24 22:15:38 +00:00
const char * pType = pTypes - > u . object . values [ t ] . name ;
const json_value * pAddrs = pTypes - > u . object . values [ t ] . value ;
2020-09-26 19:41:58 +00:00
if ( pAddrs - > type ! = json_array )
2014-09-18 14:13:06 +00:00
{
2019-03-24 22:15:38 +00:00
dbg_msg ( " client_srvbrowse " , " invalid attributes " ) ;
continue ;
2014-09-18 14:13:06 +00:00
}
2019-03-24 22:15:38 +00:00
// add type
if ( json_array_length ( pAddrs ) > 0 & & pNet - > m_NumTypes < MAX_TYPES )
2017-07-08 11:38:27 +00:00
{
2019-03-24 22:15:38 +00:00
int Pos ;
for ( Pos = 0 ; Pos < pNet - > m_NumTypes ; Pos + + )
{
if ( ! str_comp ( pNet - > m_aTypes [ Pos ] , pType ) )
break ;
}
if ( Pos = = pNet - > m_NumTypes )
{
str_copy ( pNet - > m_aTypes [ pNet - > m_NumTypes ] , pType , sizeof ( pNet - > m_aTypes [ pNet - > m_NumTypes ] ) ) ;
pNet - > m_NumTypes + + ;
}
2017-07-08 11:38:27 +00:00
}
2014-12-02 12:36:27 +00:00
2019-03-24 22:15:38 +00:00
// add addresses
2020-09-26 19:41:58 +00:00
for ( int g = 0 ; g < json_array_length ( pAddrs ) ; g + + , pCntr - > m_NumServers + + )
2017-09-03 15:36:51 +00:00
{
2019-03-24 22:15:38 +00:00
const json_value * pAddr = json_array_get ( pAddrs , g ) ;
2020-09-26 19:41:58 +00:00
if ( pAddr - > type ! = json_string )
2019-03-24 22:15:38 +00:00
{
dbg_msg ( " client_srvbrowse " , " invalid attributes " ) ;
continue ;
}
const char * pStr = json_string_get ( pAddr ) ;
net_addr_from_str ( & pCntr - > m_aServers [ pCntr - > m_NumServers ] , pStr ) ;
str_copy ( pCntr - > m_aTypes [ pCntr - > m_NumServers ] , pType , sizeof ( pCntr - > m_aTypes [ pCntr - > m_NumServers ] ) ) ;
2017-09-03 15:36:51 +00:00
}
}
2017-07-08 11:38:27 +00:00
2019-03-24 22:15:38 +00:00
pNet - > m_NumCountries + + ;
}
2017-09-03 15:36:51 +00:00
}
2014-09-13 14:36:25 +00:00
}
2019-03-19 06:57:09 +00:00
void CServerBrowser : : RecheckOfficial ( )
{
2020-10-26 14:14:07 +00:00
for ( auto & Network : m_aNetworks )
2019-03-19 06:57:09 +00:00
{
2020-10-26 14:14:07 +00:00
for ( int i = 0 ; i < Network . m_NumCountries ; i + + )
2019-03-19 06:57:09 +00:00
{
2020-10-26 14:14:07 +00:00
CNetworkCountry * pCntr = & Network . m_aCountries [ i ] ;
2019-03-24 22:15:38 +00:00
for ( int j = 0 ; j < pCntr - > m_NumServers ; j + + )
2019-03-19 06:57:09 +00:00
{
2019-03-24 22:15:38 +00:00
CServerEntry * pEntry = Find ( pCntr - > m_aServers [ j ] ) ;
if ( pEntry )
{
pEntry - > m_Info . m_Official = true ;
}
2019-03-19 06:57:09 +00:00
}
}
}
}
2017-08-30 19:34:01 +00:00
void CServerBrowser : : LoadDDNetRanks ( )
{
2017-09-03 15:36:51 +00:00
for ( int i = 0 ; i < m_NumServers ; i + + )
{
if ( m_ppServerlist [ i ] - > m_Info . m_aMap [ 0 ] )
m_ppServerlist [ i ] - > m_Info . m_HasRank = HasRank ( m_ppServerlist [ i ] - > m_Info . m_aMap ) ;
}
}
int CServerBrowser : : HasRank ( const char * pMap )
{
if ( m_ServerlistType ! = IServerBrowser : : TYPE_DDNET | | ! m_pDDNetInfo )
return - 1 ;
const json_value * pDDNetRanks = json_object_get ( m_pDDNetInfo , " maps " ) ;
if ( ! pDDNetRanks | | pDDNetRanks - > type ! = json_array )
return - 1 ;
2020-09-26 19:41:58 +00:00
for ( int i = 0 ; i < json_array_length ( pDDNetRanks ) ; i + + )
2017-09-03 15:36:51 +00:00
{
const json_value * pJson = json_array_get ( pDDNetRanks , i ) ;
if ( ! pJson | | pJson - > type ! = json_string )
continue ;
const char * pStr = json_string_get ( pJson ) ;
if ( str_comp ( pMap , pStr ) = = 0 )
return 1 ;
}
return 0 ;
}
void CServerBrowser : : LoadDDNetInfoJson ( )
{
2021-12-17 20:58:28 +00:00
IOHANDLE File = m_pStorage - > OpenFile ( DDNET_INFO , IOFLAG_READ | IOFLAG_SKIP_BOM , IStorage : : TYPE_SAVE ) ;
2017-08-30 19:34:01 +00:00
if ( ! File )
return ;
const int Length = io_length ( File ) ;
2017-08-30 22:07:59 +00:00
if ( Length < = 0 )
{
io_close ( File ) ;
return ;
}
2018-04-09 09:56:39 +00:00
char * pBuf = ( char * ) malloc ( Length ) ;
2017-08-30 19:34:01 +00:00
pBuf [ 0 ] = ' \0 ' ;
io_read ( File , pBuf , Length ) ;
io_close ( File ) ;
2017-09-03 15:36:51 +00:00
if ( m_pDDNetInfo )
json_value_free ( m_pDDNetInfo ) ;
m_pDDNetInfo = json_parse ( pBuf , Length ) ;
2017-08-30 19:34:01 +00:00
2018-04-09 09:56:39 +00:00
free ( pBuf ) ;
2017-08-30 19:34:01 +00:00
2017-09-20 20:25:55 +00:00
if ( m_pDDNetInfo & & m_pDDNetInfo - > type ! = json_object )
2017-08-30 19:34:01 +00:00
{
2017-09-03 15:36:51 +00:00
json_value_free ( m_pDDNetInfo ) ;
m_pDDNetInfo = 0 ;
2017-08-30 19:34:01 +00:00
}
2021-04-24 16:06:42 +00:00
m_OwnLocation = CServerInfo : : LOC_UNKNOWN ;
2021-05-13 23:25:59 +00:00
if ( m_pDDNetInfo )
2021-04-24 16:06:42 +00:00
{
2021-05-13 23:25:59 +00:00
const json_value & Location = ( * m_pDDNetInfo ) [ " location " ] ;
if ( Location . type ! = json_string | | CServerInfo : : ParseLocation ( & m_OwnLocation , Location ) )
{
char aBuf [ 64 ] ;
2021-06-21 12:51:45 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " cannot parse location from info.json: '%s' " , ( const char * ) Location ) ;
2021-05-13 23:25:59 +00:00
m_pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " serverbrowse " , aBuf ) ;
}
2021-04-24 16:06:42 +00:00
}
2017-08-30 19:34:01 +00:00
}
2017-09-03 15:36:51 +00:00
const json_value * CServerBrowser : : LoadDDNetInfo ( )
2017-08-30 19:34:01 +00:00
{
2017-09-03 15:36:51 +00:00
LoadDDNetInfoJson ( ) ;
LoadDDNetServers ( ) ;
2017-08-30 19:34:01 +00:00
2018-07-11 20:46:04 +00:00
RecheckOfficial ( ) ;
LoadDDNetRanks ( ) ;
2017-08-30 19:34:01 +00:00
2017-09-03 15:36:51 +00:00
return m_pDDNetInfo ;
2017-08-30 19:34:01 +00:00
}
2010-11-17 11:43:24 +00:00
bool CServerBrowser : : IsRefreshing ( ) const
{
return m_pFirstReqServer ! = 0 ;
}
2010-05-29 07:25:38 +00:00
2018-07-11 20:46:04 +00:00
bool CServerBrowser : : IsGettingServerlist ( ) const
2010-05-29 07:25:38 +00:00
{
2018-07-11 20:46:04 +00:00
return m_pHttp - > IsRefreshing ( ) ;
2010-05-29 07:25:38 +00:00
}
2010-10-30 16:56:57 +00:00
int CServerBrowser : : LoadingProgression ( ) const
{
if ( m_NumServers = = 0 )
return 0 ;
2011-04-13 18:37:12 +00:00
2010-10-30 16:56:57 +00:00
int Servers = m_NumServers ;
2020-09-26 19:41:58 +00:00
int Loaded = m_NumServers - m_NumRequests ;
return 100.0f * Loaded / Servers ;
2010-10-30 16:56:57 +00:00
}
2021-01-10 12:47:07 +00:00
void CServerBrowser : : ConfigSaveCallback ( IConfigManager * pConfigManager , void * pUserData )
2010-05-29 07:25:38 +00:00
{
CServerBrowser * pSelf = ( CServerBrowser * ) pUserData ;
char aAddrStr [ 128 ] ;
char aBuffer [ 256 ] ;
2011-12-29 22:36:53 +00:00
for ( int i = 0 ; i < pSelf - > m_NumFavoriteServers ; i + + )
2010-05-29 07:25:38 +00:00
{
2011-12-29 22:36:53 +00:00
net_addr_str ( & pSelf - > m_aFavoriteServers [ i ] , aAddrStr , sizeof ( aAddrStr ) , true ) ;
2021-05-12 19:06:08 +00:00
if ( ! pSelf - > m_aFavoriteServersAllowPing [ i ] )
{
str_format ( aBuffer , sizeof ( aBuffer ) , " add_favorite %s " , aAddrStr ) ;
}
else
{
// Add quotes to the first parameter for backward
// compatibility with versions that took a `r` console
// parameter.
str_format ( aBuffer , sizeof ( aBuffer ) , " add_favorite \" %s \" allow_ping " , aAddrStr ) ;
}
2021-01-10 12:47:07 +00:00
pConfigManager - > WriteLine ( aBuffer ) ;
2010-05-29 07:25:38 +00:00
}
}
2014-09-19 21:52:09 +00:00
2014-12-14 15:45:18 +00:00
void CServerBrowser : : DDNetFilterAdd ( char * pFilter , const char * pName )
2014-09-19 21:52:09 +00:00
{
2020-09-26 19:41:58 +00:00
if ( DDNetFiltered ( pFilter , pName ) )
2014-09-19 21:52:09 +00:00
return ;
2015-07-09 00:08:14 +00:00
2014-12-14 15:45:18 +00:00
char aBuf [ 128 ] ;
2014-09-19 21:52:09 +00:00
str_format ( aBuf , sizeof ( aBuf ) , " ,%s " , pName ) ;
2014-12-14 15:45:18 +00:00
str_append ( pFilter , aBuf , 128 ) ;
2014-09-19 21:52:09 +00:00
}
2014-12-14 15:45:18 +00:00
void CServerBrowser : : DDNetFilterRem ( char * pFilter , const char * pName )
2014-09-19 21:52:09 +00:00
{
2020-09-26 19:41:58 +00:00
if ( ! DDNetFiltered ( pFilter , pName ) )
2014-09-19 21:52:09 +00:00
return ;
// rewrite exclude/filter list
2014-12-14 15:45:18 +00:00
char aBuf [ 128 ] ;
2014-09-19 21:52:09 +00:00
2014-12-14 15:45:18 +00:00
str_copy ( aBuf , pFilter , sizeof ( aBuf ) ) ;
pFilter [ 0 ] = ' \0 ' ;
2014-09-19 21:52:09 +00:00
2019-03-05 09:46:29 +00:00
char aToken [ 128 ] ;
2019-03-11 11:54:31 +00:00
for ( const char * tok = aBuf ; ( tok = str_next_token ( tok , " , " , aToken , sizeof ( aToken ) ) ) ; )
2014-09-19 21:52:09 +00:00
{
2019-03-05 09:46:29 +00:00
if ( str_comp_nocase ( pName , aToken ) ! = 0 )
2014-09-19 21:52:09 +00:00
{
2014-12-14 15:45:18 +00:00
char aBuf2 [ 128 ] ;
2019-03-05 09:46:29 +00:00
str_format ( aBuf2 , sizeof ( aBuf2 ) , " ,%s " , aToken ) ;
2014-12-14 15:45:18 +00:00
str_append ( pFilter , aBuf2 , 128 ) ;
2014-09-19 21:52:09 +00:00
}
2019-03-11 11:39:54 +00:00
}
2014-09-19 21:52:09 +00:00
}
2014-12-14 15:45:18 +00:00
bool CServerBrowser : : DDNetFiltered ( char * pFilter , const char * pName )
2014-09-19 21:52:09 +00:00
{
2019-03-05 09:46:29 +00:00
return str_in_list ( pFilter , " , " , pName ) ; // country not excluded
2014-09-19 21:52:09 +00:00
}
2019-03-24 22:15:38 +00:00
void CServerBrowser : : CountryFilterClean ( int Network )
2014-09-19 21:52:09 +00:00
{
2019-03-24 22:15:38 +00:00
char * pExcludeCountries = Network = = NETWORK_KOG ? g_Config . m_BrFilterExcludeCountriesKoG : g_Config . m_BrFilterExcludeCountries ;
2014-12-14 15:45:18 +00:00
char aNewList [ 128 ] ;
2016-05-19 13:54:52 +00:00
aNewList [ 0 ] = ' \0 ' ;
2015-07-09 00:08:14 +00:00
2020-10-26 14:14:07 +00:00
for ( auto & Network : m_aNetworks )
2014-09-19 21:52:09 +00:00
{
2020-10-26 14:14:07 +00:00
for ( int i = 0 ; i < Network . m_NumCountries ; i + + )
2014-09-19 21:52:09 +00:00
{
2020-10-26 14:14:07 +00:00
const char * pName = Network . m_aCountries [ i ] . m_aName ;
2019-03-24 22:15:38 +00:00
if ( DDNetFiltered ( pExcludeCountries , pName ) )
{
char aBuf [ 128 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " ,%s " , pName ) ;
str_append ( aNewList , aBuf , sizeof ( aNewList ) ) ;
}
2014-09-19 21:52:09 +00:00
}
}
2019-03-24 22:15:38 +00:00
str_copy ( pExcludeCountries , aNewList , sizeof ( g_Config . m_BrFilterExcludeCountries ) ) ;
2014-09-19 21:52:09 +00:00
}
2014-12-14 15:45:18 +00:00
2019-03-24 22:15:38 +00:00
void CServerBrowser : : TypeFilterClean ( int Network )
2014-12-14 15:45:18 +00:00
{
2019-03-24 22:15:38 +00:00
char * pExcludeTypes = Network = = NETWORK_KOG ? g_Config . m_BrFilterExcludeTypesKoG : g_Config . m_BrFilterExcludeTypes ;
2014-12-14 15:45:18 +00:00
char aNewList [ 128 ] ;
2016-05-19 13:54:52 +00:00
aNewList [ 0 ] = ' \0 ' ;
2015-07-09 00:08:14 +00:00
2019-03-24 22:15:38 +00:00
for ( int i = 0 ; i < m_aNetworks [ Network ] . m_NumTypes ; i + + )
2014-12-14 15:45:18 +00:00
{
2019-03-24 22:15:38 +00:00
const char * pName = m_aNetworks [ Network ] . m_aTypes [ i ] ;
if ( DDNetFiltered ( pExcludeTypes , pName ) )
2014-12-14 15:45:18 +00:00
{
char aBuf [ 128 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " ,%s " , pName ) ;
str_append ( aNewList , aBuf , sizeof ( aNewList ) ) ;
}
}
2019-03-24 22:15:38 +00:00
str_copy ( pExcludeTypes , aNewList , sizeof ( g_Config . m_BrFilterExcludeTypes ) ) ;
2014-12-14 15:45:18 +00:00
}
2018-07-11 20:46:04 +00:00
int CServerInfo : : EstimateLatency ( int Loc1 , int Loc2 )
{
if ( Loc1 = = LOC_UNKNOWN | | Loc2 = = LOC_UNKNOWN )
{
return 999 ;
}
if ( Loc1 ! = Loc2 )
{
2021-05-27 19:45:12 +00:00
return 199 ;
2018-07-11 20:46:04 +00:00
}
2021-05-27 19:45:12 +00:00
return 99 ;
2018-07-11 20:46:04 +00:00
}
bool CServerInfo : : ParseLocation ( int * pResult , const char * pString )
{
* pResult = LOC_UNKNOWN ;
int Length = str_length ( pString ) ;
if ( Length < 2 )
{
return true ;
}
// ISO continent code. Allow antarctica, but treat it as unknown.
2021-04-24 13:48:27 +00:00
static const char LOCATIONS [ ] [ 6 ] = {
2018-07-11 20:46:04 +00:00
" an " , // LOC_UNKNOWN
" af " , // LOC_AFRICA
" as " , // LOC_ASIA
" oc " , // LOC_AUSTRALIA
" eu " , // LOC_EUROPE
" na " , // LOC_NORTH_AMERICA
" sa " , // LOC_SOUTH_AMERICA
2021-04-24 13:48:27 +00:00
" as:cn " , // LOC_CHINA
2018-07-11 20:46:04 +00:00
} ;
2021-04-24 13:48:27 +00:00
for ( int i = sizeof ( LOCATIONS ) / sizeof ( LOCATIONS [ 0 ] ) - 1 ; i > = 0 ; i - - )
2018-07-11 20:46:04 +00:00
{
2021-04-24 13:48:27 +00:00
if ( str_startswith ( pString , LOCATIONS [ i ] ) )
2018-07-11 20:46:04 +00:00
{
* pResult = i ;
return false ;
}
}
return true ;
}
bool IsVanilla ( const CServerInfo * pInfo )
{
return ! str_comp ( pInfo - > m_aGameType , " DM " ) | | ! str_comp ( pInfo - > m_aGameType , " TDM " ) | | ! str_comp ( pInfo - > m_aGameType , " CTF " ) ;
}
bool IsCatch ( const CServerInfo * pInfo )
{
return str_find_nocase ( pInfo - > m_aGameType , " catch " ) ;
}
bool IsInsta ( const CServerInfo * pInfo )
{
return str_find_nocase ( pInfo - > m_aGameType , " idm " ) | | str_find_nocase ( pInfo - > m_aGameType , " itdm " ) | | str_find_nocase ( pInfo - > m_aGameType , " ictf " ) ;
}
bool IsFNG ( const CServerInfo * pInfo )
{
return str_find_nocase ( pInfo - > m_aGameType , " fng " ) ;
}
bool IsRace ( const CServerInfo * pInfo )
{
return str_find_nocase ( pInfo - > m_aGameType , " race " ) | | str_find_nocase ( pInfo - > m_aGameType , " fastcap " ) ;
}
bool IsFastCap ( const CServerInfo * pInfo )
{
return str_find_nocase ( pInfo - > m_aGameType , " fastcap " ) ;
}
bool IsBlockInfectionZ ( const CServerInfo * pInfo )
{
return str_find_nocase ( pInfo - > m_aGameType , " blockz " ) | |
str_find_nocase ( pInfo - > m_aGameType , " infectionz " ) ;
}
bool IsBlockWorlds ( const CServerInfo * pInfo )
{
return ( str_comp_nocase_num ( pInfo - > m_aGameType , " bw " , 4 ) = = 0 ) | | ( str_comp_nocase ( pInfo - > m_aGameType , " bw " ) = = 0 ) ;
}
bool IsCity ( const CServerInfo * pInfo )
{
return str_find_nocase ( pInfo - > m_aGameType , " city " ) ;
}
bool IsDDRace ( const CServerInfo * pInfo )
{
return str_find_nocase ( pInfo - > m_aGameType , " ddrace " ) | | str_find_nocase ( pInfo - > m_aGameType , " mkrace " ) ;
}
bool IsDDNet ( const CServerInfo * pInfo )
{
return str_find_nocase ( pInfo - > m_aGameType , " ddracenet " ) | | str_find_nocase ( pInfo - > m_aGameType , " ddnet " ) ;
}
// other
bool Is64Player ( const CServerInfo * pInfo )
{
return str_find ( pInfo - > m_aGameType , " 64 " ) | | str_find ( pInfo - > m_aName , " 64 " ) | | IsDDNet ( pInfo ) ;
}
bool IsPlus ( const CServerInfo * pInfo )
{
return str_find ( pInfo - > m_aGameType , " + " ) ;
}