csgo-2018-source/matchmaking/ds_searcher.cpp
2021-07-24 21:11:47 -07:00

869 lines
25 KiB
C++

//===== Copyright © 1996-2009, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include "mm_framework.h"
#include "fmtstr.h"
#include "vstdlib/random.h"
#include "protocol.h"
#include "proto_oob.h"
#include "bitbuf.h"
#include "checksum_crc.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
ConVar mm_dedicated_allow( "mm_dedicated_allow", "1", FCVAR_DEVELOPMENTONLY, "1 = allow searches for dedicated servers" );
ConVar mm_dedicated_fake( "mm_dedicated_fake", "0", FCVAR_DEVELOPMENTONLY, "1 = pretend like search is going, but abort after some time" );
ConVar mm_dedicated_force_servers( "mm_dedicated_force_servers", "", FCVAR_RELEASE,
"Comma delimited list of ip:port of servers used to search for dedicated servers instead of searching for public servers.\n"
"Use syntax `publicip1:port|privateip1:port,publicip2:port|privateip2:port` if your server is behind NAT.\n"
"If the server is behind NAT, you can specify `0.0.0.0|privateip:port` and if server port is in the list of `mm_server_search_lan_ports` its public address should be automatically detected." );
ConVar mm_dedicated_ip( "mm_dedicated_ip", "", FCVAR_DEVELOPMENTONLY, "IP address of dedicated servers to consider available" );
ConVar mm_dedicated_timeout_request( "mm_dedicated_timeout_request", "20", FCVAR_DEVELOPMENTONLY );
ConVar mm_dedicated_search_maxping( "mm_dedicated_search_maxping", IsX360() ? "200" : "150", FCVAR_RELEASE | FCVAR_ARCHIVE, "Longest preferred ping to dedicated servers for games", true, 25.f, true, 350.f );
ConVar mm_dedicated_search_maxresults( "mm_dedicated_search_maxresults", "75", FCVAR_DEVELOPMENTONLY );
extern ConVar mm_dedicated_xlsp_timeout;
CDsSearcher::CDsSearcher( KeyValues *pSettings, uint64 uiReserveCookie, IMatchSession *pMatchSession, uint64 ullCrypt ) :
m_pSettings( pSettings ),
m_uiReserveCookie( uiReserveCookie ),
m_pReserveSettings( g_pMatchFramework->GetMatchNetworkMsgController()->PackageGameDetailsForReservation( m_pSettings ) ),
m_autodelete_pReserveSettings( m_pReserveSettings ),
#ifdef _X360
m_pTitleServers( NULL ),
#elif !defined( NO_STEAM )
m_pServerListListener( NULL ),
m_nSearchPass( 0 ),
#endif
m_eState( STATE_INIT ),
m_flTimeout( 0.0f ),
m_pAsyncOperation( NULL ),
m_pMatchSession( pMatchSession ),
m_ullCrypt( ullCrypt )
{
#ifdef _X360
ZeroMemory( m_chDatacenterQuery, sizeof( m_chDatacenterQuery ) );
ZeroMemory( &m_dc, sizeof( m_dc ) );
#endif
DevMsg( "Created DS searcher\n" );
KeyValuesDumpAsDevMsg( m_pSettings );
memset( &m_Result, 0, sizeof( m_Result ) );
// Build the reservation settings
// Load test
m_bLoadTest = (m_pSettings->GetInt( "options/sv_load_test", 0) == 1);
}
CDsSearcher::~CDsSearcher()
{
;
}
void CDsSearcher::Update()
{
switch ( m_eState )
{
case STATE_INIT:
{
char const *szNetwork = m_pSettings->GetString( "system/network", "" );
char const *szServer = m_pSettings->GetString( "options/server", "listen" );
if ( m_pSettings->GetString( "server/server", NULL ) )
{
InitWithKnownServer();
}
else if ( mm_dedicated_allow.GetBool() &&
!Q_stricmp( "LIVE", szNetwork ) &&
Q_stricmp( "listen", szServer ) &&
!( g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_SETTING_NODEDICATED ) )
{
InitDedicatedSearch();
}
else
{
m_eState = STATE_FINISHED;
}
}
break;
case STATE_WAITING:
{
if ( Plat_FloatTime() > m_flTimeout )
m_eState = STATE_FINISHED;
}
break;
#ifdef _X360
case STATE_XLSP_ENUMERATE_DCS:
m_pTitleServers->Update();
if ( m_pTitleServers->IsSearchCompleted() )
Xlsp_OnEnumerateDcsCompleted();
break;
case STATE_XLSP_NEXT_DC:
Xlsp_StartNextDc();
break;
case STATE_XLSP_REQUESTING_SERVERS:
if ( Plat_FloatTime() > m_flTimeout )
{
DevWarning( "XLSP datacenter `%s` timed out.\n", m_dc.m_szGatewayName );
m_dc.Destroy();
m_eState = STATE_XLSP_NEXT_DC;
}
break;
#elif !defined( NO_STEAM )
case STATE_STEAM_REQUESTING_SERVERS:
if ( Plat_FloatTime() > m_flTimeout )
{
DevWarning( "Steam search for dedicated servers timed out.\n" );
Steam_OnDedicatedServerListFetched();
}
break;
case STATE_STEAM_NEXT_SEARCH_PASS:
Steam_SearchPass();
break;
#endif
}
}
void CDsSearcher::OnEvent( KeyValues *pEvent )
{
char const *szEvent = pEvent->GetName();
#ifdef _X360
if ( m_eState == STATE_XLSP_REQUESTING_SERVERS &&
!Q_stricmp( "M2A_SERVER_BATCH", szEvent ) )
{
void const *pData = pEvent->GetPtr( "ptr" );
int numBytes = pEvent->GetInt( "size" );
Xlsp_OnDcServerBatch( pData, numBytes );
}
#elif !defined( NO_STEAM )
szEvent;
#endif
}
void CDsSearcher::Destroy()
{
if ( m_pAsyncOperation )
{
m_pAsyncOperation->Release();
m_pAsyncOperation = NULL;
}
#ifdef _X360
switch ( m_eState )
{
case STATE_XLSP_ENUMERATE_DCS:
if ( m_pTitleServers )
m_pTitleServers->Destroy();
m_pTitleServers = NULL;
break;
case STATE_XLSP_REQUESTING_SERVERS:
case STATE_RESERVING:
m_dc.Destroy();
break;
}
#elif !defined( NO_STEAM )
if ( m_pServerListListener )
{
m_pServerListListener->Destroy();
m_pServerListListener = NULL;
}
#endif
delete this;
}
bool CDsSearcher::IsFinished()
{
return m_eState == STATE_FINISHED;
}
CDsSearcher::DsResult_t const & CDsSearcher::GetResult()
{
return m_Result;
}
void CDsSearcher::DsResult_t::CopyToServerKey( KeyValues *pKvServer, uint64 ullCrypt ) const
{
Assert( m_bDedicated );
pKvServer->SetString( "server", "dedicated" );
#ifdef _X360
pKvServer->SetString( "adrInsecure", m_szInsecureSendableServerAddress );
#elif !defined( NO_STEAM )
if ( char const *szEncrypted = MatchSession_EncryptAddressString( m_szPublicConnectionString, ullCrypt ) )
pKvServer->SetString( "adronline", szEncrypted );
else
pKvServer->SetString( "adronline", m_szPublicConnectionString );
if ( m_szPrivateConnectionString[0] )
{
if ( char const *szEncrypted = MatchSession_EncryptAddressString( m_szPrivateConnectionString, ullCrypt ) )
pKvServer->SetString( "adrlocal", szEncrypted );
else
pKvServer->SetString( "adrlocal", m_szPrivateConnectionString );
}
#endif
}
//
// Implementation
//
void CDsSearcher::InitDedicatedSearch()
{
if ( mm_dedicated_fake.GetBool() )
{
// Special fake of the search - it just spins for some time and
// pretends like it was aborted
m_flTimeout = Plat_FloatTime() + mm_dedicated_timeout_request.GetFloat();
m_eState = STATE_WAITING;
m_Result.m_bAborted = true;
return;
}
#ifdef _X360
Xlsp_EnumerateDcs();
#elif !defined( NO_STEAM )
m_flTimeout = Plat_FloatTime() + mm_dedicated_timeout_request.GetFloat();
Steam_SearchPass();
#endif
}
void CDsSearcher::InitWithKnownServer()
{
#ifdef _X360
Assert( 0 );
m_eState = STATE_FINISHED;
return;
#elif !defined( NO_STEAM )
if ( m_pSettings->GetInt( "server/reserved" ) )
{
m_Result.m_bDedicated = true;
char const *szAdrOnline = m_pSettings->GetString( "server/adronline", "" );
if ( char const *szDecrypted = MatchSession_DecryptAddressString( szAdrOnline, m_ullCrypt ) )
szAdrOnline = szDecrypted;
Q_strncpy( m_Result.m_szConnectionString, szAdrOnline, ARRAYSIZE( m_Result.m_szConnectionString ) );
Q_strncpy( m_Result.m_szPublicConnectionString, szAdrOnline, ARRAYSIZE( m_Result.m_szPublicConnectionString ) );
char const *szAdrLocal = m_pSettings->GetString( "server/adrlocal", "" );
if ( char const *szDecrypted = MatchSession_DecryptAddressString( szAdrLocal, m_ullCrypt ) )
szAdrLocal = szDecrypted;
Q_strncpy( m_Result.m_szPrivateConnectionString, szAdrLocal, ARRAYSIZE( m_Result.m_szPrivateConnectionString ) );
m_eState = STATE_FINISHED;
}
else
{
DsServer_t dsResult(
m_pSettings->GetString( "server/adronline", "" ),
m_pSettings->GetString( "server/adrlocal", "" ),
0
);
if ( char const *szDecrypted = MatchSession_DecryptAddressString( dsResult.m_szConnectionString, m_ullCrypt ) )
Q_strncpy( dsResult.m_szConnectionString, szDecrypted, ARRAYSIZE( dsResult.m_szConnectionString ) );
if ( char const *szDecrypted = MatchSession_DecryptAddressString( dsResult.m_szPrivateConnectionString, m_ullCrypt ) )
Q_strncpy( dsResult.m_szPrivateConnectionString, szDecrypted, ARRAYSIZE( dsResult.m_szPrivateConnectionString ) );
m_arrServerList.AddToTail( dsResult );
ReserveNextServer();
}
#endif
}
#ifdef _X360
void CDsSearcher::Xlsp_EnumerateDcs()
{
m_eState = STATE_XLSP_ENUMERATE_DCS;
m_pTitleServers = new CXlspTitleServers( mm_dedicated_search_maxping.GetInt(), false );
}
void CDsSearcher::Xlsp_OnEnumerateDcsCompleted()
{
DevMsg( "Xlsp_OnEnumerateDcsCompleted - analyzing QOS results...\n" );
CUtlVector< CXlspDatacenter > &arrDcs = m_pTitleServers->GetDatacenters();
m_arrDatacenters.AddMultipleToTail( arrDcs.Count(), arrDcs.Base() );
m_pTitleServers->Destroy();
m_pTitleServers = NULL;
//
// Sort and randomize the accepted results
//
m_arrDatacenters.Sort( CXlspDatacenter::Compare );
for ( int k = 0; k < m_arrDatacenters.Count() - 1; ++ k )
{
CXlspDatacenter &dc1 = m_arrDatacenters[ k ];
CXlspDatacenter &dc2 = m_arrDatacenters[ k + 1 ];
if ( dc1.m_nPingBucket == dc2.m_nPingBucket && RandomInt( 0, 1 ) )
{
CXlspDatacenter dcSwap = dc1;
dc1 = dc2;
dc2 = dcSwap;
}
}
DevMsg( "Xlsp_OnEnumerateDcsCompleted - accepted %d datacenters.\n", m_arrDatacenters.Count() );
for ( int k = 0; k < m_arrDatacenters.Count(); ++ k )
{
DevMsg( " %d. `%s`\n", k, m_arrDatacenters[k].m_szGatewayName );
}
// Prepare the datacenter query
Xlsp_PrepareDatacenterQuery();
// Go to the next datacenter
m_eState = STATE_XLSP_NEXT_DC;
}
void CDsSearcher::Xlsp_PrepareDatacenterQuery()
{
// Compute CRC of primary user's gamertag
byte bSult = RandomInt( 5, 100 );
CRC32_t crc32 = 0;
if ( IPlayerLocal * player = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() ) )
{
char const *szPlayerName = player->GetName();
crc32 = CRC32_ProcessSingleBuffer( szPlayerName, strlen( szPlayerName ) );
uint32 sult32 = bSult | ( bSult << 8 ) | ( bSult << 16 ) | ( bSult << 24 );
crc32 ^= sult32;
}
if ( !crc32 )
bSult = 0;
// Search key
static ConVarRef sv_search_key( "sv_search_key" );
char const *szPrivateKey = sv_search_key.IsValid() ? sv_search_key.GetString() : "";
if ( !*szPrivateKey )
szPrivateKey = "default";
//
// Build query
//
Q_snprintf( m_chDatacenterQuery, ARRAYSIZE( m_chDatacenterQuery ),
"\\empty\\1"
"\\private\\%s"
"\\players\\%d"
"\\slots\\%d"
"\\perm\\%s"
"\\acct\\%02x%08x",
szPrivateKey,
m_pSettings->GetInt( "members/numPlayers", 0 ),
m_pSettings->GetInt( "members/numSlots", 0 ),
m_pSettings->GetString( "system/access", "public" ),
bSult, crc32
);
DevMsg( "Datacenters query: %s\n", m_chDatacenterQuery );
}
void CDsSearcher::Xlsp_StartNextDc()
{
if ( !m_arrDatacenters.Count() )
{
m_eState = STATE_FINISHED;
return;
}
//
// Get the next datacenter off the list
//
m_dc = m_arrDatacenters.Head();
m_arrDatacenters.RemoveMultipleFromHead( 1 );
m_flTimeout = Plat_FloatTime() + mm_dedicated_xlsp_timeout.GetFloat();
DevMsg( "[XLSP] Requesting server batch from %s:%d (%d masters) - ping %d [<= %d]\n"
" ProbesXmit=%3d ProbesRecv=%3d\n"
" RttMinInMsecs=%3d RttMedInMsecs=%3d\n"
" UpBitsPerSec=%6d DnBitsPerSec=%6d\n",
m_dc.m_szGatewayName, m_dc.m_nMasterServerPortStart, m_dc.m_numMasterServers, m_dc.m_qos.wRttMedInMsecs, m_dc.m_nPingBucket,
m_dc.m_qos.cProbesXmit, m_dc.m_qos.cProbesRecv,
m_dc.m_qos.wRttMinInMsecs, m_dc.m_qos.wRttMedInMsecs,
m_dc.m_qos.dwUpBitsPerSec, m_dc.m_qos.dwDnBitsPerSec );
if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
{
m_dc.m_adrSecure = m_dc.m_xsi.inaServer;
}
else
{
//
// Resolve the secure address
//
DWORD ret = g_pMatchExtensions->GetIXOnline()->XNetServerToInAddr( m_dc.m_xsi.inaServer, g_pMatchFramework->GetMatchTitle()->GetTitleServiceID(), &m_dc.m_adrSecure );
if ( ret != ERROR_SUCCESS )
{
DevWarning( "Failed to resolve XLSP secure address (code = 0x%08X)!\n", ret );
return;
}
}
// Convert to netadr_t on a random master port
netadr_t inetAddr;
inetAddr.SetType( NA_IP );
inetAddr.SetIPAndPort( m_dc.m_adrSecure.s_addr,
m_dc.m_nMasterServerPortStart + RandomInt( 0, m_dc.m_numMasterServers - 1 ) );
//
// Prepare the request payload
//
char msg_buffer[ INetSupport::NC_MAX_ROUTABLE_PAYLOAD ];
bf_write msg( msg_buffer, sizeof( msg_buffer ) );
msg.WriteByte( A2M_GET_SERVERS_BATCH2 );
msg.WriteByte( '\n' );
msg.WriteLong( 0 ); // batch starts at 0
msg.WriteLong( m_dc.m_adrSecure.s_addr ); // datacenter's challenge
msg.WriteString( m_chDatacenterQuery ); // datacenter query
msg.WriteByte( '\n' );
g_pMatchExtensions->GetINetSupport()->SendPacket( NULL, INetSupport::NS_SOCK_CLIENT,
inetAddr, msg.GetData(), msg.GetNumBytesWritten() );
m_eState = STATE_XLSP_REQUESTING_SERVERS;
}
void CDsSearcher::Xlsp_OnDcServerBatch( void const *pData, int numBytes )
{
if ( numBytes < 8 )
return;
bf_read msg( pData, numBytes );
int nNextId = msg.ReadLong();
nNextId;
uint nChallenge = msg.ReadLong();
if ( nChallenge != m_dc.m_adrSecure.s_addr )
return;
//
// Get master server reply message or Secure Gateway name (must match request)
//
char szReply[ MAX_PATH ] = {0};
msg.ReadString( szReply, ARRAYSIZE( szReply ), true );
if ( !szReply[0] )
{
DevWarning( "XLSP master server: empty response.\n" );
m_dc.Destroy();
m_eState = STATE_XLSP_NEXT_DC;
return;
}
if ( !Q_stricmp( "##full", szReply ) )
{
DevWarning( "XLSP master server: full.\n" );
m_dc.Destroy();
m_eState = STATE_XLSP_NEXT_DC;
return;
}
if ( !Q_stricmp( "##local", szReply ) )
{
DevWarning( "XLSP master server: game is not eligible for dedicated server.\n" );
m_dc.Destroy();
m_eState = STATE_FINISHED;
return;
}
// Bypass the gateway name check if we're faking it.
if ( !CommandLine()->FindParm( "-xlsp_fake_gateway" ) )
{
if ( Q_stricmp( m_dc.m_szGatewayName, szReply ) )
{
DevWarning( "XLSP master server: wrong reply `%s`, expected gateway `%s`.\n", szReply, m_dc.m_szGatewayName );
m_dc.Destroy();
m_eState = STATE_XLSP_NEXT_DC;
return;
}
}
//
// Process all the servers in the batch
//
m_arrServerPorts.RemoveAll();
for ( ; ; )
{
uint16 nPort = msg.ReadWord();
if ( !nPort || nPort == 0xFFFF )
{
// end of list
break;
}
m_arrServerPorts.AddToTail( nPort );
}
DevWarning( "XLSP master server: returned %d servers in batch.\n", m_arrServerPorts.Count() );
// Go ahead and start reserving
ReserveNextServer();
}
#elif !defined( NO_STEAM )
void CDsSearcher::Steam_SearchPass()
{
if ( mm_dedicated_force_servers.GetString()[0] )
{
// if convar is on to force dedicated server choices, pretend we got search results of just those servers
CSplitString serverList( mm_dedicated_force_servers.GetString(), "," );
for ( int i = 0; i < serverList.Count(); i++ )
{
// Check if the specification has a private IP address
char const * adrsStrings[2] = { serverList[i], "" };
if ( char *pchDelim = strchr( serverList[i], '|' ) )
{
*( pchDelim ++ ) = 0;
adrsStrings[1] = pchDelim;
}
netadr_t adrsForced[2];
adrsForced[0].SetFromString( adrsStrings[0] );
adrsForced[1].SetFromString( adrsStrings[1] );
// Check if a locally discovered server is known with
// either public or private address that is being forced
int numServers = g_pServerManager->GetNumServers();
for ( int iServer = 0; iServer < numServers; ++ iServer )
{
IMatchServer *pMatchServer = g_pServerManager->GetServerByIndex( iServer );
if ( !pMatchServer )
continue;
KeyValues *pServerDetails = pMatchServer->GetGameDetails();
netadr_t adrsKnown[2];
char const * adrsStringsKnown[2] = { pServerDetails->GetString( "server/adronline" ), pServerDetails->GetString( "server/adrlocal" ) };
adrsKnown[0].SetFromString( adrsStringsKnown[0] );
adrsKnown[1].SetFromString( adrsStringsKnown[1] );
for ( int iAdrForced = 0; iAdrForced < ARRAYSIZE( adrsForced ); ++ iAdrForced )
{
for ( int iAdrKnown = 0; iAdrKnown < ARRAYSIZE( adrsKnown ); ++ iAdrKnown )
{
if ( adrsForced[iAdrForced].GetIPHostByteOrder() && adrsKnown[iAdrKnown].GetIPHostByteOrder() &&
adrsForced[iAdrForced].CompareAdr( adrsKnown[iAdrKnown] ) )
{
if ( !adrsForced[!iAdrForced].GetIPHostByteOrder() ) // user not forcing other address, but we know it
adrsStrings[!iAdrForced] = adrsStringsKnown[!iAdrKnown];
goto finished_server_lookup;
}
}
}
}
finished_server_lookup:
m_arrServerList.AddToTail( CDsSearcher::DsServer_t(
adrsStrings[0], adrsStrings[1],
0 ) ); // you get no accurate ping to forced servers
}
Steam_OnDedicatedServerListFetched();
return;
}
CUtlVector< MatchMakingKeyValuePair_t > filters;
filters.EnsureCapacity( 10 );
// filter by game and require empty server
filters.AddToTail( MatchMakingKeyValuePair_t( "gamedir", COM_GetModDirectory() ) );
filters.AddToTail( MatchMakingKeyValuePair_t( "noplayers", "1" ) );
// Official servers
bool bNeedOfficialServer = false;
char const *szServerType = m_pSettings->GetString( "options/server", NULL );
if ( szServerType && !Q_stricmp( szServerType, "official" ) )
{
bNeedOfficialServer = true;
filters.AddToTail( MatchMakingKeyValuePair_t( "white", "1" ) );
}
// Allow the game to extend the filters
if ( KeyValues *pExtra = g_pMMF->GetMatchTitleGameSettingsMgr()->DefineDedicatedSearchKeys( m_pSettings, bNeedOfficialServer, m_nSearchPass ) )
{
if ( !mm_dedicated_ip.GetString()[0] )
{
for ( KeyValues *val = pExtra->GetFirstValue(); val; val = val->GetNextValue() )
{
char const *szValue = val->GetString();
if ( !szValue || !*szValue )
continue;
filters.AddToTail( MatchMakingKeyValuePair_t( val->GetName(), szValue ) );
}
}
pExtra->deleteThis();
}
// Load test
if (m_bLoadTest && m_nSearchPass == 0)
{
// Add to the "gametype" filter
for ( int i=0; i < filters.Count(); i++ )
{
MatchMakingKeyValuePair_t *pKV = &(filters[i]);
if ( !Q_stricmp( pKV->m_szKey, "gametype" ) )
{
Q_strncat( pKV->m_szValue, ",sv_load_test", sizeof(pKV->m_szValue) );
break;
}
}
}
// request the server list. We will get called back at ServerResponded, ServerFailedToRespond, and RefreshComplete
m_eState = STATE_STEAM_REQUESTING_SERVERS;
m_pServerListListener = new CServerListListener( this, filters );
++m_nSearchPass;
}
CDsSearcher::CServerListListener::CServerListListener( CDsSearcher *pDsSearcher, CUtlVector< MatchMakingKeyValuePair_t > &filters ) :
m_pOuter( pDsSearcher ),
m_hRequest( NULL )
{
MatchMakingKeyValuePair_t *pFilter = filters.Base();
DevMsg( 1, "Requesting dedicated server list...\n" );
for (int i = 0; i < filters.Count(); i++)
{
DevMsg("Filter %d: %s=%s\n", i, filters.Element(i).m_szKey, filters.Element(i).m_szValue);
}
m_hRequest = steamapicontext->SteamMatchmakingServers()->RequestInternetServerList(
( AppId_t ) g_pMatchFramework->GetMatchTitle()->GetTitleID(), &pFilter,
filters.Count(), this );
}
void CDsSearcher::CServerListListener::Destroy()
{
m_pOuter = NULL;
if ( m_hRequest )
steamapicontext->SteamMatchmakingServers()->ReleaseRequest( m_hRequest );
m_hRequest = NULL;
delete this;
}
void CDsSearcher::CServerListListener::ServerResponded( HServerListRequest hReq, int iServer )
{
gameserveritem_t *pServer = steamapicontext->SteamMatchmakingServers()
->GetServerDetails( hReq, iServer );
Msg( "[MM] Server responded '%s', dist %d\n",
pServer->m_NetAdr.GetConnectionAddressString(), pServer->m_nPing );
// Check if the server IP address matches
char const *szDedicatedIp = mm_dedicated_ip.GetString();
if ( szDedicatedIp && szDedicatedIp[0] )
{
char const *szServerConnString = pServer->m_NetAdr.GetConnectionAddressString();
szServerConnString = StringAfterPrefix( szServerConnString, szDedicatedIp );
if ( !szServerConnString ||
( szServerConnString[0] &&
szServerConnString[0] != ':' ) )
{
DevMsg( " rejected dedicated server '%s' due to ip filter '%s'\n",
pServer->m_NetAdr.GetConnectionAddressString(), szDedicatedIp );
return;
}
}
// Register the dedicated server as acceptable
// netadr_t adrPublic, adrPrivate;
// adrPublic.SetFromString( pServer->m_NetAdr.GetConnectionAddressString() );
// TODO: bool bHasPrivate = FindLANServerPrivateIPByPublicIP( adrPublic, adrPrivate );
// Don't reserve servers with human players playing
int nHumanPlayers = pServer->m_nPlayers - pServer->m_nBotPlayers;
if ( nHumanPlayers > 0 )
return;
// Don't reserve servers with too high ping
if ( ( mm_dedicated_search_maxping.GetInt() > 0 ) && ( pServer->m_nPing > mm_dedicated_search_maxping.GetInt() ) )
return;
// Register the result
if ( m_pOuter )
{
// See if maybe we know about a private address for this server
char const *szPrivateAddress = "";
netadr_t adrPublic;
adrPublic.SetFromString( pServer->m_NetAdr.GetConnectionAddressString() );
XUID xuidOnline = uint64( adrPublic.GetIPNetworkByteOrder() ) | ( uint64( adrPublic.GetPort() ) << 32ull );
if ( IMatchServer *pMatchServer = g_pServerManager->GetServerByOnlineId( xuidOnline ) )
{
szPrivateAddress = pMatchServer->GetGameDetails()->GetString( "server/adrlocal" );
}
m_pOuter->m_arrServerList.AddToTail( CDsSearcher::DsServer_t(
pServer->m_NetAdr.GetConnectionAddressString(),
szPrivateAddress,
pServer->m_nPing ) );
if ( m_pOuter->m_arrServerList.Count() > mm_dedicated_search_maxresults.GetInt() ||
Plat_FloatTime() > m_pOuter->m_flTimeout )
{
steamapicontext->SteamMatchmakingServers()->CancelQuery( hReq );
}
}
}
void CDsSearcher::CServerListListener::RefreshComplete( HServerListRequest hReq, EMatchMakingServerResponse response )
{
if ( m_pOuter )
{
m_pOuter->Steam_OnDedicatedServerListFetched();
}
}
void CDsSearcher::Steam_OnDedicatedServerListFetched()
{
if ( m_bLoadTest && m_nSearchPass < 2 )
{
m_eState = STATE_INIT;
}
else
{
if ( m_pServerListListener )
{
m_pServerListListener->Destroy();
m_pServerListListener = NULL;
}
DevMsg( 1, "Dedicated server list fetched %d servers.\n", m_arrServerList.Count() );
ReserveNextServer();
}
}
#endif
void CDsSearcher::ReserveNextServer()
{
m_eState = STATE_RESERVING;
#ifdef _X360
if ( !m_arrServerPorts.Count() )
{
m_dc.Destroy();
m_eState = STATE_XLSP_NEXT_DC;
return;
}
uint16 nPort = m_arrServerPorts.Head();
m_arrServerPorts.RemoveMultipleFromHead( 1 );
netadr_t inetAddrSecure;
inetAddrSecure.SetType( NA_IP );
inetAddrSecure.SetIPAndPort( m_dc.m_adrSecure.s_addr, nPort );
netadr_t inetAddrInsecureSendable;
inetAddrInsecureSendable.SetType( NA_IP );
inetAddrInsecureSendable.SetIPAndPort( m_dc.m_xsi.inaServer.s_addr, nPort );
Q_strncpy( m_Result.m_szConnectionString, inetAddrSecure.ToString(), ARRAYSIZE( m_Result.m_szConnectionString ) );
Q_strncpy( m_Result.m_szInsecureSendableServerAddress, inetAddrInsecureSendable.ToString(), ARRAYSIZE( m_Result.m_szInsecureSendableServerAddress ) );
netadr_t addrPublic, addrPrivate;
addrPrivate.SetType( NA_NULL );
addrPublic = inetAddrSecure;
#elif !defined( NO_STEAM )
if ( !m_arrServerList.Count() )
{
if ( Plat_FloatTime() < m_flTimeout )
{
m_eState = STATE_STEAM_NEXT_SEARCH_PASS;
return;
}
else
{
m_eState = STATE_FINISHED;
return;
}
}
DsServer_t dss = m_arrServerList.Head();
m_arrServerList.RemoveMultipleFromHead( 1 );
Q_strncpy( m_Result.m_szPublicConnectionString, dss.m_szConnectionString, ARRAYSIZE( m_Result.m_szPublicConnectionString ) );
Q_strncpy( m_Result.m_szPrivateConnectionString, dss.m_szPrivateConnectionString, ARRAYSIZE( m_Result.m_szPrivateConnectionString ) );
netadr_t addrPublic, addrPrivate;
addrPublic.SetFromString( dss.m_szConnectionString );
if ( dss.m_szPrivateConnectionString[0] )
addrPrivate.SetFromString( dss.m_szPrivateConnectionString );
else
addrPrivate.SetType( NA_NULL );
#else
netadr_t addrPublic, addrPrivate;
#endif
g_pMatchExtensions->GetINetSupport()->ReserveServer( addrPublic, addrPrivate,
m_uiReserveCookie, m_pReserveSettings,
this, &m_pAsyncOperation );
}
void CDsSearcher::OnOperationFinished( IMatchAsyncOperation *pOperation )
{
Assert( pOperation == m_pAsyncOperation );
uint64 uiResult = m_pAsyncOperation->GetResult();
if ( m_pAsyncOperation->GetState() == AOS_FAILED || !uiResult )
{
ReserveNextServer();
}
else
{
m_Result.m_bDedicated = true;
char const *szReservedAddr = ( char const * )( int )uiResult;
Q_strncpy( m_Result.m_szConnectionString, szReservedAddr, ARRAYSIZE( m_Result.m_szConnectionString ) );
// If this server reservation reported number of game slots then
// force that setting into session data
uint32 numGameSlots = uint32( pOperation->GetResultExtraInfo() & 0xFF );
if ( m_pMatchSession && ( numGameSlots > 0 ) )
{
KeyValues *kvUpdate = new KeyValues( "update" );
KeyValues::AutoDelete autodelete( kvUpdate );
kvUpdate->SetInt( "update/members/numSlots", numGameSlots );
m_pMatchSession->UpdateSessionSettings( kvUpdate );
}
m_eState = STATE_FINISHED;
}
}