csgo-2018-source/matchmaking/x360_xlsp_cmd.cpp

866 lines
24 KiB
C++
Raw Permalink Normal View History

2021-07-25 12:11:47 +08:00
//===== Copyright <20> 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