|
|
//===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include "mm_framework.h"
#include "vstdlib/random.h"
#include "fmtstr.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
static ConVar mm_session_search_num_results( "mm_session_search_num_results", "10", FCVAR_DEVELOPMENTONLY ); static ConVar mm_session_search_distance( "mm_session_search_distance", "1", FCVAR_DEVELOPMENTONLY ); ConVar mm_session_search_qos_timeout( "mm_session_search_qos_timeout", "15.0", FCVAR_RELEASE ); // static ConVar mm_session_search_ping_limit( "mm_session_search_ping_limit", "200", FCVAR_RELEASE ); -- removed, using mm_dedicated_search_maxping instead
static ConVar mm_session_search_ping_buckets( "mm_session_search_ping_buckets", "4", FCVAR_RELEASE );
CMatchSearcher::CMatchSearcher( KeyValues *pSettings ) : m_pSettings( pSettings ), // takes ownership
m_autodelete_pSettings( m_pSettings ), m_pSessionSearchTree( NULL ), m_autodelete_pSessionSearchTree( m_pSessionSearchTree ), m_pSearchPass( NULL ), m_eState( STATE_INIT ) { #ifdef _X360
ZeroMemory( &m_xOverlapped, sizeof( m_xOverlapped ) ); m_pQosResults = NULL; m_pCancelOverlappedJob = NULL; #endif
DevMsg( "Created CMatchSearcher:\n" ); KeyValuesDumpAsDevMsg( m_pSettings, 1 );
InitializeSettings(); }
CMatchSearcher::~CMatchSearcher() { DevMsg( "Destroying CMatchSearcher:\n" ); KeyValuesDumpAsDevMsg( m_pSettings, 1 );
// Free all retrieved game details
CUtlVector< SearchResult_t > *arrResults[] = { &m_arrSearchResults, &m_arrSearchResultsAggregate }; for ( int ia = 0; ia < ARRAYSIZE( arrResults ); ++ ia ) { for ( int k = 0; k < arrResults[ia]->Count(); ++ k ) { SearchResult_t &sr = arrResults[ia]->Element( k ); if ( sr.m_pGameDetails ) { sr.m_pGameDetails->deleteThis(); sr.m_pGameDetails = NULL; } } } }
void CMatchSearcher::InitializeSettings() { // Initialize only the settings required to connect...
if ( KeyValues *kv = m_pSettings->FindKey( "system", true ) ) { KeyValuesAddDefaultString( kv, "network", "LIVE" ); KeyValuesAddDefaultString( kv, "access", "public" ); }
if ( KeyValues *pMembers = m_pSettings->FindKey( "members", true ) ) { int numMachines = pMembers->GetInt( "numMachines", -1 ); if ( numMachines == -1 ) { numMachines = 1; pMembers->SetInt( "numMachines", numMachines ); }
int numPlayers = pMembers->GetInt( "numPlayers", -1 ); if ( numPlayers == -1 ) { numPlayers = 1; #ifdef _GAMECONSOLE
numPlayers = XBX_GetNumGameUsers(); #endif
pMembers->SetInt( "numPlayers", numPlayers ); }
pMembers->SetInt( "numSlots", numPlayers );
KeyValues *pMachine = pMembers->FindKey( "machine0");
if ( !pMachine ) { pMachine = pMembers->CreateNewKey(); pMachine->SetName( "machine0" );
XUID machineid = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID();
pMachine->SetUint64( "id", machineid ); pMachine->SetUint64( "flags", MatchSession_GetMachineFlags() ); pMachine->SetInt( "numPlayers", numPlayers ); pMachine->SetUint64( "dlcmask", g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" ) ); pMachine->SetString( "tuver", MatchSession_GetTuInstalledString() ); pMachine->SetInt( "ping", 0 );
for ( int k = 0; k < numPlayers; ++ k ) { if ( KeyValues *pPlayer = pMachine->FindKey( CFmtStr( "player%d", k ), true ) ) { int iController = 0; #ifdef _GAMECONSOLE
iController = XBX_GetUserId( k ); #endif
IPlayerLocal *player = g_pPlayerManager->GetLocalPlayer( iController );
pPlayer->SetUint64( "xuid", player->GetXUID() ); pPlayer->SetString( "name", player->GetName() ); } } } }
DevMsg( "CMatchSearcher::InitializeGameSettings adjusted settings:\n" ); KeyValuesDumpAsDevMsg( m_pSettings, 1 ); }
KeyValues * CMatchSearcher::GetSearchSettings() { return m_pSettings; }
void CMatchSearcher::Destroy() { // Stop the search
if ( m_eState == STATE_SEARCHING ) { #ifdef _X360
m_pCancelOverlappedJob = ThreadExecute( MMX360_CancelOverlapped, &m_xOverlapped ); // UpdateDormantOperations will clean the rest
MMX360_RegisterDormant( this ); return; #endif
}
#ifdef _X360
if ( m_eState == STATE_CHECK_QOS ) { g_pMatchExtensions->GetIXOnline()->XNetQosRelease( m_pQosResults ); m_pQosResults = NULL; } #endif
#if !defined( NO_STEAM )
while ( m_arrOutstandingAsyncOperation.Count() > 0 ) { IMatchAsyncOperation *pAsyncOperation = m_arrOutstandingAsyncOperation.Tail(); m_arrOutstandingAsyncOperation.RemoveMultipleFromTail( 1 ); if ( pAsyncOperation ) pAsyncOperation->Release(); } #endif
delete this; }
void CMatchSearcher::OnSearchEvent( KeyValues *pNotify ) { g_pMatchEventsSubscription->BroadcastEvent( pNotify ); }
#ifdef _X360
bool CMatchSearcher::UpdateDormantOperation() { if ( !m_pCancelOverlappedJob->IsFinished() ) return true; // keep running dormant
m_pCancelOverlappedJob->Release(); m_pCancelOverlappedJob = NULL;
delete this; return false; // destroyed object, remove from dormant list
} #endif
void CMatchSearcher::Update() { switch ( m_eState ) { case STATE_INIT: m_eState = STATE_SEARCHING;
// Session is searching
OnSearchEvent( new KeyValues( "OnMatchSessionUpdate", "state", "progress", "progress", "searching" ) );
// Kick off the search
StartSearch(); break;
case STATE_SEARCHING: // Waiting for session search to complete
#ifdef _X360
if ( XHasOverlappedIoCompleted( &m_xOverlapped ) ) Live_OnSessionSearchCompleted(); #endif
break;
#ifdef _X360
case STATE_CHECK_QOS: // Keep checking for results or until the wait time expires
if ( Plat_FloatTime() > m_flQosTimeout || !m_pQosResults->cxnqosPending ) Live_OnQosCheckCompleted(); break; #endif
#if !defined (NO_STEAM)
case STATE_WAITING_LOBBY_DATA_AND_PING: { bool bWaitLonger = ( ( Plat_MSTime() - m_uiQosTimeoutStartMS ) < ( mm_session_search_qos_timeout.GetFloat() * 1000.f ) ); if ( bWaitLonger ) { bool bHasPendingLobbyData = false; for ( int j = 0; j < m_arrSearchResults.Count(); ++ j ) { SearchResult_t &sr = m_arrSearchResults[j]; if ( !sr.m_numPlayers ) { // didn't even receive lobby data from Steam yet
bHasPendingLobbyData = true; } else if ( sr.m_svAdr.GetIPHostByteOrder() && ( sr.m_svPing <= 0 ) ) { // received valid IP for lobby, still pinging
bHasPendingLobbyData = true; } } if ( !bHasPendingLobbyData ) bWaitLonger = false; } if ( !bWaitLonger ) { m_CallbackOnLobbyDataReceived.Unregister();
// Go ahead and start joining the results
AggregateSearchPassResults(); OnSearchPassDone( m_pSearchPass ); } } break; #endif
} }
#if !defined (NO_STEAM)
void CMatchSearcher::Steam_OnLobbyDataReceived( LobbyDataUpdate_t *pLobbyDataUpdate ) { int iResultIndex = 0; for ( ; iResultIndex < m_arrSearchResults.Count(); ++ iResultIndex ) { if ( m_arrSearchResults[ iResultIndex ].m_uiLobbyId == pLobbyDataUpdate->m_ulSteamIDLobby ) break; } if ( !m_arrSearchResults.IsValidIndex( iResultIndex ) ) return;
SearchResult_t *pRes = &m_arrSearchResults[ iResultIndex ];
if ( !pLobbyDataUpdate->m_bSuccess ) { DevMsg( "[MM] Could not get lobby data for lobby %llu (%llx)\n", pRes->m_uiLobbyId, pRes->m_uiLobbyId ); pRes->m_numPlayers = -1; // set numPlayers to negative number to indicate a failure here
} else { // Get num players
const char *numPlayers = steamapicontext->SteamMatchmaking()->GetLobbyData( pRes->m_uiLobbyId, "members:numPlayers" ); if ( !numPlayers ) { DevMsg( "[MM] Unable to get num players for lobby (%llx)\n", pRes->m_uiLobbyId ); pRes->m_numPlayers = -1; // set numPlayers to negative number to indicate a failure here
} else { pRes->m_numPlayers = V_atoi( numPlayers ); if ( !pRes->m_numPlayers ) pRes->m_numPlayers = -1; // set numPlayers to negative number to indicate a failure here
}
// Get the address of the server
const char* pServerAdr = steamapicontext->SteamMatchmaking()->GetLobbyData( pRes->m_uiLobbyId, "server:adronline" );
if ( !pServerAdr ) { // DevMsg( "[MM] Unable to get server address from lobby (%llx)\n",
// pRes->m_uiLobbyId );
} else { pRes->m_svAdr.SetFromString( pServerAdr ); DevMsg ( "[MM] Lobby %d: id %llu (%llx), num Players %d, sv ip %s, pinging dist %d\n", iResultIndex, pRes->m_uiLobbyId, pRes->m_uiLobbyId, pRes->m_numPlayers, pRes->m_svAdr.ToString(), pRes->m_svPing ); // Ping server
IMatchAsyncOperation *pAsyncOperationPing = NULL; g_pMatchExtensions->GetINetSupport()->ServerPing( pRes->m_svAdr, this, &pAsyncOperationPing ); m_arrOutstandingAsyncOperation.AddToTail( pAsyncOperationPing ); pRes->m_pAsyncOperationPingWeakRef = pAsyncOperationPing; } } }
// Callback for server reservation check
void CMatchSearcher::OnOperationFinished( IMatchAsyncOperation *pOperation ) { if ( !pOperation ) return;
if ( m_eState != STATE_WAITING_LOBBY_DATA_AND_PING ) return;
for ( int i = 0; i < m_arrSearchResults.Count(); ++ i ) { if ( m_arrSearchResults[i].m_pAsyncOperationPingWeakRef != pOperation ) continue;
int result = pOperation->GetResult(); bool failed = ( pOperation->GetState() == AOS_FAILED ); SearchResult_t *pRes = &m_arrSearchResults[i];
pRes->m_svPing = ( failed ? -1 : ( result ? result : -1 ) ); if ( pRes->m_svPing < 0 ) { DevMsg( "[MM] Failed pinging server %s for lobby#%d %llu (%llx)\n", pRes->m_svAdr.ToString(), i, pRes->m_uiLobbyId, pRes->m_uiLobbyId ); } else { DevMsg( "[MM] Successfully pinged server %s for lobby#%d %llu (%llx) = %d ms\n", pRes->m_svAdr.ToString(), i, pRes->m_uiLobbyId, pRes->m_uiLobbyId, pRes->m_svPing ); }
m_arrSearchResults[i].m_pAsyncOperationPingWeakRef = NULL; } } #endif
void CMatchSearcher::OnSearchDone() { m_eState = STATE_DONE; }
void CMatchSearcher::AggregateSearchPassResults() { #ifndef NO_STEAM
extern ConVar mm_dedicated_search_maxping; int nPingLimitMax = mm_dedicated_search_maxping.GetInt(); if ( nPingLimitMax <= 0 ) nPingLimitMax = 5000; for ( int k = 0; k < mm_session_search_ping_buckets.GetInt(); ++ k ) { int iPingLow = ( nPingLimitMax * k ) / mm_session_search_ping_buckets.GetInt(); int iPingHigh = ( nPingLimitMax * ( k + 1 ) ) / mm_session_search_ping_buckets.GetInt(); for ( int iResult = 0; iResult < m_arrSearchResults.Count(); ++ iResult ) { SearchResult_t *pRes = &m_arrSearchResults[iResult]; if ( ( pRes->m_svPing > iPingLow ) && ( pRes->m_svPing <= iPingHigh ) ) { m_arrSearchResultsAggregate.AddToTail( *pRes );
DevMsg ( "[MM] Search aggregated result%d / %d: id %llu (%llx), num Players %d, sv ip %s, dist %d\n", m_arrSearchResultsAggregate.Count(), iResult, pRes->m_uiLobbyId, pRes->m_uiLobbyId, pRes->m_numPlayers, pRes->m_svAdr.ToString(), pRes->m_svPing ); } } } DevMsg ( "[MM] Search aggregated %d results\n", m_arrSearchResultsAggregate.Count() );
#endif
m_arrSearchResults.Purge();
}
void CMatchSearcher::OnSearchPassDone( KeyValues *pSearchPass ) { // If we have enough results, then call it done
if ( m_arrSearchResultsAggregate.Count() >= mm_session_search_num_results.GetInt() ) { OnSearchDone(); return; }
// Evaluate if there is a nextpass condition
char const *szNextPassKeyName = "nextpass"; if ( KeyValues *pConditions = pSearchPass->FindKey( "nextpass?" ) ) { // Inspect conditions and select which next pass will happen
}
if ( KeyValues *pNextPass = pSearchPass->FindKey( szNextPassKeyName ) ) { StartSearchPass( pNextPass ); } else { OnSearchDone(); } }
#ifdef _X360
void CMatchSearcher::Live_OnSessionSearchCompleted() { DevMsg( "Received %d search results from Xbox LIVE.\n", GetXSearchResult()->dwSearchResults );
for( unsigned int i = 0; i < GetXSearchResult()->dwSearchResults; ++ i ) { XSESSION_SEARCHRESULT const &xsr = GetXSearchResult()->pResults[i];
SearchResult_t sr = { xsr.info, NULL }; m_arrSearchResults.AddToTail( sr );
DevMsg( 2, "Result #%02d: %llx\n", i + 1, ( const uint64& ) xsr.info.sessionID ); }
if ( !m_arrSearchResults.Count() ) { OnSearchPassDone( m_pSearchPass ); } else { DevMsg( "Checking QOS with %d search results.\n", m_arrSearchResults.Count() ); Live_CheckSearchResultsQos(); } }
void CMatchSearcher::Live_CheckSearchResultsQos() { m_eState = STATE_CHECK_QOS;
int nResults = m_arrSearchResults.Count(); CUtlVector< const void * > memQosData; memQosData.SetCount( 3 * nResults );
const void ** bufQosData[3]; for ( int k = 0; k < ARRAYSIZE( bufQosData ); ++ k ) bufQosData[k] = &memQosData[ k * nResults ];
for ( int k = 0; k < m_arrSearchResults.Count(); ++ k ) { SearchResult_t const &sr = m_arrSearchResults[k];
bufQosData[0][k] = &sr.m_info.hostAddress; bufQosData[1][k] = &sr.m_info.sessionID; bufQosData[2][k] = &sr.m_info.keyExchangeKey; }
//
// Note: XNetQosLookup requires only 2 successful probes to be received from the host.
// This is much less than the recommended 8 probes because on a 10% data loss profile
// it is impossible to find the host when requiring 8 probes to be received.
m_flQosTimeout = Plat_FloatTime() + mm_session_search_qos_timeout.GetFloat(); int res = g_pMatchExtensions->GetIXOnline()->XNetQosLookup( nResults, reinterpret_cast< XNADDR const ** >( bufQosData[0] ), reinterpret_cast< XNKID const ** >( bufQosData[1] ), reinterpret_cast< XNKEY const ** >( bufQosData[2] ), 0, // number of security gateways to probe
NULL, // gateway ip addresses
NULL, // gateway service ids
2, // number of probes
0, // upstream bandwith to use (0 = default)
0, // flags - not supported
NULL, // signal event
&m_pQosResults );// results
if ( res != 0 ) { DevWarning( "OnlineSearch::Live_CheckSearchResultsQos - XNetQosLookup failed (code = 0x%08X)!\n", res ); m_arrSearchResults.Purge(); OnSearchPassDone( m_pSearchPass ); } }
void CMatchSearcher::Live_OnQosCheckCompleted() { for ( uint k = m_pQosResults->cxnqos; k --> 0; ) { XNQOSINFO &xqi = m_pQosResults->axnqosinfo[k];
BYTE uNeedFlags = XNET_XNQOSINFO_TARGET_CONTACTED | XNET_XNQOSINFO_DATA_RECEIVED; if ( ( ( xqi.bFlags & uNeedFlags ) != uNeedFlags) || ( xqi.bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) ) { m_arrSearchResults.Remove( k ); continue; }
extern ConVar mm_dedicated_search_maxping; if ( mm_dedicated_search_maxping.GetInt() > 0 && xqi.wRttMedInMsecs > mm_dedicated_search_maxping.GetInt() ) { m_arrSearchResults.Remove( k ); continue; }
if ( xqi.cbData && xqi.pbData ) { MM_GameDetails_QOS_t gd = { xqi.pbData, xqi.cbData, xqi.wRttMedInMsecs }; Assert( !m_arrSearchResults[k].m_pGameDetails ); m_arrSearchResults[k].m_pGameDetails = g_pMatchFramework->GetMatchNetworkMsgController()->UnpackGameDetailsFromQOS( &gd ); } }
g_pMatchExtensions->GetIXOnline()->XNetQosRelease( m_pQosResults ); m_pQosResults = NULL;
// Go ahead and start joining the results
DevMsg( "Qos completed with %d search results.\n", m_arrSearchResults.Count() ); AggregateSearchPassResults(); OnSearchPassDone( m_pSearchPass ); }
#elif !defined( NO_STEAM )
void CMatchSearcher::Steam_OnLobbyMatchListReceived( LobbyMatchList_t *pLobbyMatchList, bool bError ) { Msg( "[MM] Received %d search results.\n", bError ? 0 : pLobbyMatchList->m_nLobbiesMatching );
m_CallbackOnLobbyDataReceived.Register( this, &CMatchSearcher::Steam_OnLobbyDataReceived );
// Walk through search results and request lobby data
for ( int iLobby = 0; iLobby < (int) ( bError ? 0 : pLobbyMatchList->m_nLobbiesMatching ); ++ iLobby ) { uint64 uiLobbyId = steamapicontext->SteamMatchmaking()->GetLobbyByIndex( iLobby ).ConvertToUint64();
SearchResult_t sr = { uiLobbyId }; sr.m_pGameDetails = NULL; sr.m_svAdr.SetFromString( "0.0.0.0" ); sr.m_svPing = 0; sr.m_numPlayers = 0; sr.m_pAsyncOperationPingWeakRef = NULL; m_arrSearchResults.AddToTail( sr );
steamapicontext->SteamMatchmaking()->RequestLobbyData( sr.m_uiLobbyId ); }
m_eState = STATE_WAITING_LOBBY_DATA_AND_PING; // Waiting for lobby data
m_uiQosPingLastMS = m_uiQosTimeoutStartMS = Plat_MSTime(); }
KeyValues * CMatchSearcher::SearchResult_t::GetGameDetails() const { if ( !m_pGameDetails ) { m_pGameDetails = g_pMatchFramework->GetMatchNetworkMsgController()->UnpackGameDetailsFromSteamLobby( m_uiLobbyId ); } return m_pGameDetails; }
#endif
void CMatchSearcher::StartSearch() { Assert( !m_pSessionSearchTree ); m_pSessionSearchTree = g_pMMF->GetMatchTitleGameSettingsMgr()->DefineSessionSearchKeys( m_pSettings ); m_autodelete_pSessionSearchTree.Assign( m_pSessionSearchTree );
Assert( m_pSessionSearchTree ); if ( !m_pSessionSearchTree ) { DevWarning( "OnlineSearch::StartSearch failed to build filter list!\n" ); OnSearchDone(); } else { StartSearchPass( m_pSessionSearchTree ); } }
void CMatchSearcher::StartSearchPass( KeyValues *pSearchPass ) { KeyValues *pSearchParams = pSearchPass; m_pSearchPass = pSearchPass;
// Make sure we have fresh buffer for search results
m_arrSearchResults.Purge(); m_eState = STATE_SEARCHING;
DevMsg( "OnlineSearch::StartSearchPass:\n" ); KeyValuesDumpAsDevMsg( pSearchParams, 1 );
#ifdef _X360
DWORD dwSearchRule = pSearchParams->GetInt( "rule" );
m_arrContexts.RemoveAll(); if ( KeyValues *pContexts = pSearchParams->FindKey( "Contexts" ) ) { for ( KeyValues *val = pContexts->GetFirstValue(); val; val = val->GetNextValue() ) { XUSER_CONTEXT ctx = { 0 }; ctx.dwContextId = atoi( val->GetName() ); if ( val->GetDataType() == KeyValues::TYPE_INT ) { ctx.dwValue = val->GetInt(); m_arrContexts.AddToTail( ctx ); } } }
m_arrProperties.RemoveAll(); if ( KeyValues *pContexts = pSearchParams->FindKey( "Properties" ) ) { for ( KeyValues *val = pContexts->GetFirstValue(); val; val = val->GetNextValue() ) { XUSER_PROPERTY prop = { 0 }; prop.dwPropertyId = atoi( val->GetName() );
if ( val->GetDataType() == KeyValues::TYPE_INT ) { prop.value.type = XUSER_DATA_TYPE_INT32; prop.value.nData = val->GetInt(); m_arrProperties.AddToTail( prop ); } } }
DWORD ret = ERROR_SUCCESS; DWORD numBytes = 0;
DWORD dwNumSlotsRequired = pSearchParams->GetInt( "numPlayers" );
//
// Issue the asynchrounous session search request
//
ret = g_pMatchExtensions->GetIXOnline()->XSessionSearchEx( dwSearchRule, XBX_GetPrimaryUserId(), mm_session_search_num_results.GetInt(), dwNumSlotsRequired, m_arrProperties.Count(), m_arrContexts.Count(), m_arrProperties.Base(), m_arrContexts.Base(), &numBytes, NULL, NULL );
// Log the search request to read X360 queries easier
DevMsg( "XSessionSearchEx by rule %d for slots %d\n", dwSearchRule, dwNumSlotsRequired ); for ( int k = 0; k < m_arrContexts.Count(); ++ k ) DevMsg( " CTX %u/0x%08X = 0x%X/%u\n", m_arrContexts[k].dwContextId, m_arrContexts[k].dwContextId, m_arrContexts[k].dwValue, m_arrContexts[k].dwValue ); for ( int k = 0; k < m_arrProperties.Count(); ++ k ) DevMsg( " PRP %u/0x%08X = 0x%X/%u\n", m_arrProperties[k].dwPropertyId, m_arrProperties[k].dwPropertyId, m_arrProperties[k].value.nData, m_arrProperties[k].value.nData ); DevMsg( "will use %u bytes buffer.\n", numBytes ); if ( ERROR_INSUFFICIENT_BUFFER == ret && numBytes > 0 ) { m_bufSearchResultHeader.EnsureCapacity( numBytes ); ZeroMemory( GetXSearchResult(), numBytes ); ZeroMemory( &m_xOverlapped, sizeof( m_xOverlapped ) );
DevMsg( "Searching...\n" ); ret = g_pMatchExtensions->GetIXOnline()->XSessionSearchEx( dwSearchRule, XBX_GetPrimaryUserId(), mm_session_search_num_results.GetInt(), dwNumSlotsRequired, m_arrProperties.Count(), m_arrContexts.Count(), m_arrProperties.Base(), m_arrContexts.Base(), &numBytes, GetXSearchResult(), &m_xOverlapped );
if ( ret == ERROR_IO_PENDING ) return; }
// Otherwise search failed
DevWarning( "XSessionSearchEx failed (code = 0x%08X)\n", ret ); ZeroMemory( &m_xOverlapped, sizeof( m_xOverlapped ) ); OnSearchPassDone( m_pSearchPass );
#elif !defined( NO_STEAM )
ISteamMatchmaking *mm = steamapicontext->SteamMatchmaking();
// Configure filters
DWORD dwNumSlotsRequired = pSearchParams->GetInt( "numPlayers" ); mm->AddRequestLobbyListFilterSlotsAvailable( dwNumSlotsRequired );
// Set filters
char const * arrKeys[] = { "Filter<", "Filter<=", "Filter=", "Filter<>", "Filter>=", "Filter>" }; ELobbyComparison nValueCmp[] = { k_ELobbyComparisonLessThan, k_ELobbyComparisonEqualToOrLessThan, k_ELobbyComparisonEqual, k_ELobbyComparisonNotEqual, k_ELobbyComparisonEqualToOrGreaterThan, k_ELobbyComparisonGreaterThan }; for ( int k = 0; k < ARRAYSIZE( arrKeys ); ++ k ) { if ( KeyValues *kv = pSearchParams->FindKey( arrKeys[k] ) ) { for ( KeyValues *val = kv->GetFirstValue(); val; val = val->GetNextValue() ) { if ( val->GetDataType() == KeyValues::TYPE_STRING ) { mm->AddRequestLobbyListStringFilter( val->GetName(), val->GetString(), nValueCmp[k] ); } else if ( val->GetDataType() == KeyValues::TYPE_INT ) { mm->AddRequestLobbyListNumericalFilter( val->GetName(), val->GetInt(), nValueCmp[k] ); } } } }
// Set ordering near values
if ( KeyValues *kv = pSearchParams->FindKey( "Near" ) ) { for ( KeyValues *val = kv->GetFirstValue(); val; val = val->GetNextValue() ) { if ( val->GetDataType() == KeyValues::TYPE_INT ) { mm->AddRequestLobbyListNearValueFilter( val->GetName(), val->GetInt() ); } else { // TODO: need to set near value filter for strings
} } }
ELobbyDistanceFilter eFilter = k_ELobbyDistanceFilterDefault; switch ( mm_session_search_distance.GetInt() ) { case k_ELobbyDistanceFilterClose: case k_ELobbyDistanceFilterDefault: case k_ELobbyDistanceFilterFar: case k_ELobbyDistanceFilterWorldwide: eFilter = ( ELobbyDistanceFilter ) mm_session_search_distance.GetInt(); break; } // Set distance filter to Near
mm->AddRequestLobbyListDistanceFilter( eFilter );
// Set dependent lobby
if ( uint64 uiDependentLobbyId = pSearchParams->GetUint64( "DependentLobby", 0ull ) ) { mm->AddRequestLobbyListCompatibleMembersFilter( uiDependentLobbyId ); }
// RequestLobbyList - will get called back at Steam_OnLobbyMatchListReceived
DevMsg( "Searching...\n" ); SteamAPICall_t hCall = mm->RequestLobbyList();
m_CallbackOnLobbyMatchListReceived.Set( hCall, this, &CMatchSearcher::Steam_OnLobbyMatchListReceived );
#endif
}
//
// Results retrieval overrides
//
bool CMatchSearcher::IsSearchFinished() const { return m_eState == STATE_DONE; }
int CMatchSearcher::GetNumSearchResults() const { return IsSearchFinished() ? m_arrSearchResultsAggregate.Count() : 0; }
CMatchSearcher::SearchResult_t const & CMatchSearcher::GetSearchResult( int idx ) const { if ( !IsSearchFinished() || !m_arrSearchResultsAggregate.IsValidIndex( idx ) ) { Assert( false ); static SearchResult_t s_empty; return s_empty; } else { return m_arrSearchResultsAggregate[ idx ]; } }
|