//----------------------------------------------------------------------------- // // Desc: Matchmaking classs for Jedi Academy (ID 0x4c41000b) // Generated by MatchSim // //----------------------------------------------------------------------------- #include "match.h" #ifdef _DEBUG //----------------------------------------------------------------------------- // Name: Print // Desc: Write formatted debug output //----------------------------------------------------------------------------- static VOID __cdecl Print( const WCHAR* strFormat, ... ) { const int MAX_OUTPUT_STR = 80; WCHAR strBuffer[ MAX_OUTPUT_STR ]; va_list pArglist; va_start( pArglist, strFormat ); INT iChars= wvsprintfW( strBuffer, strFormat, pArglist ); assert( iChars < MAX_OUTPUT_STR ); (VOID) iChars; // Avoid compiler warning OutputDebugStringW( L"\n*** Matchmaking: " ); OutputDebugStringW( strBuffer ); OutputDebugStringW( L"\n\n" ); va_end( pArglist ); } #endif //----------------------------------------------------------------------------- // Name: CSession // Desc: Constructor //----------------------------------------------------------------------------- CSession::CSession() { m_State = STATE_IDLE; m_hSessionTask = NULL; m_bUpdate = FALSE; m_bListening = FALSE; m_bKeyRegistered = FALSE; SetupAttributes(); } //----------------------------------------------------------------------------- // Name: ~CSession // Desc: Destructor //----------------------------------------------------------------------------- CSession::~CSession() { Reset(); } //----------------------------------------------------------------------------- // Name: Reset // Desc: Reset the Session //----------------------------------------------------------------------------- VOID CSession::Reset() { Close(); Listen( FALSE, NO_WAIT ); PurgeSessionQ( TRUE ); if( m_bKeyRegistered ) { XNetUnregisterKey( &SessionID ); m_bKeyRegistered = FALSE; } } //----------------------------------------------------------------------------- // Name: Create // Desc: Create a new matchmaking session //----------------------------------------------------------------------------- HRESULT CSession::Create() { assert( m_State == STATE_IDLE ); HRESULT hr = XOnlineMatchSessionCreate( PublicFilled, PublicOpen, PrivateFilled, PrivateOpen, NUM_ATTRIBUTES, m_Attributes, NULL, &m_hSessionTask ); if( hr == S_OK ) { m_State = STATE_CREATING; } else { #ifdef _DEBUG Print( L"Session Creation Failed with 0x%%x", hr ); #endif } return hr; } //----------------------------------------------------------------------------- // Name: Update // Desc: Update an existing session //----------------------------------------------------------------------------- HRESULT CSession::Update() { switch ( m_State ) { case STATE_IDLE: case STATE_DELETING: return E_UNEXPECTED; case STATE_CREATING: case STATE_UPDATING: // If the session is still being created or is in the process of // updating, set m_bUpdate so that another update will be done // afterwards m_bUpdate = TRUE; return S_OK; case STATE_ACTIVE: { XOnlineTaskClose( m_hSessionTask ); m_hSessionTask = NULL; HRESULT hr = XOnlineMatchSessionUpdate( SessionID, PublicFilled, PublicOpen, PrivateFilled, PrivateOpen, NUM_ATTRIBUTES, m_Attributes, NULL, &m_hSessionTask ); m_bUpdate = FALSE; // Clear update flag since we just updated if( SUCCEEDED( hr ) ) { m_State = STATE_UPDATING; } else { #ifdef _DEBUG Print( L"Session Update Failed with 0x%%x", hr ); #endif Close(); } return hr; } default: assert(0); return E_UNEXPECTED; } } //----------------------------------------------------------------------------- // Name: Delete // Desc: Delete an existing session //----------------------------------------------------------------------------- HRESULT CSession::Delete() { HRESULT hr = S_OK; switch( m_State ) { case STATE_IDLE: break; case STATE_CREATING: Close(); // Close down create task break; case STATE_UPDATING: Close(); // Close down update task Listen( FALSE ); // Send go-aways break; case STATE_DELETING: break; case STATE_ACTIVE: if( m_bListening ) { Listen( FALSE ); } else { INT iResult = XNetUnregisterKey( &SessionID ); assert( iResult == 0 ); (VOID) iResult; // Avoid compiler warning } m_bKeyRegistered = FALSE; Close(); hr = XOnlineMatchSessionDelete( SessionID, NULL, &m_hSessionTask ); if(SUCCEEDED( hr ) ) { m_State = STATE_DELETING; } else { #ifdef _DEBUG Print( L"Session Delete Failed with 0x%x", hr ); #endif } break; } return hr; } //----------------------------------------------------------------------------- // Name: ProcessStateCreateSession // Desc: Continue servicing the creation task //----------------------------------------------------------------------------- HRESULT CSession::ProcessStateCreateSession() { HRESULT hr = XOnlineTaskContinue( m_hSessionTask ); if( hr != XONLINETASK_S_RUNNING ) { if( FAILED( hr ) ) { Close(); #ifdef _DEBUG Print( L"Session Creation Failed with 0x%x", hr ); #endif } else { // Extract the new session ID and Key-Exchange Key HRESULT hrGet = XOnlineMatchSessionGetInfo( m_hSessionTask, &SessionID, &KeyExchangeKey ); assert( SUCCEEDED( hrGet ) ); (VOID)hrGet; // Avoid compiler warning m_State = STATE_ACTIVE; INT iKeyRegistered = XNetRegisterKey( &SessionID, &KeyExchangeKey ); if( iKeyRegistered == WSAENOMORE ) { #ifdef _DEBUG Print( L"Out of keys... Purging SessionQ and trying again" ); #endif // Too many keys have been registered, remove // the registered key on the session queue and try again PurgeSessionQHead(); iKeyRegistered = XNetRegisterKey( &SessionID, &KeyExchangeKey ); } assert( iKeyRegistered == NO_ERROR ); m_bKeyRegistered = ( iKeyRegistered == NO_ERROR); // Start listening for Qos probes for this new session Listen( TRUE ); // If a call to Update() was made while the session // was being created, call Update to send the new // information. if( m_bUpdate ) { hr = Update(); } } } return hr; } //----------------------------------------------------------------------------- // Name: ProcessStateUpdateSession // Desc: Continue servicing the session update task //----------------------------------------------------------------------------- HRESULT CSession::ProcessStateUpdateSession() { HRESULT hr = XOnlineTaskContinue( m_hSessionTask ); if( hr != XONLINETASK_S_RUNNING ) { if( FAILED( hr ) ) { Close(); #ifdef _DEBUG Print( L"Session Update Failed with 0x%x", hr ); #endif } else { m_State = STATE_ACTIVE; // If a call to Update() was made while the session // was already being updated, call Update to send the new // information. if( m_bUpdate ) { hr = Update(); } } } return hr; } //----------------------------------------------------------------------------- // Name: ProcessStateDeleteSession // Desc: Continue servicing the session deletion task //----------------------------------------------------------------------------- HRESULT CSession::ProcessStateDeleteSession() { HRESULT hr = XOnlineTaskContinue( m_hSessionTask ); if( hr != XONLINETASK_S_RUNNING ) { if( FAILED( hr ) ) { #ifdef _DEBUG Print( L"Session Delete Failed with 0x%%x", hr ); #endif } Close(); } return hr; } //----------------------------------------------------------------------------- // Name: ProcessStateActiveSession // Desc: Continue servicing the session task //----------------------------------------------------------------------------- HRESULT CSession::ProcessStateActiveSession() { HRESULT hr = XOnlineTaskContinue( m_hSessionTask ); if( FAILED( hr ) ) { Close(); #ifdef _DEBUG Print( L"Session Task Failed with 0x%%x", hr ); #endif } return hr; } //----------------------------------------------------------------------------- // Name: Process // Desc: Perform a unit of work (such as servicing tasks) as necessary //----------------------------------------------------------------------------- HRESULT CSession::Process() { PurgeSessionQ(); HRESULT hr; switch( m_State ) { case STATE_IDLE: hr = XONLINETASK_S_SUCCESS; break; case STATE_CREATING: hr = ProcessStateCreateSession(); break; case STATE_UPDATING: hr = ProcessStateUpdateSession(); break; case STATE_DELETING: hr = ProcessStateDeleteSession(); break; case STATE_ACTIVE: hr = ProcessStateActiveSession(); break; default: assert(0); return E_UNEXPECTED; } if( FAILED( hr ) ) { Reset(); } return hr; } //----------------------------------------------------------------------------- // Name: Close // Desc: Close down any session tasks //----------------------------------------------------------------------------- VOID CSession::Close() { if( m_hSessionTask ) { XOnlineTaskClose( m_hSessionTask); m_hSessionTask = NULL; m_State = STATE_IDLE; } m_bUpdate = FALSE; } //----------------------------------------------------------------------------- // Name: PurgeSessionQ // Desc: Cease Qos listening, and unregister, old SessionIDs //----------------------------------------------------------------------------- VOID CSession::PurgeSessionQ( BOOL fRemoveAll ) { // Cleanup any registered SessionIDs which are sending // go-away qos responses const DWORD QOS_PROBE_REJECT_TIMELIMIT = 15000; // timeout in ms const QosQEntry *pItem; // Entries in the queue are in reverse chronological order, // so we can stop once we encounter an entry that has not reached // the time limit while( ( pItem = m_SessionQosQ.Head() ) != NULL ) { if( fRemoveAll || GetTickCount() - pItem->dwStartTick >= QOS_PROBE_REJECT_TIMELIMIT ) { PurgeSessionQHead(); } else break; } } //----------------------------------------------------------------------------- // Name: PurgeSessionQHead // Desc: Cease Qos listening, and unregister, the first session on the queue //----------------------------------------------------------------------------- VOID CSession::PurgeSessionQHead() { const QosQEntry *pItem; if ( ( pItem = m_SessionQosQ.Head() ) != NULL ) { #ifdef _DEBUG Print( L"Qos listening rejection period expired for 0x%x", pItem ); #endif INT iQos = XNetQosListen( &pItem->SessionID, NULL, 0, 0, XNET_QOS_LISTEN_RELEASE ); assert( iQos == 0 ); (VOID) iQos; // If this SessionID is not in use, unregister it if( !m_bKeyRegistered || memcmp( &pItem->SessionID, &SessionID, sizeof( XNKID ) ) != 0 ) { INT iResult = XNetUnregisterKey( &pItem->SessionID ); assert( iResult == 0 ); (VOID) iResult; } m_SessionQosQ.Dequeue(); } } //----------------------------------------------------------------------------- // Name: Listen // Desc: Control Qos listening // bEnable dwBitsPerSec // ------- --------- // TRUE Bandwidth setting (zero for default) // FALSE Bandwidth setting (zero is default, NO_WAIT means shut down // immediately) //----------------------------------------------------------------------------- VOID CSession::Listen( BOOL bEnable, DWORD dwBitsPerSec ) { if( bEnable ) { m_SessionQosQ.Remove( SessionID ); INT iQos = XNetQosListen( &SessionID, (BYTE *) m_QosResponse.Data, m_QosResponse.Length, dwBitsPerSec, XNET_QOS_LISTEN_ENABLE | XNET_QOS_LISTEN_SET_DATA | XNET_QOS_LISTEN_SET_BITSPERSEC ); assert( iQos == 0 ); (VOID)iQos; // Avoid compiler warning m_bListening = TRUE; } else { if( m_bListening ) { INT iQos; m_bListening = FALSE; if( dwBitsPerSec == NO_WAIT ) // Stop listening, and release Qos resources { #ifdef _DEBUG Print( L"Qos listening stopped" ); #endif iQos = XNetQosListen( &SessionID, NULL, 0, 0, XNET_QOS_LISTEN_RELEASE ); assert( iQos == 0 ); } else { // Start rejecting probes for a period of time by sending a "go away" // response. Then, after a certain period of time, actually stop // listening by releasing Qos resources m_SessionQosQ.Add( SessionID, GetTickCount() ); iQos = XNetQosListen( &SessionID, NULL, 0, dwBitsPerSec, XNET_QOS_LISTEN_DISABLE | XNET_QOS_LISTEN_SET_BITSPERSEC ); assert( iQos == 0 ); #ifdef _DEBUG Print( L"Qos probe rejection period started for 0x%x", m_SessionQosQ.Tail() ); #endif } (VOID)iQos; // Avoid compiler warning } } } //----------------------------------------------------------------------------- // Name: GetQosResponse // Desc: Return the value of the Qos Response //----------------------------------------------------------------------------- CBlob CSession::GetQosResponse() { return m_QosResponse; } //----------------------------------------------------------------------------- // Name: SetQosResponse // Desc: Set data to be sent in response to Qos probes //----------------------------------------------------------------------------- VOID CSession::SetQosResponse( CBlob Value ) { m_QosResponse = Value; if( m_bListening ) { // Call XNetQosListen to set the new value INT iQos = XNetQosListen(&SessionID, (BYTE *) m_QosResponse.Data, m_QosResponse.Length, 0, XNET_QOS_LISTEN_SET_DATA ); assert( iQos == 0 ); (VOID)iQos; // Avoid compiler warning } } //----------------------------------------------------------------------------- // Name: SetupAttributes // Desc: Initialize the m_Attributes array and related string/blob buffers //----------------------------------------------------------------------------- VOID CSession::SetupAttributes() { ZeroMemory( &m_Attributes, sizeof( m_Attributes ) ); m_Attributes[GAME_TYPE_INDEX].dwAttributeID = XATTRIB_GAME_TYPE; m_Attributes[CURRENT_MAP_INDEX].dwAttributeID = XATTRIB_CURRENT_MAP; m_Attributes[SESSION_NAME_INDEX].dwAttributeID = XATTRIB_SESSION_NAME; m_Attributes[SESSION_NAME_INDEX].info.string.lpValue = m_strSessionName; m_strSessionName[0] = L'\0'; m_Attributes[FRIENDLY_FIRE_INDEX].dwAttributeID = XATTRIB_FRIENDLY_FIRE; m_Attributes[JEDI_MASTERY_INDEX].dwAttributeID = XATTRIB_JEDI_MASTERY; m_Attributes[TOTAL_PLAYERS_INDEX].dwAttributeID = XATTRIB_TOTAL_PLAYERS; m_Attributes[SABER_ONLY_INDEX].dwAttributeID = XATTRIB_SABER_ONLY; m_Attributes[DEDICATED_INDEX].dwAttributeID = XATTRIB_DEDICATED; } //----------------------------------------------------------------------------- // Name: GetGameType // Desc: Return the value of the 'GameType' attribute //----------------------------------------------------------------------------- ULONGLONG CSession::GetGameType() { return m_Attributes[GAME_TYPE_INDEX].info.integer.qwValue; } //----------------------------------------------------------------------------- // Name: SetGameType // Desc: Set the 'GameType' attribute //----------------------------------------------------------------------------- VOID CSession::SetGameType( ULONGLONG Value ) { m_Attributes[GAME_TYPE_INDEX].info.integer.qwValue = Value; m_Attributes[GAME_TYPE_INDEX].fChanged = TRUE; } //----------------------------------------------------------------------------- // Name: GetCurrentMap // Desc: Return the value of the 'CurrentMap' attribute //----------------------------------------------------------------------------- ULONGLONG CSession::GetCurrentMap() { return m_Attributes[CURRENT_MAP_INDEX].info.integer.qwValue; } //----------------------------------------------------------------------------- // Name: SetCurrentMap // Desc: Set the 'CurrentMap' attribute //----------------------------------------------------------------------------- VOID CSession::SetCurrentMap( ULONGLONG Value ) { m_Attributes[CURRENT_MAP_INDEX].info.integer.qwValue = Value; m_Attributes[CURRENT_MAP_INDEX].fChanged = TRUE; } //----------------------------------------------------------------------------- // Name: GetSessionName // Desc: Return the value of the 'SessionName' attribute //----------------------------------------------------------------------------- const WCHAR * CSession::GetSessionName() { return m_strSessionName; } //----------------------------------------------------------------------------- // Name: SetSessionName // Desc: Set the 'SessionName' attribute //----------------------------------------------------------------------------- VOID CSession::SetSessionName( const WCHAR * Value ) { wcscpy( m_strSessionName, Value ); m_Attributes[SESSION_NAME_INDEX].fChanged = TRUE; } //----------------------------------------------------------------------------- // Name: GetFriendlyFire // Desc: Return the value of the 'FriendlyFire' attribute //----------------------------------------------------------------------------- ULONGLONG CSession::GetFriendlyFire() { return m_Attributes[FRIENDLY_FIRE_INDEX].info.integer.qwValue; } //----------------------------------------------------------------------------- // Name: SetFriendlyFire // Desc: Set the 'FriendlyFire' attribute //----------------------------------------------------------------------------- VOID CSession::SetFriendlyFire( ULONGLONG Value ) { m_Attributes[FRIENDLY_FIRE_INDEX].info.integer.qwValue = Value; m_Attributes[FRIENDLY_FIRE_INDEX].fChanged = TRUE; } //----------------------------------------------------------------------------- // Name: GetJediMastery // Desc: Return the value of the 'JediMastery' attribute //----------------------------------------------------------------------------- ULONGLONG CSession::GetJediMastery() { return m_Attributes[JEDI_MASTERY_INDEX].info.integer.qwValue; } //----------------------------------------------------------------------------- // Name: SetJediMastery // Desc: Set the 'JediMastery' attribute //----------------------------------------------------------------------------- VOID CSession::SetJediMastery( ULONGLONG Value ) { m_Attributes[JEDI_MASTERY_INDEX].info.integer.qwValue = Value; m_Attributes[JEDI_MASTERY_INDEX].fChanged = TRUE; } //----------------------------------------------------------------------------- // Name: GetTotalPlayers // Desc: Return the value of the 'TotalPlayers' attribute //----------------------------------------------------------------------------- ULONGLONG CSession::GetTotalPlayers() { return m_Attributes[TOTAL_PLAYERS_INDEX].info.integer.qwValue; } //----------------------------------------------------------------------------- // Name: SetTotalPlayers // Desc: Set the 'TotalPlayers' attribute //----------------------------------------------------------------------------- VOID CSession::SetTotalPlayers( ULONGLONG Value ) { m_Attributes[TOTAL_PLAYERS_INDEX].info.integer.qwValue = Value; m_Attributes[TOTAL_PLAYERS_INDEX].fChanged = TRUE; } //----------------------------------------------------------------------------- // Name: GetSaberOnly // Desc: Return the value of the 'SaberOnly' attribute //----------------------------------------------------------------------------- ULONGLONG CSession::GetSaberOnly() { return m_Attributes[SABER_ONLY_INDEX].info.integer.qwValue; } //----------------------------------------------------------------------------- // Name: SetSaberOnly // Desc: Set the 'SaberOnly' attribute //----------------------------------------------------------------------------- VOID CSession::SetSaberOnly( ULONGLONG Value ) { m_Attributes[SABER_ONLY_INDEX].info.integer.qwValue = Value; m_Attributes[SABER_ONLY_INDEX].fChanged = TRUE; } //----------------------------------------------------------------------------- // Name: GetDedicated // Desc: Return the value of the 'Dedicated' attribute //----------------------------------------------------------------------------- ULONGLONG CSession::GetDedicated() { return m_Attributes[DEDICATED_INDEX].info.integer.qwValue; } //----------------------------------------------------------------------------- // Name: SetDedicated // Desc: Set the 'Dedicated' attribute //----------------------------------------------------------------------------- VOID CSession::SetDedicated( ULONGLONG Value ) { m_Attributes[DEDICATED_INDEX].info.integer.qwValue = Value; m_Attributes[DEDICATED_INDEX].fChanged = TRUE; } //----------------------------------------------------------------------------- // Name: CSessionQosQ // Desc: Constructor //----------------------------------------------------------------------------- CSession::CSessionQosQ::CSessionQosQ() { m_pHead = m_pTail = NULL; } //----------------------------------------------------------------------------- // Name: Add // Desc: Add a session id to the qos go-away response queue //----------------------------------------------------------------------------- VOID CSession::CSessionQosQ::Add( XNKID & SessionID, DWORD dwStartTick ) { assert( m_pHead && m_pTail || !m_pHead && !m_pTail ); QosQEntry *pItem = new QosQEntry; pItem->SessionID = SessionID; pItem->dwStartTick = dwStartTick; pItem->pNext = NULL; if( m_pTail ) m_pTail->pNext = pItem; m_pTail = pItem; if( !m_pHead ) m_pHead = pItem; } //----------------------------------------------------------------------------- // Name: Remove // Desc: Remove a session from the qos go-away response queue //----------------------------------------------------------------------------- VOID CSession::CSessionQosQ::Remove( XNKID &SessionID ) { assert( m_pHead && m_pTail || !m_pHead && !m_pTail ); QosQEntry *pPrev = NULL; for( QosQEntry *pItem = m_pHead; pItem != NULL; pItem = pItem->pNext ) { if( memcmp(&pItem->SessionID, &SessionID, sizeof( XNKID ) ) == 0 ) { if( pPrev ) { pPrev->pNext = pItem->pNext; } else { m_pHead = pItem->pNext; } if( pItem == m_pTail ) { m_pTail = pPrev; } delete pItem; break; } pPrev = pItem; } } //----------------------------------------------------------------------------- // Name: Dequeue // Desc: Remove the head of the qos go-away response queue //----------------------------------------------------------------------------- VOID CSession::CSessionQosQ::Dequeue() { assert( m_pHead && m_pTail || !m_pHead && !m_pTail ); QosQEntry *pItem = m_pHead; if( pItem ) { m_pHead = pItem->pNext; delete pItem; if( !m_pHead ) m_pTail = NULL; } } // Attribute layout for OptiMatch return attributes. // This must match the order of both the query return attributes // and the fields specified in the COptiMatchResult class. static const XONLINE_ATTRIBUTE_SPEC OptiMatchAttributeSpec[]= { { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // GameType { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // CurrentMap { X_ATTRIBUTE_DATATYPE_STRING, ( XATTRIB_SESSION_NAME_MAX_LEN + 1 ) * sizeof( WCHAR ) }, // SessionName { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // FriendlyFire { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // JediMastery { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // SaberOnly { X_ATTRIBUTE_DATATYPE_INTEGER, sizeof( ULONGLONG ) }, // Dedicated }; //----------------------------------------------------------------------------- // Name: COptiMatchQuery // Desc: Constructor //----------------------------------------------------------------------------- COptiMatchQuery::COptiMatchQuery() { m_State = STATE_IDLE; m_hrQuery = S_FALSE; m_hSearchTask = NULL; m_pXnQos = NULL; } //----------------------------------------------------------------------------- // Name: COptiMatchQuery() // Desc: Destructor //----------------------------------------------------------------------------- COptiMatchQuery::~COptiMatchQuery() { Cancel(); } //----------------------------------------------------------------------------- // Name: Cancel // Desc: Cancel a OptiMatch query //----------------------------------------------------------------------------- void COptiMatchQuery::Cancel() { switch( m_State ) { case STATE_IDLE: break; case STATE_RUNNING: case STATE_PROBING_BANDWIDTH: case STATE_PROBING_CONNECTIVITY: case STATE_DONE: if( m_hSearchTask ) { XOnlineTaskClose( m_hSearchTask ); m_hSearchTask = NULL; } Clear(); m_State = STATE_IDLE; m_hrQuery = S_FALSE; break; default: assert(0); } } //----------------------------------------------------------------------------- // Name: Clear // Desc: Release resources //----------------------------------------------------------------------------- void COptiMatchQuery::Clear() { if( m_pXnQos ) { XNetQosRelease( m_pXnQos ); m_pXnQos = NULL; } Results.Clear(); } //----------------------------------------------------------------------------- // Name: Query // Desc: Execute the OptiMatch query (id 0x1) //----------------------------------------------------------------------------- HRESULT COptiMatchQuery::Query( ULONGLONG GameType, // Optional: X_MATCH_NULL_INTEGER to omit ULONGLONG CurrentMap, // Optional: X_MATCH_NULL_INTEGER to omit ULONGLONG MinimumPlayers, ULONGLONG MaximumPlayers, ULONGLONG FriendlyFire, // Optional: X_MATCH_NULL_INTEGER to omit ULONGLONG JediMastery, // Optional: X_MATCH_NULL_INTEGER to omit ULONGLONG SaberOnly, // Optional: X_MATCH_NULL_INTEGER to omit ULONGLONG Dedicated // Optional: X_MATCH_NULL_INTEGER to omit ) { const DWORD SEARCH_PROC_ID = 0x1; if( m_State == STATE_DONE ) // Clear existing results { Clear(); m_State = STATE_IDLE; } assert( m_State == STATE_IDLE ); if (m_State != STATE_IDLE) return E_UNEXPECTED; XONLINE_ATTRIBUTE QueryParameters[8] = { 0 }; QueryParameters[0].dwAttributeID = ( GameType == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER; QueryParameters[0].info.integer.qwValue = GameType; QueryParameters[1].dwAttributeID = ( CurrentMap == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER; QueryParameters[1].info.integer.qwValue = CurrentMap; QueryParameters[2].dwAttributeID = X_ATTRIBUTE_DATATYPE_INTEGER; QueryParameters[2].info.integer.qwValue = MinimumPlayers; QueryParameters[3].dwAttributeID = X_ATTRIBUTE_DATATYPE_INTEGER; QueryParameters[3].info.integer.qwValue = MaximumPlayers; QueryParameters[4].dwAttributeID = ( FriendlyFire == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER; QueryParameters[4].info.integer.qwValue = FriendlyFire; QueryParameters[5].dwAttributeID = ( JediMastery == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER; QueryParameters[5].info.integer.qwValue = JediMastery; QueryParameters[6].dwAttributeID = ( SaberOnly == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER; QueryParameters[6].info.integer.qwValue = SaberOnly; QueryParameters[7].dwAttributeID = ( Dedicated == X_MATCH_NULL_INTEGER ) ? X_ATTRIBUTE_DATATYPE_NULL : X_ATTRIBUTE_DATATYPE_INTEGER; QueryParameters[7].info.integer.qwValue = Dedicated; // Calculate maximum space required to hold results DWORD dwResultsLen = XOnlineMatchSearchResultsLen( MAX_OPTI_MATCH_RESULTS, sizeof( OptiMatchAttributeSpec ) / sizeof( OptiMatchAttributeSpec[0] ), OptiMatchAttributeSpec ); HRESULT hr = XOnlineMatchSearch( SEARCH_PROC_ID, MAX_OPTI_MATCH_RESULTS, 8, QueryParameters, dwResultsLen, NULL, &m_hSearchTask ); if( SUCCEEDED( hr ) ) m_State = STATE_RUNNING; return hr; } //----------------------------------------------------------------------------- // Name: Probe // Desc: Initiate bandwidth probing //----------------------------------------------------------------------------- HRESULT COptiMatchQuery::Probe() { assert( m_State == STATE_DONE ); if (m_State != STATE_DONE) return E_UNEXPECTED; // Clean up any earlier probes if( m_pXnQos ) { XNetQosRelease( m_pXnQos ); m_pXnQos = NULL; } DWORD dwNumSessions = Results.Size(); if( dwNumSessions ) { for( DWORD i = 0; i < dwNumSessions; ++i ) { Results[i].pQosInfo = NULL; m_rgpXnAddr[i] = &Results[i].HostAddress; m_rgpXnKid[i] = &Results[i].SessionID; m_rgpXnKey[i] = &Results[i].KeyExchangeKey; } INT iQos = XNetQosLookup( dwNumSessions, m_rgpXnAddr, m_rgpXnKid, m_rgpXnKey, 0, NULL, NULL, NUM_QOS_PROBES, QOS_BITS_PER_SEC, 0, NULL, &m_pXnQos ); assert( iQos == 0 ); if( iQos == 0 ) m_State = STATE_PROBING_BANDWIDTH; else return E_FAIL; } return S_OK; } //----------------------------------------------------------------------------- // Name: Process // Desc: Continue servicing the query task //----------------------------------------------------------------------------- HRESULT COptiMatchQuery::Process() { if( m_State == STATE_IDLE ) return S_OK; if( m_State == STATE_DONE ) return m_hrQuery; if( m_State == STATE_PROBING_BANDWIDTH ) { // Update any completed Qos info for sessions // as they finish for( DWORD iResult = 0; iResult < Results.Size(); ++iResult ) { if( !Results[iResult].pQosInfo && ( m_pXnQos->axnqosinfo[iResult].bFlags & XNET_XNQOSINFO_COMPLETE ) ) { Results[iResult].pQosInfo = &m_pXnQos->axnqosinfo[iResult]; } } // Check if all probing is complete if( m_pXnQos->cxnqosPending == 0 ) { m_State = STATE_DONE; return m_hrQuery; } return XONLINETASK_S_RUNNING; } if (m_State == STATE_PROBING_CONNECTIVITY ) { // Check if probing is complete if( m_pXnQos->cxnqosPending == 0 ) { // Copy QoS info into individual result objects, removing // any entries which are not reachable DWORD dwNumQosResults = Results.Size(); DWORD iResult = 0; for( DWORD iQos = 0; iQos < dwNumQosResults; ++iQos ) { if( ( m_pXnQos->axnqosinfo[iQos].bFlags & XNET_XNQOSINFO_TARGET_CONTACTED ) && !( m_pXnQos->axnqosinfo[iQos].bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) ) { Results[iResult].pQosInfo = &m_pXnQos->axnqosinfo[iQos]; iResult++; } else { #ifdef _DEBUG Print( L"Removing Matching Session %lu (host not reachable or disabled)\n", iResult ); #endif // Target not contacted, or is disabled, so remove result Results.Remove( iResult ); } } m_State = STATE_DONE; return m_hrQuery; } return XONLINETASK_S_RUNNING; } HRESULT hr = XOnlineTaskContinue( m_hSearchTask ); if( hr != XONLINETASK_S_RUNNING ) { m_hrQuery = hr; m_State = STATE_DONE; if( SUCCEEDED( hr ) ) { // Fetch results XONLINE_MATCH_SEARCHRESULT** ppSearchResults; DWORD dwNumSessions; hr= XOnlineMatchSearchGetResults( m_hSearchTask, &ppSearchResults, &dwNumSessions ); if( SUCCEEDED( hr ) ) { Results.SetSize( dwNumSessions ); for( DWORD i=0; i < dwNumSessions; ++i ) { XONLINE_MATCH_SEARCHRESULT* pxms = ppSearchResults[i]; Results.v[i].SessionID = pxms->SessionID; Results.v[i].KeyExchangeKey = pxms->KeyExchangeKey; Results.v[i].HostAddress = pxms->HostAddress; Results.v[i].PublicOpen = pxms->dwPublicOpen; Results.v[i].PrivateOpen = pxms->dwPrivateOpen; Results.v[i].PublicFilled = pxms->dwPublicFilled; Results.v[i].PrivateFilled = pxms->dwPrivateFilled; hr = XOnlineMatchSearchParse( ppSearchResults[i], sizeof( OptiMatchAttributeSpec ) / sizeof( OptiMatchAttributeSpec[0] ), OptiMatchAttributeSpec, &Results.v[i] ); assert(SUCCEEDED(hr)); // Save data for Qos probing Results.v[i].pQosInfo = NULL; m_rgpXnAddr[i] = &Results.v[i].HostAddress; m_rgpXnKid[i] = &Results.v[i].SessionID; m_rgpXnKey[i] = &Results.v[i].KeyExchangeKey; } if( dwNumSessions ) { INT iQos = XNetQosLookup( dwNumSessions, m_rgpXnAddr, m_rgpXnKid, m_rgpXnKey, 0, NULL, NULL, 0, QOS_BITS_PER_SEC, 0, NULL, &m_pXnQos ); assert( iQos == 0 ); if( iQos == 0 ) m_State = STATE_PROBING_CONNECTIVITY; } } } XOnlineTaskClose( m_hSearchTask ); m_hSearchTask = NULL; } return hr; } //----------------------------------------------------------------------------- // Name: CJoinSessionByIDQuery // Desc: Constructor //----------------------------------------------------------------------------- CJoinSessionByIDQuery::CJoinSessionByIDQuery() { m_State = STATE_IDLE; m_hrQuery = S_FALSE; m_hSearchTask = NULL; m_pXnQos = NULL; } //----------------------------------------------------------------------------- // Name: CJoinSessionByIDQuery() // Desc: Destructor //----------------------------------------------------------------------------- CJoinSessionByIDQuery::~CJoinSessionByIDQuery() { Cancel(); } //----------------------------------------------------------------------------- // Name: Cancel // Desc: Cancel a JoinSessionByID query //----------------------------------------------------------------------------- void CJoinSessionByIDQuery::Cancel() { switch( m_State ) { case STATE_IDLE: break; case STATE_RUNNING: case STATE_PROBING_BANDWIDTH: case STATE_PROBING_CONNECTIVITY: case STATE_DONE: if( m_hSearchTask ) { XOnlineTaskClose( m_hSearchTask ); m_hSearchTask = NULL; } Clear(); m_State = STATE_IDLE; m_hrQuery = S_FALSE; break; default: assert(0); } } //----------------------------------------------------------------------------- // Name: Clear // Desc: Release resources //----------------------------------------------------------------------------- void CJoinSessionByIDQuery::Clear() { if( m_pXnQos ) { XNetQosRelease( m_pXnQos ); m_pXnQos = NULL; } Results.Clear(); } //----------------------------------------------------------------------------- // Name: Query // Desc: Execute the JoinSessionByID query (id 0x2) //----------------------------------------------------------------------------- HRESULT CJoinSessionByIDQuery::Query( ULONGLONG SessionID ) { const DWORD SEARCH_PROC_ID = 0x2; if( m_State == STATE_DONE ) // Clear existing results { Clear(); m_State = STATE_IDLE; } assert( m_State == STATE_IDLE ); if (m_State != STATE_IDLE) return E_UNEXPECTED; XONLINE_ATTRIBUTE QueryParameters[1] = { 0 }; QueryParameters[0].dwAttributeID = X_ATTRIBUTE_DATATYPE_INTEGER; QueryParameters[0].info.integer.qwValue = SessionID; // Calculate maximum space required to hold results DWORD dwResultsLen = XOnlineMatchSearchResultsLen( MAX_JOIN_SESSION_BY_ID_RESULTS, 0, NULL ); HRESULT hr = XOnlineMatchSearch( SEARCH_PROC_ID, MAX_JOIN_SESSION_BY_ID_RESULTS, 1, QueryParameters, dwResultsLen, NULL, &m_hSearchTask ); if( SUCCEEDED( hr ) ) m_State = STATE_RUNNING; return hr; } //----------------------------------------------------------------------------- // Name: Probe // Desc: Initiate bandwidth probing //----------------------------------------------------------------------------- HRESULT CJoinSessionByIDQuery::Probe() { assert( m_State == STATE_DONE ); if (m_State != STATE_DONE) return E_UNEXPECTED; // Clean up any earlier probes if( m_pXnQos ) { XNetQosRelease( m_pXnQos ); m_pXnQos = NULL; } DWORD dwNumSessions = Results.Size(); if( dwNumSessions ) { for( DWORD i = 0; i < dwNumSessions; ++i ) { Results[i].pQosInfo = NULL; m_rgpXnAddr[i] = &Results[i].HostAddress; m_rgpXnKid[i] = &Results[i].SessionID; m_rgpXnKey[i] = &Results[i].KeyExchangeKey; } INT iQos = XNetQosLookup( dwNumSessions, m_rgpXnAddr, m_rgpXnKid, m_rgpXnKey, 0, NULL, NULL, NUM_QOS_PROBES, QOS_BITS_PER_SEC, 0, NULL, &m_pXnQos ); assert( iQos == 0 ); if( iQos == 0 ) m_State = STATE_PROBING_BANDWIDTH; else return E_FAIL; } return S_OK; } //----------------------------------------------------------------------------- // Name: Process // Desc: Continue servicing the query task //----------------------------------------------------------------------------- HRESULT CJoinSessionByIDQuery::Process() { if( m_State == STATE_IDLE ) return S_OK; if( m_State == STATE_DONE ) return m_hrQuery; if( m_State == STATE_PROBING_BANDWIDTH ) { // Update any completed Qos info for sessions // as they finish for( DWORD iResult = 0; iResult < Results.Size(); ++iResult ) { if( !Results[iResult].pQosInfo && ( m_pXnQos->axnqosinfo[iResult].bFlags & XNET_XNQOSINFO_COMPLETE ) ) { Results[iResult].pQosInfo = &m_pXnQos->axnqosinfo[iResult]; } } // Check if all probing is complete if( m_pXnQos->cxnqosPending == 0 ) { m_State = STATE_DONE; return m_hrQuery; } return XONLINETASK_S_RUNNING; } if (m_State == STATE_PROBING_CONNECTIVITY ) { // Check if probing is complete if( m_pXnQos->cxnqosPending == 0 ) { // Copy QoS info into individual result objects, removing // any entries which are not reachable DWORD dwNumQosResults = Results.Size(); DWORD iResult = 0; for( DWORD iQos = 0; iQos < dwNumQosResults; ++iQos ) { if( ( m_pXnQos->axnqosinfo[iQos].bFlags & XNET_XNQOSINFO_TARGET_CONTACTED ) && !( m_pXnQos->axnqosinfo[iQos].bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) ) { Results[iResult].pQosInfo = &m_pXnQos->axnqosinfo[iQos]; iResult++; } else { #ifdef _DEBUG Print( L"Removing Matching Session %lu (host not reachable or disabled)\n", iResult ); #endif // Target not contacted, or is disabled, so remove result Results.Remove( iResult ); } } m_State = STATE_DONE; return m_hrQuery; } return XONLINETASK_S_RUNNING; } HRESULT hr = XOnlineTaskContinue( m_hSearchTask ); if( hr != XONLINETASK_S_RUNNING ) { m_hrQuery = hr; m_State = STATE_DONE; if( SUCCEEDED( hr ) ) { // Fetch results XONLINE_MATCH_SEARCHRESULT** ppSearchResults; DWORD dwNumSessions; hr= XOnlineMatchSearchGetResults( m_hSearchTask, &ppSearchResults, &dwNumSessions ); if( SUCCEEDED( hr ) ) { Results.SetSize( dwNumSessions ); for( DWORD i=0; i < dwNumSessions; ++i ) { XONLINE_MATCH_SEARCHRESULT* pxms = ppSearchResults[i]; Results.v[i].SessionID = pxms->SessionID; Results.v[i].KeyExchangeKey = pxms->KeyExchangeKey; Results.v[i].HostAddress = pxms->HostAddress; Results.v[i].PublicOpen = pxms->dwPublicOpen; Results.v[i].PrivateOpen = pxms->dwPrivateOpen; Results.v[i].PublicFilled = pxms->dwPublicFilled; Results.v[i].PrivateFilled = pxms->dwPrivateFilled; // Save data for Qos probing Results.v[i].pQosInfo = NULL; m_rgpXnAddr[i] = &Results.v[i].HostAddress; m_rgpXnKid[i] = &Results.v[i].SessionID; m_rgpXnKey[i] = &Results.v[i].KeyExchangeKey; } if( dwNumSessions ) { INT iQos = XNetQosLookup( dwNumSessions, m_rgpXnAddr, m_rgpXnKid, m_rgpXnKey, 0, NULL, NULL, 0, QOS_BITS_PER_SEC, 0, NULL, &m_pXnQos ); assert( iQos == 0 ); if( iQos == 0 ) m_State = STATE_PROBING_CONNECTIVITY; } } } XOnlineTaskClose( m_hSearchTask ); m_hSearchTask = NULL; } return hr; }