1470 lines
43 KiB
C++
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;
|
|
}
|
|
|
|
|
|
|