|
|
//===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
//
// Purpose:
//
//===========================================================================//
#include "mm_framework.h"
#include "vstdlib/random.h"
#include "protocol.h"
#include "proto_oob.h"
#include "filesystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
ConVar mm_dedicated_xlsp_max_dcs( "mm_dedicated_xlsp_max_dcs", "25", FCVAR_DEVELOPMENTONLY, "Max number of XLSP datacenters supported" ); ConVar mm_dedicated_xlsp_force_dc( "mm_dedicated_xlsp_force_dc", "", FCVAR_DEVELOPMENTONLY, "Name of XLSP datacenter to force connection to" ); ConVar mm_dedicated_xlsp_timeout( "mm_dedicated_xlsp_timeout", "20", FCVAR_DEVELOPMENTONLY, "Timeout for XLSP operations" ); ConVar mm_dedicated_xlsp_command_timeout( "mm_dedicated_xlsp_command_timeout", "10", FCVAR_DEVELOPMENTONLY, "Timeout for XLSP command" );
ConVar mm_dedicated_xlsp_force_dc_offset( "mm_dedicated_xlsp_force_dc_offset", "0", FCVAR_DEVELOPMENTONLY, "Offset of XLSP datacenter master to debug master servers" );
static int s_nXlspConnectionCmdReplyId = 0x10000000; static int GetNextXlspConnectionCmdReplyId() { return ++ s_nXlspConnectionCmdReplyId; }
#ifdef _X360
//
// Datacenter implementation
//
static int GetBucketedRTT( int iRTT ) { static int s_latencyBucketLevels[] = { 5, 25, 50, 100, 200, 0xFFFFFF }; for ( int k = 0; k < ARRAYSIZE( s_latencyBucketLevels ); ++ k ) { if ( iRTT <= s_latencyBucketLevels[ k ] ) return s_latencyBucketLevels[ k ]; } return s_latencyBucketLevels[ ARRAYSIZE( s_latencyBucketLevels ) - 1 ]; }
int CXlspDatacenter::Compare( CXlspDatacenter const *dc1, CXlspDatacenter const *dc2 ) { int nPing1 = dc1->m_nPingBucket, nPing2 = dc2->m_nPingBucket;
if ( nPing1 != nPing2 ) return ( nPing1 < nPing2 ) ? -1 : 1; else return 0; }
bool CXlspDatacenter::ParseServerInfo() { char *pToken = V_stristr( m_xsi.szServerInfo, "**" ); if ( !pToken ) return false;
// Get our bare gateway name
int nTokenLength = pToken - m_xsi.szServerInfo; nTokenLength = MIN( sizeof( m_szGatewayName ) - 1, nTokenLength ); sprintf( m_szGatewayName, "%.*s", nTokenLength, m_xsi.szServerInfo );
// parse out the gateway information
// get the master server's port and range
pToken += 2; int nSupportsPII = 0; sscanf( pToken, "%d_%d_%d", &m_nMasterServerPortStart, &m_numMasterServers, &nSupportsPII ); m_bSupportsPII = ( nSupportsPII != 0 );
if ( !m_nMasterServerPortStart || !m_numMasterServers ) return false;
return true; }
void CXlspDatacenter::Destroy() { if ( m_adrSecure.s_addr ) { g_pMatchExtensions->GetIXOnline()->XNetUnregisterInAddr( m_adrSecure ); }
Q_memset( this, 0, sizeof( *this ) ); }
//
// XLSP title servers enumeration implementation
//
CXlspTitleServers::CXlspTitleServers( int nPingLimit, bool bMustSupportPII ) : m_hEnumerate( NULL ), m_numServers( 0 ), m_pQos( NULL ), m_eState( STATE_INIT ), m_flTimeout( 0.0f ), m_nPingLimit( nPingLimit ), m_bMustSupportPII( bMustSupportPII ), m_pCancelOverlappedJob( NULL ) { ZeroMemory( &m_xOverlapped, sizeof( m_xOverlapped ) ); }
CXlspTitleServers::~CXlspTitleServers() { }
void CXlspTitleServers::Destroy() { switch ( m_eState ) { case STATE_XLSP_ENUMERATE_DCS: m_pCancelOverlappedJob = ThreadExecute( MMX360_CancelOverlapped, &m_xOverlapped ); // UpdateDormantOperations will clean the rest
break; case STATE_XLSP_QOS_DCS: g_pMatchExtensions->GetIXOnline()->XNetQosRelease( m_pQos ); m_pQos = NULL; break; }
if ( !m_pCancelOverlappedJob ) delete this; else MMX360_RegisterDormant( this ); // keep running UpdateDormantOperation frame loop
}
bool CXlspTitleServers::UpdateDormantOperation() { if ( !m_pCancelOverlappedJob->IsFinished() ) return true; // keep running dormant
m_pCancelOverlappedJob->Release(); m_pCancelOverlappedJob = NULL;
CloseHandle( m_hEnumerate ); m_hEnumerate = NULL; delete this; return false; // destroyed the object, remove from dormant list
}
void CXlspTitleServers::Update() { switch ( m_eState ) { case STATE_INIT: m_flTimeout = Plat_FloatTime() + mm_dedicated_xlsp_timeout.GetFloat(); if ( EnumerateDcs( m_hEnumerate, m_xOverlapped, m_bufXlspEnumerateDcs ) ) m_eState = STATE_XLSP_ENUMERATE_DCS; else m_eState = STATE_FINISHED; break;
case STATE_XLSP_ENUMERATE_DCS: if ( !XHasOverlappedIoCompleted( &m_xOverlapped ) ) { if ( Plat_FloatTime() > m_flTimeout ) { // Enumeration timeout elapsed
m_pCancelOverlappedJob = ThreadExecute( MMX360_CancelOverlapped, &m_xOverlapped ); // UpdateDormantOperations will clean the rest
m_eState = STATE_FINISHED; } return; }
m_flTimeout = Plat_FloatTime() + mm_dedicated_xlsp_timeout.GetFloat(); if ( ExecuteQosDcs( m_hEnumerate, m_xOverlapped, m_bufXlspEnumerateDcs, m_numServers, m_pQos ) ) m_eState = STATE_XLSP_QOS_DCS; else m_eState = STATE_FINISHED; break;
case STATE_XLSP_QOS_DCS: if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) || Plat_FloatTime() > m_flTimeout || !m_pQos->cxnqosPending ) { ParseDatacentersFromQos( m_arrDcs, m_bufXlspEnumerateDcs, m_pQos ); m_eState = STATE_FINISHED; } break; } }
bool CXlspTitleServers::EnumerateDcs( HANDLE &hEnumerate, XOVERLAPPED &xOverlapped, CUtlBuffer &bufResults ) { // If we're using a fake xlsp server, then we don't want to do any of this.
if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) ) { return true; }
int numDcs = mm_dedicated_xlsp_max_dcs.GetInt(); DevMsg( "Enumerating XLSP datacenters (%d max supported)...\n", numDcs );
//
// Create enumerator
//
DWORD numBytes = 0; DWORD ret = g_pMatchExtensions->GetIXOnline()->XTitleServerCreateEnumerator( NULL, numDcs, &numBytes, &hEnumerate ); if ( ret != ERROR_SUCCESS ) { DevWarning( "XTitleServerCreateEnumerator failed (code = 0x%08X)\n", ret ); hEnumerate = NULL; return false; }
// Allocate results buffer
bufResults.EnsureCapacity( numBytes ); XTITLE_SERVER_INFO *pXSI = ( XTITLE_SERVER_INFO * ) bufResults.Base(); ZeroMemory( pXSI, numBytes ); ZeroMemory( &xOverlapped, sizeof( XOVERLAPPED ) );
//
// Enumerate
//
ret = XEnumerate( hEnumerate, pXSI, numBytes, NULL, &xOverlapped ); if ( ret != ERROR_IO_PENDING ) { DevWarning( "XEnumerate of XTitleServerCreateEnumerator failed (code = 0x%08X)\n", ret ); CloseHandle( hEnumerate ); hEnumerate = NULL; return false; }
return true; }
bool CXlspTitleServers::ExecuteQosDcs( HANDLE &hEnumerate, XOVERLAPPED &xOverlapped, CUtlBuffer &bufResults, DWORD &numServers, XNQOS *&pQos ) { // If we're using a fake xlsp server, then we don't want to do any of this.
if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) ) { return true; }
numServers = 0; XGetOverlappedResult( &xOverlapped, &numServers, TRUE ); CloseHandle( hEnumerate ); hEnumerate = NULL;
DevMsg( "Xlsp_OnEnumerateDcsCompleted found %d datacenters.\n", numServers ); if ( !numServers ) { return false; }
//
// Prepare for QOS lookup to the datacenters
//
XTITLE_SERVER_INFO *pXSI = ( XTITLE_SERVER_INFO * ) bufResults.Base(); DWORD dwServiceId = g_pMatchFramework->GetMatchTitle()->GetTitleServiceID(); CUtlVector< IN_ADDR > qosAddr; CUtlVector< DWORD > qosServiceId; for ( DWORD k = 0; k < numServers; ++ k ) { qosAddr.AddToTail( pXSI[k].inaServer ); qosServiceId.AddToTail( dwServiceId ); }
//
// Submit QOS lookup
//
pQos = NULL; DWORD ret = g_pMatchExtensions->GetIXOnline()->XNetQosLookup( 0, NULL, NULL, NULL, numServers, qosAddr.Base(), qosServiceId.Base(), 8, 0, 0, NULL, &pQos ); if ( ret != ERROR_SUCCESS ) { DevWarning( "XNetQosLookup failed to start for XLSP datacenters, code = 0x%08X!\n", ret ); return false; }
return true; }
void CXlspTitleServers::ParseDatacentersFromQos( CUtlVector< CXlspDatacenter > &arrDcs, CUtlBuffer &bufResults, XNQOS *&pQos ) { // If we're using a fake xlsp server, then we don't want to do any of this and instead add our fake server to the datacenter list.
if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) ) { netadr_t gatewayAdr; gatewayAdr.SetFromString( CommandLine()->GetParm( CommandLine()->FindParm( "-xlsp_fake_gateway" ) + 1) );
char szSG[200]; sprintf(szSG, "%s**%d_1", gatewayAdr.ToString(true), gatewayAdr.GetPort() );
CXlspDatacenter dc; ZeroMemory( &dc, sizeof( dc ) ); Q_strncpy( dc.m_xsi.szServerInfo, szSG, sizeof(dc.m_xsi.szServerInfo) ); dc.m_xsi.inaServer.S_un.S_addr = gatewayAdr.GetIPNetworkByteOrder(); dc.ParseServerInfo(); arrDcs.AddToTail( dc ); return; }
if ( !pQos ) return;
XTITLE_SERVER_INFO *pXSI = ( XTITLE_SERVER_INFO * ) bufResults.Base();
for ( DWORD k = 0; k < pQos->cxnqos; ++ k ) { // Datacenter info
CXlspDatacenter dc; ZeroMemory( &dc, sizeof( dc ) ); dc.m_xsi = pXSI[k]; dc.m_qos = pQos->axnqosinfo[k];
// Cull centers that failed to be contacted
uint uiRequiredQosFlags = ( XNET_XNQOSINFO_COMPLETE | XNET_XNQOSINFO_TARGET_CONTACTED ); if ( ( ( dc.m_qos.bFlags & uiRequiredQosFlags ) != uiRequiredQosFlags ) || ( dc.m_qos.bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) ) { DevWarning( "XLSP datacenter %d `%s` failed connection probe (0x%08X).\n", k, dc.m_xsi.szServerInfo, dc.m_qos.bFlags ); continue; }
// Cull any non-conformant XLSP center names
if ( !dc.ParseServerInfo() ) { DevWarning( "XLSP datacenter %d `%s` has non-conformant server info.\n", k, dc.m_xsi.szServerInfo ); continue; }
// Check if PII is required
if ( m_bMustSupportPII && !dc.m_bSupportsPII ) { DevWarning( "XLSP datacenter %d `%s` does not support PII.\n", k, dc.m_xsi.szServerInfo ); continue; }
// Check if we are forcing a specific datacenter
bool bForcedUse = false; if ( char const *szForcedDcName = mm_dedicated_xlsp_force_dc.GetString() ) { if ( *szForcedDcName && !Q_stricmp( szForcedDcName, dc.m_szGatewayName ) ) bForcedUse = true;
if ( *szForcedDcName && !bForcedUse ) { // Gateway doesn't match forced datacenter
DevWarning( "XLSP datacenter %d `%s` is ignored because we are forcing datacenter name `%s`.\n", k, dc.m_xsi.szServerInfo, szForcedDcName ); continue; } }
// Check ping
if ( m_nPingLimit > 0 && dc.m_qos.wRttMedInMsecs > m_nPingLimit && !bForcedUse ) { DevWarning( "XLSP datacenter %d `%s` is ignored because its ping %d is greater than max allowed %d.\n", k, dc.m_xsi.szServerInfo, dc.m_qos.wRttMedInMsecs, m_nPingLimit ); continue; }
// Remeber the datacenter as a potential candidate
dc.m_nPingBucket = GetBucketedRTT( dc.m_qos.wRttMedInMsecs ); DevMsg( "XLSP datacenter %d `%s` accepted, ping %d [<= %d]\n", k, dc.m_szGatewayName, dc.m_qos.wRttMedInMsecs, dc.m_nPingBucket ); arrDcs.AddToTail( dc ); }
// Release the QOS query
g_pMatchExtensions->GetIXOnline()->XNetQosRelease( pQos ); pQos = NULL; }
bool CXlspTitleServers::IsSearchCompleted() const { return m_eState == STATE_FINISHED; }
CUtlVector< CXlspDatacenter > & CXlspTitleServers::GetDatacenters() { Assert( IsSearchCompleted() ); return m_arrDcs; }
//
// XLSP connection implementation
//
CXlspConnection::CXlspConnection( bool bMustSupportPII ) : m_pTitleServers( NULL ), m_pCmdResult( NULL ), m_flTimeout( 0.0f ), m_eState( STATE_INIT ), m_idCmdReplyId( 0 ), m_numCmdRetriesAllowed( 0 ), m_flCmdRetryTimeout( 0 ), m_bMustSupportPII( bMustSupportPII ) { ZeroMemory( &m_dc, sizeof( m_dc ) ); }
CXlspConnection::~CXlspConnection() { ; }
void CXlspConnection::Destroy() { if ( m_pTitleServers ) m_pTitleServers->Destroy(); m_pTitleServers = NULL;
if ( m_eState >= STATE_CONNECTED ) m_dc.Destroy();
if ( m_eState == STATE_RUNNINGCMD ) g_pMatchEventsSubscription->Unsubscribe( this );
if ( m_pCmdResult ) m_pCmdResult->deleteThis(); m_pCmdResult = NULL;
delete this; }
bool CXlspConnection::IsConnected() const { return m_eState == STATE_CONNECTED || m_eState == STATE_RUNNINGCMD; }
bool CXlspConnection::HasError() const { return m_eState == STATE_ERROR; }
static int XlspConnection_CompareDcs( CXlspDatacenter const *dc1, CXlspDatacenter const *dc2 ) { int nPing1 = dc1->m_nPingBucket, nPing2 = dc2->m_nPingBucket;
if ( nPing1 != nPing2 ) return ( nPing1 < nPing2 ) ? -1 : 1; // Compare by name
if ( int iNameCmp = Q_stricmp( dc1->m_szGatewayName, dc2->m_szGatewayName ) ) return iNameCmp;
// Compare by IP addr
int iAddrCmp = dc1->m_xsi.inaServer.s_addr - dc2->m_xsi.inaServer.s_addr; if ( iAddrCmp ) return iAddrCmp;
return 0; }
void CXlspConnection::ConnectXlspDc() { CUtlVector< CXlspDatacenter > &arrDcs = m_pTitleServers->GetDatacenters(); arrDcs.Sort( XlspConnection_CompareDcs );
for ( int k = 0; k < arrDcs.Count(); ++ k ) { m_dc = arrDcs[k];
DevMsg( "[XLSP] Connecting to %s:%d (%d masters) - ping %d [<= %d]\n" " ProbesXmit=%3d ProbesRecv=%3d\n" " RttMinInMsecs=%3d RttMedInMsecs=%3d\n" " UpBitsPerSec=%6d DnBitsPerSec=%6d\n", m_dc.m_szGatewayName, m_dc.m_nMasterServerPortStart, m_dc.m_numMasterServers, m_dc.m_qos.wRttMedInMsecs, m_dc.m_nPingBucket, m_dc.m_qos.cProbesXmit, m_dc.m_qos.cProbesRecv, m_dc.m_qos.wRttMinInMsecs, m_dc.m_qos.wRttMedInMsecs, m_dc.m_qos.dwUpBitsPerSec, m_dc.m_qos.dwDnBitsPerSec );
//
// Resolve the secure address
//
DWORD ret = ERROR_SUCCESS; if ( CommandLine()->FindParm( "-xlsp_fake_gateway" ) ) { m_dc.m_adrSecure = m_dc.m_xsi.inaServer; } else { ret = g_pMatchExtensions->GetIXOnline()->XNetServerToInAddr( m_dc.m_xsi.inaServer, g_pMatchFramework->GetMatchTitle()->GetTitleServiceID(), &m_dc.m_adrSecure ); }
if ( ret != ERROR_SUCCESS ) { DevWarning( "Failed to resolve XLSP secure address (code = 0x%08X)!\n", ret ); continue; } else { DevMsg( "Resolved XLSP server address\n" ); m_eState = STATE_CONNECTED;
m_pTitleServers->Destroy(); m_pTitleServers = NULL; return; } }
ZeroMemory( &m_dc, sizeof( m_dc ) ); m_eState = STATE_ERROR; }
CXlspDatacenter const & CXlspConnection::GetDatacenter() const { return m_dc; }
void CXlspConnection::ResolveCmdSystemValues( KeyValues *pCommand ) { // Expand the command data based on DC fields
if ( KeyValues *pExp = pCommand->FindKey( "*dcpgmi" ) ) pExp->SetInt( NULL, m_dc.m_qos.wRttMinInMsecs ); if ( KeyValues *pExp = pCommand->FindKey( "*dcpgme" ) ) pExp->SetInt( NULL, m_dc.m_qos.wRttMedInMsecs ); if ( KeyValues *pExp = pCommand->FindKey( "*dcpgbu" ) ) pExp->SetInt( NULL, m_dc.m_nPingBucket ); if ( KeyValues *pExp = pCommand->FindKey( "*dcbwup" ) ) pExp->SetInt( NULL, m_dc.m_qos.dwUpBitsPerSec ); if ( KeyValues *pExp = pCommand->FindKey( "*dcbwdn" ) ) pExp->SetInt( NULL, m_dc.m_qos.dwDnBitsPerSec ); if ( KeyValues *pExp = pCommand->FindKey( "*net" ) ) pExp->SetInt( NULL, g_pMatchExtensions->GetIXOnline()->XNetGetEthernetLinkStatus() ); if ( KeyValues *pExp = pCommand->FindKey( "*nat" ) ) pExp->SetInt( NULL, g_pMatchExtensions->GetIXOnline()->XOnlineGetNatType() ); if ( KeyValues *pExp = pCommand->FindKey( "*mac" ) ) { // Console MAC address
XNADDR xnaddr; uint64 uiMachineId = 0ull; if ( XNET_GET_XNADDR_PENDING == g_pMatchExtensions->GetIXOnline()->XNetGetTitleXnAddr( &xnaddr ) || g_pMatchExtensions->GetIXOnline()->XNetXnAddrToMachineId( &xnaddr, &uiMachineId ) ) uiMachineId = 0ull; pExp->SetUint64( NULL, uiMachineId ); } if ( KeyValues *pExp = pCommand->FindKey( "*diskDsn" ) ) { // Serial number of GAME volume
struct VolumeInformation_t { char chVolumeName[128]; char chFsName[128]; DWORD dwVolumeSerial; DWORD dwMaxComponentLen; DWORD dwFsFlags; } vi; memset( &vi, 0, sizeof( vi ) ); if ( !GetVolumeInformation( "d:\\", vi.chVolumeName, sizeof( vi.chVolumeName ) - 1, &vi.dwVolumeSerial, &vi.dwMaxComponentLen, &vi.dwFsFlags, vi.chFsName, sizeof( vi.chFsName ) - 1 ) ) { memset( &vi, 0, sizeof( vi ) ); } uint64 uiDsn = vi.dwVolumeSerial; if ( g_pFullFileSystem && g_pFullFileSystem->IsDVDHosted() ) uiDsn |= ( 1ull << 33 ); // DVD hosted
pExp->SetUint64( NULL, uiDsn ); } if ( KeyValues *pExp = pCommand->FindKey( "*diskDnfo" ) ) { // Space information of GAME volume
struct FreeSpace_t { ULARGE_INTEGER ulFreeTitle, ulTotal, ulFree; } fs; memset( &fs, 0, sizeof( fs ) ); if ( !GetDiskFreeSpaceEx( "d:\\", &fs.ulFreeTitle, &fs.ulTotal, &fs.ulFree ) ) { memset( &fs, 0, sizeof( fs ) ); } uint32 uiTotalMbs = fs.ulTotal.QuadPart / ( 1024 * 1024 ); uint32 uiFreeMbs = fs.ulFree.QuadPart / ( 1024 * 1024 ); pExp->SetUint64( NULL, uint64( uiTotalMbs ) | ( uint64( uiFreeMbs ) << 32 ) ); } if ( KeyValues *pExp = pCommand->FindKey( "*diskCnfo" ) ) { // Space information of CACHE partition
struct FreeSpace_t { ULARGE_INTEGER ulFreeTitle, ulTotal, ulFree; } fs; memset( &fs, 0, sizeof( fs ) ); if ( !GetDiskFreeSpaceEx( "cache:\\", &fs.ulFreeTitle, &fs.ulTotal, &fs.ulFree ) ) { memset( &fs, 0, sizeof( fs ) ); } uint32 uiTotalMbs = fs.ulTotal.QuadPart / ( 1024 * 1024 ); uint32 uiFreeMbs = fs.ulFree.QuadPart / ( 1024 * 1024 ); pExp->SetUint64( NULL, uint64( uiTotalMbs ) | ( uint64( uiFreeMbs ) << 32 ) ); } if ( KeyValues *pExp = pCommand->FindKey( "*diskHnfo" ) ) { // Space information of HDD volume
XDEVICE_DATA xdd; memset( &xdd, 0, sizeof( xdd ) ); xdd.DeviceID = 1; if ( XContentGetDeviceData( xdd.DeviceID, &xdd ) ) { memset( &xdd, 0, sizeof( xdd ) ); } uint32 uiTotalMbs = xdd.ulDeviceBytes / ( 1024 * 1024 ); uint32 uiFreeMbs = xdd.ulDeviceFreeBytes / ( 1024 * 1024 ); pExp->SetUint64( NULL, uint64( uiTotalMbs ) | ( uint64( uiFreeMbs ) << 32 ) ); } if ( KeyValues *pExp = pCommand->FindKey( "*disk1nfo" ) ) { // Space information of user-chosen storage device
XDEVICE_DATA xdd; memset( &xdd, 0, sizeof( xdd ) ); xdd.DeviceID = XBX_GetStorageDeviceId( XBX_GetPrimaryUserId() ); if ( XContentGetDeviceData( xdd.DeviceID, &xdd ) ) { memset( &xdd, 0, sizeof( xdd ) ); } uint32 uiTotalMbs = xdd.ulDeviceBytes / ( 1024 * 1024 ); uint32 uiFreeMbs = xdd.ulDeviceFreeBytes / ( 1024 * 1024 ); pExp->SetUint64( NULL, uint64( uiTotalMbs ) | ( uint64( uiFreeMbs ) << 32 ) ); } }
netadr_t CXlspConnection::GetXlspDcAddress() { //
// Convert DC address to netadr_t on a random master port
//
netadr_t inetAddr; inetAddr.SetType( NA_IP ); inetAddr.SetIPAndPort( m_dc.m_adrSecure.s_addr, m_dc.m_nMasterServerPortStart + RandomInt( 0, m_dc.m_numMasterServers - 1 ) + mm_dedicated_xlsp_force_dc_offset.GetInt() ); return inetAddr; }
bool CXlspConnection::ExecuteCmd( KeyValues *pCommand, int numRetriesAllowed, float flCmdRetryTimeout ) { if ( !pCommand ) return false;
if ( m_eState != STATE_CONNECTED ) { Assert( !"CXlspConnection::ExecuteCmd while not connected to XLSP server!\n" ); return false; }
ResolveCmdSystemValues( pCommand );
// Serialize the command
CUtlBuffer bufBinData; bufBinData.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() ); if ( !pCommand->WriteAsBinary( bufBinData ) ) return false;
// Destroy the previous result data
if ( m_pCmdResult ) m_pCmdResult->deleteThis(); m_pCmdResult = NULL;
m_idCmdReplyId = GetNextXlspConnectionCmdReplyId(); m_numCmdRetriesAllowed = numRetriesAllowed; m_flCmdRetryTimeout = ( ( flCmdRetryTimeout > 0 ) ? flCmdRetryTimeout : mm_dedicated_xlsp_command_timeout.GetFloat() );
//
// Prepare the request payload
//
char msg_buffer[ INetSupport::NC_MAX_ROUTABLE_PAYLOAD ]; bf_write msg( msg_buffer, sizeof( msg_buffer ) );
msg.WriteByte( A2A_KV_CMD ); msg.WriteByte( A2A_KV_VERSION );
// Xbox 360 -> Master server
msg.WriteLong( MAKE_4BYTES( 'X', '-', 'M', '1' ) );
msg.WriteLong( m_idCmdReplyId ); msg.WriteLong( m_dc.m_adrSecure.s_addr ); // datacenter's challenge
msg.WriteLong( 0 ); msg.WriteLong( bufBinData.TellMaxPut() ); msg.WriteBytes( bufBinData.Base(), bufBinData.TellMaxPut() ); // datacenter command
DevMsg( 2, "Xbox->XLSP: 0x%08X 0x%08X ( %u bytes, %d retries allowed )\n", m_idCmdReplyId, (uint32) m_dc.m_adrSecure.s_addr, msg.GetNumBytesWritten(), m_numCmdRetriesAllowed ); KeyValuesDumpAsDevMsg( pCommand, 1, 2 );
g_pMatchExtensions->GetINetSupport()->SendPacket( NULL, INetSupport::NS_SOCK_CLIENT, GetXlspDcAddress(), msg.GetData(), msg.GetNumBytesWritten() );
if ( m_numCmdRetriesAllowed > 0 ) { m_bufCmdRetry.SetCount( msg.GetNumBytesWritten() ); memcpy( m_bufCmdRetry.Base(), msg.GetData(), msg.GetNumBytesWritten() ); } else { m_bufCmdRetry.Purge(); }
g_pMatchEventsSubscription->Subscribe( this ); m_eState = STATE_RUNNINGCMD; m_flTimeout = Plat_FloatTime() + m_flCmdRetryTimeout; return true; }
void CXlspConnection::OnEvent( KeyValues *pEvent ) { char const *szName = pEvent->GetName();
if ( !Q_stricmp( "A2A_KV_CMD", szName ) ) { // Master server -> Xbox 360
if ( pEvent->GetInt( "header" ) == MAKE_4BYTES( 'M', '-', 'X', '1' ) && pEvent->GetInt( "replyid" ) == m_idCmdReplyId ) { g_pMatchEventsSubscription->Unsubscribe( this ); m_eState = STATE_CONNECTED;
m_pCmdResult = pEvent->GetFirstTrueSubKey(); if ( !m_pCmdResult ) m_pCmdResult = pEvent;
// Keep it as a copy
m_pCmdResult = m_pCmdResult->MakeCopy();
DevMsg( 2, "Xbox<<XLSP: 0x%08X ( %u bytes )\n", m_idCmdReplyId, pEvent->GetInt( "size" ) ); // KeyValuesDumpAsDevMsg( m_pCmdResult, 1, 2 );
} } }
void CXlspConnection::Update() { switch ( m_eState ) { case STATE_INIT: m_eState = STATE_CONNECTING; m_pTitleServers = new CXlspTitleServers( 0, m_bMustSupportPII ); // no ping limitation
break;
case STATE_CONNECTING: m_pTitleServers->Update(); if ( m_pTitleServers->IsSearchCompleted() ) ConnectXlspDc(); break;
case STATE_RUNNINGCMD: if ( Plat_FloatTime() > m_flTimeout ) { if ( m_numCmdRetriesAllowed > 0 ) { // Retry and increase timeout
-- m_numCmdRetriesAllowed; m_flTimeout = Plat_FloatTime() + m_flCmdRetryTimeout; DevMsg( 2, "Xbox->XLSP: 0x%08X 0x%08X ( %u bytes, %d retries remaining )\n", m_idCmdReplyId, (uint32) m_dc.m_adrSecure.s_addr, m_bufCmdRetry.Count(), m_numCmdRetriesAllowed ); g_pMatchExtensions->GetINetSupport()->SendPacket( NULL, INetSupport::NS_SOCK_CLIENT, GetXlspDcAddress(), m_bufCmdRetry.Base(), m_bufCmdRetry.Count() ); return; } DevWarning( 2, "Xbox->XLSP: 0x%08X 0x%08X - TIMED OUT!\n", m_idCmdReplyId, (uint32) m_dc.m_adrSecure.s_addr ); g_pMatchEventsSubscription->Unsubscribe( this ); m_eState = STATE_ERROR; } break; } }
//
// Connection batch implementation
//
CXlspConnectionCmdBatch::CXlspConnectionCmdBatch( CXlspConnection *pConnection, CUtlVector<KeyValues*> &arrCommands, int numRetriesAllowedPerEachCmd, float flCommandTimeout ) : m_eState( STATE_BATCH_WAITING ), m_iCommand( 0 ), m_pXlspConn( pConnection ), m_numRetriesAllowedPerEachCmd( numRetriesAllowedPerEachCmd ), m_flCommandTimeout( ( flCommandTimeout > 0 ) ? flCommandTimeout : mm_dedicated_xlsp_command_timeout.GetFloat() ) { m_arrCommands.Swap( arrCommands ); }
CXlspConnectionCmdBatch::~CXlspConnectionCmdBatch() { }
bool CXlspConnectionCmdBatch::IsFinished() const { return m_eState >= STATE_FINISHED; }
bool CXlspConnectionCmdBatch::HasAllResults() const { return IsFinished() && m_arrCommands.Count() == m_arrResults.Count(); }
void CXlspConnectionCmdBatch::Update() { m_pXlspConn->Update();
if ( m_pXlspConn->HasError() ) { m_eState = STATE_FINISHED; }
switch ( m_eState ) { case STATE_BATCH_WAITING: if ( m_pXlspConn->IsConnected() ) { RunNextCmd(); } break;
case STATE_RUNNINGCMD: if ( KeyValues *pCmdResult = m_pXlspConn->GetCmdResult() ) { m_arrResults.AddToTail( pCmdResult->MakeCopy() ); RunNextCmd(); } break; } }
void CXlspConnectionCmdBatch::RunNextCmd() { if ( m_iCommand >= m_arrCommands.Count() || m_pXlspConn->HasError() || !m_pXlspConn->ExecuteCmd( m_arrCommands[ m_iCommand ], m_numRetriesAllowedPerEachCmd, m_flCommandTimeout ) ) { m_eState = STATE_FINISHED; } else { ++ m_iCommand; m_eState = STATE_RUNNINGCMD; } }
void CXlspConnectionCmdBatch::Destroy() { for ( int k = 0; k < m_arrCommands.Count(); ++ k ) m_arrCommands[k]->deleteThis();
for ( int k = 0; k < m_arrResults.Count(); ++ k ) m_arrResults[k]->deleteThis();
m_arrCommands.Purge(); m_arrResults.Purge();
m_pXlspConn = NULL;
delete this; }
#endif // _X360
|