Files
Jedi-Academy/tools/jawa/match.cpp
2013-04-04 14:32:05 -07:00

1470 lines
43 KiB
C++

//-----------------------------------------------------------------------------
//
// 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;
}