You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1177 lines
34 KiB
1177 lines
34 KiB
//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=====================================================================================//
|
|
|
|
#ifndef _X360
|
|
#include "xbox/xboxstubs.h"
|
|
#endif
|
|
|
|
#include "mm_framework.h"
|
|
|
|
#include "proto_oob.h"
|
|
#include "fmtstr.h"
|
|
#include "vstdlib/random.h"
|
|
#include "mathlib/IceKey.H"
|
|
#include "filesystem.h"
|
|
|
|
#if !defined( NO_STEAM )
|
|
#include "steam_datacenterjobs.h"
|
|
#endif
|
|
|
|
// NOTE: This has to be the last file included!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
|
|
static ConVar mm_datacenter_update_interval( "mm_datacenter_update_interval", "3600", FCVAR_DEVELOPMENTONLY, "Interval between datacenter stats updates." );
|
|
static ConVar mm_datacenter_retry_interval( "mm_datacenter_retry_interval", "75", FCVAR_DEVELOPMENTONLY, "Interval between datacenter stats retries." );
|
|
static ConVar mm_datacenter_retry_infochunks_attempts( "mm_datacenter_retry_infochunks_attempts", "3", FCVAR_DEVELOPMENTONLY, "How many times can we retry retrieving each info chunk before failing." );
|
|
static ConVar mm_datacenter_query_delay( "mm_datacenter_query_delay", "2", FCVAR_DEVELOPMENTONLY, "Delay after datacenter update is enabled before data is actually queried." );
|
|
static ConVar mm_datacenter_report_version( "mm_datacenter_report_version", "5", FCVAR_DEVELOPMENTONLY, "Data version to report to DC." );
|
|
static ConVar mm_datacenter_delay_mount_frames( "mm_datacenter_delay_mount_frames", "6", FCVAR_DEVELOPMENTONLY, "How many frames to delay before attempting to mount the xlsp patch." );
|
|
|
|
|
|
static CDatacenter g_Datacenter;
|
|
CDatacenter *g_pDatacenter = &g_Datacenter;
|
|
|
|
CON_COMMAND( mm_datacenter_debugprint, "Shows information retrieved from data center" )
|
|
{
|
|
KeyValuesDumpAsDevMsg( g_pDatacenter->GetDataInfo(), 1 );
|
|
KeyValuesDumpAsDevMsg( g_pDatacenter->GetStats(), 1 );
|
|
}
|
|
|
|
//
|
|
// Buffer encryption/decryption
|
|
//
|
|
|
|
#ifdef _GAMECONSOLE
|
|
|
|
bool DecryptBuffer( CUtlBuffer &bufCypherText, CUtlBuffer &bufPlainText, unsigned int uiXorMask1 = 0 )
|
|
{
|
|
if ( bufCypherText.TellPut() < 20 )
|
|
return false;
|
|
|
|
int numBytes = bufCypherText.GetInt();
|
|
int iRandom = bufCypherText.GetInt() ^ 0xce135ef8 ^ uiXorMask1;
|
|
int iRandom2 = bufCypherText.GetInt() ^ 0x7ea55bb0;
|
|
|
|
if ( numBytes < 0 ||
|
|
bufCypherText.TellGet() + (numBytes + 7)/8 + 8 > bufCypherText.TellPut() )
|
|
return false;
|
|
|
|
IceKey cipher(1); /* medium encryption level */
|
|
unsigned char ucEncryptionKey[8] = { 0 };
|
|
*( int * )&ucEncryptionKey[ 0 ] = iRandom;
|
|
*( int * )&ucEncryptionKey[ 4 ] = iRandom2;
|
|
cipher.set( ucEncryptionKey );
|
|
|
|
bufPlainText.EnsureCapacity( numBytes + 8 + 8 );
|
|
unsigned char *pvPlainText = ( unsigned char * ) bufPlainText.PeekPut();
|
|
unsigned char *pvCypher = ( unsigned char * ) bufCypherText.PeekGet();
|
|
|
|
for ( int k = 0; k < numBytes;
|
|
k += 8, pvPlainText += 8, pvCypher += 8 )
|
|
{
|
|
cipher.decrypt( pvCypher, pvPlainText );
|
|
}
|
|
|
|
unsigned char ucDecryptedKey[8] = {0};
|
|
cipher.decrypt( pvCypher, ucDecryptedKey );
|
|
if ( memcmp( ucDecryptedKey, ucEncryptionKey, sizeof( ucDecryptedKey ) ) )
|
|
return false;
|
|
|
|
bufPlainText.SeekPut( bufPlainText.SEEK_HEAD, numBytes );
|
|
return true;
|
|
}
|
|
|
|
void EncryptBuffer( CUtlBuffer &bufPlainText, CUtlBuffer &bufCypherText, unsigned int uiXorMask1 = 0 )
|
|
{
|
|
int numBytes = bufPlainText.TellPut();
|
|
|
|
float flRandom = Plat_FloatTime();
|
|
int iRandom = *reinterpret_cast< int * >( &flRandom );
|
|
int iRandom2 = reinterpret_cast< int >( bufPlainText.Base() ) ^ 0x5ef8ce13;
|
|
|
|
// Function was written to allow bufCypherText to contain pre-existing data; this simply appends
|
|
// the encrypted buffer to the end.
|
|
// Add ( numBytes + 7 ) bytes since data is encrypted in blocks of 8 bytes, so numBytes is rounded up to multiple of 8
|
|
// Add 12 bytes for numBytes and 2 random salt values
|
|
// Add 16 bytes for double-encryption of encryption key
|
|
// Add 1 byte to ensure there's room for NULL termination at the end (avoid re-allocation)
|
|
bufCypherText.EnsureCapacity( bufCypherText.TellPut() + numBytes + 7 + 12 + 16 + 1 );
|
|
|
|
bufCypherText.PutInt( numBytes ); // TellPut() + 4
|
|
bufCypherText.PutInt( iRandom ^ 0xce135ef8 ^ uiXorMask1 ); // TellPut() + 8
|
|
bufCypherText.PutInt( iRandom2 ^ 0x7ea55bb0 ); // TellPut() + 12
|
|
|
|
IceKey cipher(1); /* medium encryption level */
|
|
unsigned char ucEncryptionKey[8] = { 0 };
|
|
*( int * )&ucEncryptionKey[ 0 ] = iRandom;
|
|
*( int * )&ucEncryptionKey[ 4 ] = iRandom2;
|
|
cipher.set( ucEncryptionKey );
|
|
|
|
// Add some padding to the end of bufPlainText so the source data is padded out to 8 bytes
|
|
bufPlainText.Put( ucEncryptionKey, sizeof( ucEncryptionKey ) );
|
|
|
|
// Write directly into cypher buffer and increment Put pointer later
|
|
unsigned char *pvPlainText = ( unsigned char * ) bufPlainText.PeekGet();
|
|
unsigned char *pvCypher = ( unsigned char * ) bufCypherText.PeekPut(); // getting pointer to TellPut() + 12
|
|
|
|
int numBytesWritten = 0;
|
|
for ( int k = 0; k < numBytes;
|
|
k += 8, pvPlainText += 8, pvCypher += 8, numBytesWritten += 8 )
|
|
{
|
|
cipher.encrypt( pvPlainText, pvCypher ); // writing numBytes rounded to multiple of 8, 8 bytes at a time (TellPut() + 12 + numBytes)
|
|
}
|
|
cipher.encrypt( ucEncryptionKey, pvCypher ); // TellPut() + 12 + ceil(numBytes, 8)
|
|
numBytesWritten += 8;
|
|
cipher.encrypt( ucEncryptionKey, pvCypher + 8 ); // TellPut() + 12 + ceil(numBytes, 8) + 8
|
|
numBytesWritten += 8;
|
|
|
|
bufCypherText.SeekPut( bufCypherText.SEEK_CURRENT, numBytesWritten );
|
|
}
|
|
|
|
#ifndef _CERT
|
|
CON_COMMAND( mm_datacenter_encrypt_file, "" )
|
|
{
|
|
if ( args.ArgC() != 5 )
|
|
{
|
|
Warning( "Incorrect mm_datacenter_encrypt_file syntax!\n" );
|
|
Warning( " mm_datacenter_encrypt_file D:\\update\\in.txt D:\\update\\out.bin 20100305 3589\n" );
|
|
return;
|
|
}
|
|
|
|
char const *szIn = args.Arg( 1 );
|
|
char const *szOut = args.Arg( 2 );
|
|
|
|
KeyValues *kv = new KeyValues( "" );
|
|
KeyValues::AutoDelete autodelete_kv( kv );
|
|
|
|
if ( !kv->LoadFromFile( g_pFullFileSystem, szIn ) )
|
|
{
|
|
Warning( "Failed to load '%s'\n", szIn );
|
|
return;
|
|
}
|
|
|
|
CUtlBuffer bufOut;
|
|
bufOut.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
|
|
if ( !kv->WriteAsBinary( bufOut ) )
|
|
{
|
|
Warning( "Failed to serialize kv!\n" );
|
|
return;
|
|
}
|
|
|
|
uint uiXorMaskDecrypt = Q_atoi( args.Arg( 3 ) ) ^ Q_atoi( args.Arg( 4 ) );
|
|
CUtlBuffer bufDcCrypt;
|
|
EncryptBuffer( bufOut, bufDcCrypt, uiXorMaskDecrypt );
|
|
if ( !g_pFullFileSystem->WriteFile( szOut, NULL, bufDcCrypt ) )
|
|
{
|
|
Warning( "Failed to save '%s'\n", szOut );
|
|
return;
|
|
}
|
|
|
|
Msg( "Successfully encrypted '%s'\n", szOut );
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
//
|
|
// Datacenter implementation
|
|
//
|
|
|
|
|
|
CDatacenter::CDatacenter() :
|
|
m_pInfoChunks( NULL ),
|
|
m_pDataInfo( NULL ),
|
|
#ifdef _X360
|
|
m_pXlspConnection( NULL ),
|
|
m_pXlspBatch( NULL ),
|
|
m_nVersionStored( 0 ),
|
|
m_nVersionApplied( 0 ),
|
|
m_numDelayedMountAttempts( 0 ),
|
|
m_flDcRequestDelayUntil( 0.0f ),
|
|
#elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
|
|
m_JobIDDataRequest( k_GIDNil ),
|
|
#endif
|
|
m_flNextSearchTime( 0.0f ),
|
|
m_bCanReachDatacenter( true ),
|
|
m_eState( STATE_IDLE )
|
|
{
|
|
#ifdef _X360
|
|
memset( m_bStorageDeviceAvail, 0, sizeof( m_bStorageDeviceAvail ) );
|
|
#endif
|
|
}
|
|
|
|
CDatacenter::~CDatacenter()
|
|
{
|
|
if ( m_pInfoChunks )
|
|
m_pInfoChunks->deleteThis();
|
|
m_pInfoChunks = NULL;
|
|
|
|
if ( m_pDataInfo )
|
|
m_pDataInfo->deleteThis();
|
|
m_pDataInfo = NULL;
|
|
|
|
#ifdef _X360
|
|
Assert( !m_pXlspConnection );
|
|
Assert( !m_pXlspBatch );
|
|
#elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
|
|
if ( GGCClient() )
|
|
{
|
|
GCSDK::CJob *pJob = GGCClient()->GetJobMgr().GetPJob( m_JobIDDataRequest );
|
|
delete pJob;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CDatacenter::PushAwayNextUpdate()
|
|
{
|
|
// Push away the next update to prevent start/stop updates
|
|
float flNextUpdateTime = Plat_FloatTime() + mm_datacenter_query_delay.GetFloat();
|
|
if ( flNextUpdateTime > m_flNextSearchTime )
|
|
m_flNextSearchTime = flNextUpdateTime;
|
|
}
|
|
|
|
void CDatacenter::EnableUpdate( bool bEnable )
|
|
{
|
|
DevMsg( "Datacenter::EnableUpdate( %d ), current state = %d\n", bEnable, m_eState );
|
|
|
|
if ( bEnable && m_eState == STATE_PAUSED )
|
|
{
|
|
m_eState = STATE_IDLE;
|
|
|
|
PushAwayNextUpdate();
|
|
}
|
|
|
|
if ( !bEnable )
|
|
{
|
|
RequestStop();
|
|
m_eState = STATE_PAUSED;
|
|
}
|
|
}
|
|
|
|
KeyValues * CDatacenter::GetDataInfo()
|
|
{
|
|
return m_pInfoChunks;
|
|
}
|
|
|
|
KeyValues * CDatacenter::GetStats()
|
|
{
|
|
return m_pInfoChunks ? m_pInfoChunks->FindKey( "stat" ) : NULL;
|
|
}
|
|
|
|
//
|
|
// CreateCmdBatch
|
|
// creates a new instance of cmd batch to communicate
|
|
// with datacenter backend
|
|
//
|
|
IDatacenterCmdBatch * CDatacenter::CreateCmdBatch( bool bMustSupportPII )
|
|
{
|
|
CDatacenterCmdBatchImpl *pBatch = new CDatacenterCmdBatchImpl( this, bMustSupportPII );
|
|
m_arrCmdBatchObjects.AddToTail( pBatch );
|
|
return pBatch;
|
|
}
|
|
|
|
//
|
|
// CanReachDatacenter
|
|
// returns true if we were able to establish a connection with the
|
|
// datacenter backend regardless if it returned valid data or not.
|
|
bool CDatacenter::CanReachDatacenter()
|
|
{
|
|
return m_bCanReachDatacenter;
|
|
}
|
|
|
|
void CDatacenter::OnCmdBatchReleased( CDatacenterCmdBatchImpl *pCmdBatch )
|
|
{
|
|
m_arrCmdBatchObjects.FindAndRemove( pCmdBatch );
|
|
}
|
|
|
|
void CDatacenter::OnEvent( KeyValues *pEvent )
|
|
{
|
|
char const *szEvent = pEvent->GetName();
|
|
|
|
if ( !V_stricmp( szEvent, "OnProfileStorageAvailable" ) )
|
|
{
|
|
OnStorageDeviceAvailable( pEvent->GetInt( "iController" ) );
|
|
}
|
|
#ifdef _X360
|
|
else if ( !V_stricmp( szEvent, "OnProfilesChanged" ) )
|
|
{
|
|
memset( m_bStorageDeviceAvail, 0, sizeof( m_bStorageDeviceAvail ) );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CDatacenter::OnStorageDeviceAvailable( int iCtrlr )
|
|
{
|
|
#ifdef _X360
|
|
DWORD nStorageDevice = XBX_GetStorageDeviceId( iCtrlr );
|
|
if ( !XBX_DescribeStorageDevice( nStorageDevice ) )
|
|
return;
|
|
|
|
if ( iCtrlr >= 0 && iCtrlr < XUSER_MAX_COUNT )
|
|
m_bStorageDeviceAvail[ iCtrlr ] = true;
|
|
|
|
// Build the config name we're looking for
|
|
char strFileName[MAX_PATH];
|
|
|
|
XBX_MakeStorageContainerRoot( iCtrlr, XBX_USER_SETTINGS_CONTAINER_DRIVE, strFileName, sizeof( strFileName ) );
|
|
int nLen = strlen( strFileName );
|
|
|
|
// Call through normal API function once the content container is opened
|
|
Q_snprintf( strFileName + nLen, sizeof(strFileName) - nLen, ":\\%08X_dc.nfo", g_pMatchFramework->GetMatchTitle()->GetTitleID() );
|
|
|
|
CUtlBuffer bufDcInfoCrypt, bufDcInfo;
|
|
if ( !g_pFullFileSystem->ReadFile( strFileName, NULL, bufDcInfoCrypt ) )
|
|
{
|
|
DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d has no dc.nfo\n", iCtrlr );
|
|
return;
|
|
}
|
|
if ( !DecryptBuffer( bufDcInfoCrypt, bufDcInfo ) )
|
|
{
|
|
DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d dc.nfo decrypt failed\n", iCtrlr );
|
|
return;
|
|
}
|
|
|
|
// Try reading key values info
|
|
KeyValues *pKv = new KeyValues( "" );
|
|
bufDcInfo.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
|
|
if ( !pKv->ReadAsBinary( bufDcInfo ) )
|
|
{
|
|
DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d kv read failed\n", iCtrlr );
|
|
pKv->deleteThis();
|
|
return;
|
|
}
|
|
|
|
// Check if the client is running the required TU
|
|
char const *szTuRequired = pKv->GetString( "turequired" );
|
|
if ( Q_stricmp( szTuRequired, MatchSession_GetTuInstalledString() ) )
|
|
{
|
|
DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d has dc.nfo for wrong TU version (turequired = %s, current tu = %s)\n",
|
|
iCtrlr, szTuRequired, MatchSession_GetTuInstalledString() );
|
|
pKv->deleteThis();
|
|
return;
|
|
}
|
|
|
|
// Check version of the key values
|
|
int nVersionStored = pKv->GetInt( "version", 0 );
|
|
if ( m_pInfoChunks->GetInt( "version", 0 ) >= nVersionStored )
|
|
{
|
|
DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d has stale dc.nfo (version = %d, current version = %d)\n",
|
|
iCtrlr, nVersionStored, m_pInfoChunks->GetInt( "version", 0 ) );
|
|
m_nVersionStored = MAX( m_nVersionStored, nVersionStored );
|
|
pKv->deleteThis();
|
|
return;
|
|
}
|
|
|
|
// Key values obtained from storage are fresh
|
|
if ( m_pInfoChunks )
|
|
m_pInfoChunks->deleteThis();
|
|
m_pInfoChunks = pKv;
|
|
|
|
DevMsg( "CDatacenter::OnStorageDeviceAvailable - ctrlr%d has valid dc.nfo (version = %d)\n", iCtrlr, nVersionStored );
|
|
m_nVersionStored = nVersionStored;
|
|
|
|
OnDatacenterInfoUpdated();
|
|
#endif
|
|
}
|
|
|
|
void CDatacenter::StorageDeviceWriteInfo( int iCtrlr )
|
|
{
|
|
#ifdef _X360
|
|
float flTimeStart;
|
|
flTimeStart = Plat_FloatTime();
|
|
|
|
// Build the config name we're looking for
|
|
char strFileName[MAX_PATH];
|
|
|
|
XBX_MakeStorageContainerRoot( iCtrlr, XBX_USER_SETTINGS_CONTAINER_DRIVE, strFileName, sizeof( strFileName ) );
|
|
int nLen = strlen( strFileName );
|
|
|
|
// Call through normal API function once the content container is opened
|
|
Q_snprintf( strFileName + nLen, sizeof(strFileName) - nLen, ":\\%08X_dc.nfo", g_pMatchFramework->GetMatchTitle()->GetTitleID() );
|
|
|
|
//
|
|
// Serialize our data
|
|
//
|
|
CUtlBuffer bufDcInfo;
|
|
bufDcInfo.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
|
|
if ( !m_pInfoChunks->WriteAsBinary( bufDcInfo ) )
|
|
return;
|
|
|
|
CUtlBuffer bufDcCrypt;
|
|
EncryptBuffer( bufDcInfo, bufDcCrypt );
|
|
g_pFullFileSystem->WriteFile( strFileName, NULL, bufDcCrypt );
|
|
|
|
// Finish container writes
|
|
g_pMatchExtensions->GetIXboxSystem()->FinishContainerWrites( iCtrlr );
|
|
|
|
DevMsg( "CDatacenter::StorageDeviceWriteInfo finished in %.2f sec\n", Plat_FloatTime() - flTimeStart );
|
|
#endif
|
|
}
|
|
|
|
void CDatacenter::TrySaveInfoToUserStorage()
|
|
{
|
|
#ifdef _X360
|
|
static ConVarRef host_write_last_time( "host_write_last_time" );
|
|
for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
|
|
{
|
|
int iCtrlr = XBX_GetUserId( k );
|
|
if ( iCtrlr >= 0 && iCtrlr < XUSER_MAX_COUNT &&
|
|
m_bStorageDeviceAvail[ iCtrlr ] && m_pInfoChunks &&
|
|
( Plat_FloatTime() - host_write_last_time.GetFloat() > 3.05f ) &&
|
|
( m_pInfoChunks->GetInt( "version", 0 ) > m_nVersionStored ) )
|
|
{
|
|
m_bStorageDeviceAvail[ iCtrlr ] = false;
|
|
StorageDeviceWriteInfo( iCtrlr );
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CDatacenter::Update()
|
|
{
|
|
#ifdef _X360
|
|
#elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
|
|
// Give a time-slice to the GCClient, which is used by Steam to communicate with the datacenter
|
|
if ( GGCClient() && !IsLocalClientConnectedToServer() )
|
|
{
|
|
GGCClient()->BMainLoop( k_nThousand );
|
|
}
|
|
#endif
|
|
|
|
switch ( m_eState )
|
|
{
|
|
case STATE_IDLE:
|
|
if ( Plat_FloatTime() > m_flNextSearchTime &&
|
|
!IsLocalClientConnectedToServer() )
|
|
RequestStart();
|
|
else
|
|
{
|
|
TrySaveInfoToUserStorage();
|
|
|
|
#ifdef _X360
|
|
if ( m_numDelayedMountAttempts > 0 )
|
|
{
|
|
if ( !IsLocalClientConnectedToServer() )
|
|
{
|
|
if ( m_numDelayedMountAttempts <= 2 )
|
|
{
|
|
m_numDelayedMountAttempts -= 2; // if knocking it down to 0, then will allow a retry next frame, otherwise will knock it into negative and will not allow a retry
|
|
OnDatacenterInfoUpdated();
|
|
}
|
|
else
|
|
{
|
|
-- m_numDelayedMountAttempts;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// User connected to server, reset delayed mount attempts
|
|
m_numDelayedMountAttempts = mm_datacenter_delay_mount_frames.GetInt(); // attempt to mount again when disconnected
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case STATE_REQUESTING_DATA:
|
|
case STATE_REQUESTING_CHUNKS:
|
|
RequestUpdate();
|
|
break;
|
|
|
|
case STATE_PAUSED:
|
|
// paused
|
|
break;
|
|
}
|
|
|
|
// Update all the contained cmd batches
|
|
for ( int k = 0; k < m_arrCmdBatchObjects.Count(); ++ k )
|
|
{
|
|
m_arrCmdBatchObjects[k]->Update();
|
|
}
|
|
}
|
|
|
|
void CDatacenter::RequestStart()
|
|
{
|
|
#ifdef _X360
|
|
if ( XBX_GetPrimaryUserId() == XBX_INVALID_USER_ID )
|
|
return;
|
|
if ( !XBX_GetNumGameUsers() || XBX_GetPrimaryUserIsGuest() )
|
|
return;
|
|
|
|
IPlayerLocal *pLocalPlayer = g_pPlayerManager->GetLocalPlayer( XBX_GetPrimaryUserId() );
|
|
if ( !pLocalPlayer )
|
|
return;
|
|
|
|
// We are about to send the request, inject a delay here so that
|
|
// we had enough time to discover and mount DLC
|
|
if ( !m_flDcRequestDelayUntil )
|
|
{
|
|
m_flDcRequestDelayUntil = Plat_FloatTime();
|
|
g_pMatchFramework->GetMatchSystem()->GetDlcManager()->RequestDlcUpdate();
|
|
return;
|
|
}
|
|
else if ( ( Plat_FloatTime() < m_flDcRequestDelayUntil + mm_datacenter_query_delay.GetFloat() ) ||
|
|
( !g_pMatchFramework->GetMatchSystem()->GetDlcManager()->IsDlcUpdateFinished() ) )
|
|
{
|
|
return; // waiting for the first-time request delay
|
|
}
|
|
#elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
|
|
if ( !GGCClient() )
|
|
return;
|
|
|
|
// Avoid stacking requests
|
|
if ( GGCClient()->GetJobMgr().BJobExists( m_JobIDDataRequest ) )
|
|
return;
|
|
|
|
GCSDK::CJob *pJob = new CGCClientJobDataRequest();
|
|
m_JobIDDataRequest = pJob->GetJobID();
|
|
pJob->StartJob( NULL );
|
|
#else
|
|
#endif
|
|
|
|
#ifdef _X360
|
|
m_pXlspConnection = new CXlspConnection( true );
|
|
|
|
CUtlVector< KeyValues * > arrCommands;
|
|
if ( KeyValues *cmd = new KeyValues( "datarequest" ) )
|
|
{
|
|
// Game title id
|
|
cmd->SetInt( "titleid", g_pMatchFramework->GetMatchTitle()->GetTitleID() );
|
|
|
|
// Data request fields
|
|
cmd->SetInt( "version", m_pInfoChunks->GetInt( "version", 0 ) );
|
|
cmd->SetInt( "verrprt", mm_datacenter_report_version.GetInt() );
|
|
cmd->SetUint64( "dlcmask", g_pMatchFramework->GetMatchSystem()->GetDlcManager()->GetDataInfo()->GetUint64( "@info/installed" ) );
|
|
|
|
// XUID
|
|
cmd->SetUint64( "xuid", pLocalPlayer->GetXUID() );
|
|
|
|
// Obfuscated name
|
|
char chName[ 2 * MAX_PLAYER_NAME_LENGTH ] = {0};
|
|
Q_strncpy( chName + 1, pLocalPlayer->GetName(), ARRAYSIZE( chName ) - 1 );
|
|
chName[0] = RandomInt( 5, 15 );
|
|
for ( char *pch = chName + 1; *pch; ++ pch )
|
|
( *pch ) = ( *pch ) ^ chName[0];
|
|
cmd->SetString( "name", chName );
|
|
|
|
// Prepare user privileges
|
|
struct Priv_t { XPRIVILEGE_TYPE ePriv, eFriendsOnly; uint64 iFlag; };
|
|
Priv_t arrPrivs[] = {
|
|
{ XPRIVILEGE_MULTIPLAYER_SESSIONS, XPRIVILEGE_MULTIPLAYER_SESSIONS, 1ull << 0 },
|
|
{ XPRIVILEGE_PURCHASE_CONTENT, XPRIVILEGE_PURCHASE_CONTENT, 1ull << 4 },
|
|
{ XPRIVILEGE_TRADE_CONTENT, XPRIVILEGE_TRADE_CONTENT, 1ull << 8 },
|
|
{ XPRIVILEGE_CONTENT_AUTHOR, XPRIVILEGE_CONTENT_AUTHOR, 1ull << 12 },
|
|
{ XPRIVILEGE_COMMUNICATIONS, XPRIVILEGE_COMMUNICATIONS_FRIENDS_ONLY, 1ull << 16 },
|
|
{ XPRIVILEGE_PROFILE_VIEWING, XPRIVILEGE_PROFILE_VIEWING_FRIENDS_ONLY, 1ull << 20 },
|
|
{ XPRIVILEGE_USER_CREATED_CONTENT, XPRIVILEGE_USER_CREATED_CONTENT_FRIENDS_ONLY, 1ull << 24 },
|
|
{ XPRIVILEGE_PRESENCE, XPRIVILEGE_PRESENCE_FRIENDS_ONLY, 1ull << 28 },
|
|
{ XPRIVILEGE_VIDEO_COMMUNICATIONS, XPRIVILEGE_VIDEO_COMMUNICATIONS_FRIENDS_ONLY, 1ull << 32 },
|
|
};
|
|
uint64 uiPrivilegesMask = 0ull;
|
|
for ( int jj = 0; jj < ARRAYSIZE( arrPrivs ); ++ jj )
|
|
{
|
|
uint64 uiThisPriv = 0ull;
|
|
BOOL bPriv = FALSE;
|
|
Priv_t const &priv = arrPrivs[jj];
|
|
DWORD dwResult = XUserCheckPrivilege( XBX_GetPrimaryUserId(), priv.ePriv, &bPriv );
|
|
if ( dwResult == ERROR_SUCCESS )
|
|
{
|
|
uiThisPriv |= priv.iFlag;
|
|
if ( bPriv )
|
|
{
|
|
uiThisPriv |= ( priv.iFlag << 1 );
|
|
}
|
|
else if ( priv.eFriendsOnly != priv.ePriv )
|
|
{
|
|
dwResult = XUserCheckPrivilege( XBX_GetPrimaryUserId(), priv.eFriendsOnly, &bPriv );
|
|
if ( dwResult == ERROR_SUCCESS )
|
|
{
|
|
uiThisPriv |= ( priv.iFlag << 2 );
|
|
if ( bPriv )
|
|
uiThisPriv |= ( priv.iFlag << 3 );
|
|
}
|
|
}
|
|
}
|
|
uiPrivilegesMask |= uiThisPriv;
|
|
}
|
|
cmd->SetUint64( "priv", uiPrivilegesMask );
|
|
|
|
// LV
|
|
cmd->SetInt( "lv", !!g_pMatchExtensions->GetIVEngineClient()->IsLowViolence() );
|
|
|
|
// Console rgn/locale
|
|
cmd->SetInt( "xrgn", XGetGameRegion() );
|
|
cmd->SetInt( "xlng", XGetLanguage() );
|
|
cmd->SetInt( "xloc", XGetLocale() );
|
|
|
|
// Video mode
|
|
XVIDEO_MODE xvid;
|
|
XGetVideoMode( &xvid );
|
|
|
|
cmd->SetInt( "scrw", xvid.dwDisplayWidth );
|
|
cmd->SetInt( "scrh", xvid.dwDisplayHeight );
|
|
cmd->SetInt( "vidi", xvid.fIsInterlaced );
|
|
cmd->SetInt( "vidw", xvid.fIsWideScreen );
|
|
cmd->SetInt( "vidh", xvid.fIsHiDef );
|
|
cmd->SetInt( "vids", xvid.VideoStandard );
|
|
cmd->SetFloat( "scrr", xvid.RefreshRate );
|
|
|
|
// Sound mode
|
|
static ConVarRef snd_surround_speakers( "snd_surround_speakers" );
|
|
cmd->SetInt( "snd", snd_surround_speakers.GetInt() );
|
|
|
|
// Controllers
|
|
int uMaskControllersConnected = 0;
|
|
for ( int k = 0; k < XUSER_MAX_COUNT; ++ k )
|
|
{
|
|
XINPUT_CAPABILITIES caps;
|
|
if ( XInputGetCapabilities( k, XINPUT_FLAG_GAMEPAD, &caps ) == ERROR_SUCCESS )
|
|
{
|
|
uMaskControllersConnected |= ( 1 << k );
|
|
}
|
|
}
|
|
cmd->SetInt( "joy", uMaskControllersConnected );
|
|
|
|
// Profile info
|
|
const UserProfileData &ups = pLocalPlayer->GetPlayerProfileData();
|
|
cmd->SetInt( "urgn", ups.region );
|
|
cmd->SetInt( "uach", ups.achearned );
|
|
cmd->SetInt( "uzon", ups.zone );
|
|
cmd->SetInt( "ucrd", ups.cred );
|
|
cmd->SetInt( "utit", ups.titlesplayed );
|
|
cmd->SetInt( "udif", ups.difficulty );
|
|
cmd->SetInt( "usns", ups.sensitivity );
|
|
cmd->SetInt( "uyax", ups.yaxis );
|
|
cmd->SetInt( "utia", ups.titleachearned );
|
|
cmd->SetInt( "utic", ups.titlecred );
|
|
|
|
// Datacenter information
|
|
cmd->SetInt( "*dcpgmi", 0 );
|
|
cmd->SetInt( "*dcpgme", 0 );
|
|
cmd->SetInt( "*dcpgbu", 0 );
|
|
cmd->SetInt( "*dcbwup", 0 );
|
|
cmd->SetInt( "*dcbwdn", 0 );
|
|
cmd->SetInt( "*net", 0 );
|
|
cmd->SetInt( "*nat", 0 );
|
|
cmd->SetUint64( "*mac", 0 );
|
|
|
|
cmd->SetUint64( "*diskDsn", 0 );
|
|
cmd->SetUint64( "*diskDnfo", 0 );
|
|
cmd->SetUint64( "*diskCnfo", 0 );
|
|
cmd->SetUint64( "*diskHnfo", 0 );
|
|
cmd->SetUint64( "*disk1nfo", 0 );
|
|
|
|
// Let title extend it
|
|
g_pMMF->GetMatchTitleGameSettingsMgr()->ExtendDatacenterReport( cmd, "datarequest" );
|
|
|
|
arrCommands.AddToTail( cmd );
|
|
}
|
|
|
|
m_pXlspBatch = new CXlspConnectionCmdBatch( m_pXlspConnection, arrCommands );
|
|
#endif
|
|
|
|
#if !defined( _PS3 ) && !defined( NO_STEAM_GAMECOORDINATOR )
|
|
DevMsg( "Datacenter::RequestStart, time %.2f\n", Plat_FloatTime() );
|
|
#endif
|
|
m_eState = STATE_REQUESTING_DATA;
|
|
}
|
|
|
|
void CDatacenter::RequestStop()
|
|
{
|
|
#if !defined( _PS3 ) && !defined( NO_STEAM_GAMECOORDINATOR )
|
|
DevMsg( "Datacenter::RequestStop, time %.2f, state %d\n", Plat_FloatTime(), m_eState );
|
|
#endif
|
|
|
|
bool bWasRequestingData = false;
|
|
|
|
#ifdef _X360
|
|
if ( m_pXlspBatch )
|
|
{
|
|
m_pXlspBatch->Destroy();
|
|
m_pXlspBatch = NULL;
|
|
}
|
|
|
|
if ( m_pXlspConnection )
|
|
{
|
|
m_pXlspConnection->Destroy();
|
|
m_pXlspConnection = NULL;
|
|
|
|
bWasRequestingData = true;
|
|
}
|
|
#elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
|
|
if ( GGCClient() )
|
|
{
|
|
bWasRequestingData = GGCClient()->GetJobMgr().BJobExists( m_JobIDDataRequest );
|
|
}
|
|
m_JobIDDataRequest = k_GIDNil;
|
|
#endif
|
|
|
|
if ( bWasRequestingData )
|
|
m_flNextSearchTime = Plat_FloatTime() + mm_datacenter_retry_interval.GetFloat();
|
|
|
|
m_eState = STATE_IDLE;
|
|
}
|
|
|
|
void CDatacenter::RequestUpdate()
|
|
{
|
|
bool bSuccessfulUpdate = false;
|
|
|
|
#ifdef _X360
|
|
m_pXlspBatch->Update();
|
|
if ( !m_pXlspBatch->IsFinished() )
|
|
return;
|
|
|
|
CUtlVector< KeyValues * > &arrResults = m_pXlspBatch->GetResults();
|
|
|
|
DevMsg( "Datacenter::RequestUpdate, time %.2f, state %d, batch finished: results %d, error %d\n",
|
|
Plat_FloatTime(), m_eState, arrResults.Count(), m_pXlspConnection->HasError() );
|
|
|
|
switch ( m_eState )
|
|
{
|
|
case STATE_REQUESTING_DATA:
|
|
if ( m_pXlspBatch->HasAllResults() )
|
|
{
|
|
// We received information about chunks and datacenter info
|
|
// "datarequest" command succeeded
|
|
if ( m_pDataInfo )
|
|
m_pDataInfo->deleteThis();
|
|
|
|
m_pDataInfo = arrResults[0]->MakeCopy();
|
|
|
|
// Special handling for base disk turequired
|
|
char const *szTuRequiredDc = m_pDataInfo->GetString( "turequired" );
|
|
if ( !V_strcmp( szTuRequiredDc, "0" ) )
|
|
m_pDataInfo->SetString( "turequired", "00000000" );
|
|
|
|
DevMsg( "Datacenter::RequestUpdate - data info received\n" );
|
|
KeyValuesDumpAsDevMsg( m_pDataInfo, 1 );
|
|
|
|
// Destroy prev batch
|
|
m_pXlspBatch->Destroy();
|
|
m_pXlspBatch = NULL;
|
|
|
|
// Prepare the new batch
|
|
CUtlVector< KeyValues * > arrBatch;
|
|
if ( KeyValues *pBatch2 = m_pDataInfo->FindKey( "chunks" ) )
|
|
{
|
|
bool bTitleSupportsXlspPatch = !!( g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_XLSPPATCH_SUPPORTED );
|
|
while ( KeyValues *pReq = pBatch2->GetFirstTrueSubKey() )
|
|
{
|
|
int numChunks = pReq->GetInt( "chunks", 0 );
|
|
if ( KeyValues *pChunks = pReq->FindKey( "chunks" ) )
|
|
{
|
|
pReq->RemoveSubKey( pChunks );
|
|
pReq->SetInt( "version", m_pDataInfo->GetInt( "version" ) );
|
|
pChunks->deleteThis();
|
|
}
|
|
|
|
bool bShouldRequest = true;
|
|
if ( !V_stricmp( "data_patch", pReq->GetName() ) && !bTitleSupportsXlspPatch )
|
|
bShouldRequest = false;
|
|
|
|
if ( bShouldRequest )
|
|
{
|
|
if ( !numChunks )
|
|
{
|
|
arrBatch.AddToTail( pReq->MakeCopy() );
|
|
}
|
|
else
|
|
{
|
|
for ( int k = 0; k < numChunks; ++ k )
|
|
{
|
|
pReq->SetInt( "chunk", k + 1 );
|
|
arrBatch.AddToTail( pReq->MakeCopy() );
|
|
}
|
|
}
|
|
}
|
|
|
|
pBatch2->RemoveSubKey( pReq );
|
|
pReq->deleteThis();
|
|
}
|
|
}
|
|
|
|
if ( !arrBatch.Count() )
|
|
{
|
|
bSuccessfulUpdate = true;
|
|
}
|
|
// Start our new batch
|
|
else
|
|
{
|
|
DevMsg( "Datacenter::RequestUpdate - info chunks - requesting %d packets\n", arrBatch.Count() );
|
|
m_pXlspBatch = new CXlspConnectionCmdBatch( m_pXlspConnection, arrBatch, mm_datacenter_retry_infochunks_attempts.GetInt() );
|
|
m_eState = STATE_REQUESTING_CHUNKS;
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case STATE_REQUESTING_CHUNKS:
|
|
bSuccessfulUpdate = m_pXlspBatch->HasAllResults();
|
|
DevMsg( "Datacenter::RequestUpdate - info chunks - received %d packets\n", arrResults.Count() );
|
|
if ( bSuccessfulUpdate && arrResults.Count() )
|
|
{
|
|
if ( m_pInfoChunks )
|
|
m_pInfoChunks->deleteThis();
|
|
|
|
m_pInfoChunks = m_pDataInfo->MakeCopy();
|
|
|
|
for ( int k = 0; k < arrResults.Count(); ++ k )
|
|
{
|
|
m_pInfoChunks->MergeFrom( arrResults[k], KeyValues::MERGE_KV_UPDATE );
|
|
DevMsg( 2, "-- info chunk %d\n", k + 1 );
|
|
KeyValuesDumpAsDevMsg( arrResults[k], 1, 2 );
|
|
}
|
|
|
|
DevMsg( 2, "-- Full info chunks:\n" );
|
|
KeyValuesDumpAsDevMsg( m_pInfoChunks, 1,2 );
|
|
}
|
|
break;
|
|
}
|
|
#elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR ) && !defined( SWDS )
|
|
if ( GGCClient() )
|
|
{
|
|
CGCClientJobDataRequest *pJob = (CGCClientJobDataRequest *)GGCClient()->GetJobMgr().GetPJob( m_JobIDDataRequest );
|
|
if ( pJob )
|
|
{
|
|
if ( !pJob->BComplete() )
|
|
return;
|
|
|
|
bSuccessfulUpdate = pJob->BSuccess();
|
|
if ( bSuccessfulUpdate )
|
|
{
|
|
if ( m_pDataInfo )
|
|
m_pDataInfo->deleteThis();
|
|
if ( m_pInfoChunks )
|
|
m_pInfoChunks->deleteThis();
|
|
|
|
m_pDataInfo = pJob->GetResults()->MakeCopy();
|
|
m_pInfoChunks = pJob->GetResults()->MakeCopy();
|
|
}
|
|
|
|
pJob->Finish();
|
|
}
|
|
}
|
|
#else
|
|
#endif
|
|
|
|
RequestStop();
|
|
|
|
#if !defined( _PS3 ) && !defined( NO_STEAM_GAMECOORDINATOR )
|
|
DevMsg( "Datacenter::RequestUpdate %s\n", bSuccessfulUpdate ? "successful" : "failed" );
|
|
#endif
|
|
|
|
m_bCanReachDatacenter = bSuccessfulUpdate;
|
|
|
|
if ( bSuccessfulUpdate )
|
|
{
|
|
m_flNextSearchTime = Plat_FloatTime() + mm_datacenter_update_interval.GetFloat();
|
|
|
|
OnDatacenterInfoUpdated();
|
|
}
|
|
}
|
|
|
|
static void UnpackPatchBinary( KeyValues *pPatch, CUtlBuffer &buf )
|
|
{
|
|
int nSize = pPatch->GetInt( "size" );
|
|
if ( !nSize )
|
|
{
|
|
buf.Purge();
|
|
return;
|
|
}
|
|
|
|
buf.EnsureCapacity( nSize + sizeof( uint64 ) ); // include extra room for alignments
|
|
buf.SeekPut( buf.SEEK_HEAD, nSize ); // set the data size
|
|
|
|
unsigned char *pchBuffer = ( unsigned char * ) buf.Base();
|
|
memset( pchBuffer, 0, nSize );
|
|
|
|
for ( KeyValues *sub = pPatch->GetFirstTrueSubKey(); sub; sub = sub->GetNextTrueSubKey() )
|
|
{
|
|
for( KeyValues *val = sub->GetFirstValue(); val; val = val->GetNextValue() )
|
|
{
|
|
if ( !val->GetName()[0] && val->GetDataType() == KeyValues::TYPE_UINT64 )
|
|
{
|
|
if ( !nSize )
|
|
{
|
|
nSize -= sizeof( uint64 );
|
|
goto unpack_error;
|
|
}
|
|
|
|
uint64 ui = val->GetUint64();
|
|
|
|
for ( int k = 0; k < MIN( nSize, sizeof( ui ) ); ++ k )
|
|
{
|
|
pchBuffer[k] = ( unsigned char )( ( ui >> ( 8 * k ) ) & 0xFF );
|
|
}
|
|
|
|
nSize -= MIN( nSize, sizeof( ui ) );
|
|
pchBuffer += MIN( nSize, sizeof( ui ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// If all the bytes were correctly written to buffer, then the unpack succeeded
|
|
if ( !nSize )
|
|
return;
|
|
|
|
unpack_error:
|
|
// Transmitted patch indicated a size different than transmitted data!
|
|
DevWarning( "UnpackPatchBinary error: %d size indicated, but %d bytes failed to unpack!\n", buf.TellPut(), nSize );
|
|
buf.Purge();
|
|
return;
|
|
}
|
|
|
|
void CDatacenter::OnDatacenterInfoUpdated()
|
|
{
|
|
#ifdef _X360
|
|
// Check if the client is running the required TU
|
|
char const *szTuRequired = m_pInfoChunks->GetString( "turequired" );
|
|
if ( Q_stricmp( szTuRequired, MatchSession_GetTuInstalledString() ) )
|
|
{
|
|
DevWarning( "CDatacenter::OnDatacenterInfoUpdated - wrong TU version (datacenter requires = %s, installed = %s)\n",
|
|
szTuRequired, MatchSession_GetTuInstalledString() );
|
|
return;
|
|
}
|
|
|
|
// Don't try to mount the update in the middle of the game
|
|
if ( IsLocalClientConnectedToServer() )
|
|
{
|
|
m_numDelayedMountAttempts = mm_datacenter_delay_mount_frames.GetInt(); // attempt to mount again when disconnected
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
// Downloaded update version
|
|
int nUpdateVersion = m_pInfoChunks->GetInt( "version", 0 );
|
|
|
|
bool bXlspPatchFileMounted = false;
|
|
#ifdef _X360
|
|
// Filesystem needs to mount new patch binary
|
|
if ( KeyValues *pPatch = m_pInfoChunks->FindKey( "patch" ) )
|
|
{
|
|
if ( nUpdateVersion > m_nVersionApplied )
|
|
{
|
|
CUtlBuffer bufPatchData;
|
|
UnpackPatchBinary( pPatch, bufPatchData );
|
|
DevMsg( "CDatacenter::OnDatacenterInfoUpdated mounting patch binary data: %d bytes at %p\n", bufPatchData.TellPut(), bufPatchData.Base() );
|
|
if ( g_pFullFileSystem->AddXLSPUpdateSearchPath( bufPatchData.Base(), bufPatchData.TellPut() ) )
|
|
{
|
|
DevMsg( "CDatacenter::OnDatacenterInfoUpdated successfully mounted patch binary data (version %d -> %d)\n", m_nVersionApplied, nUpdateVersion );
|
|
m_nVersionApplied = nUpdateVersion;
|
|
bXlspPatchFileMounted = true;
|
|
|
|
// Try to apply cvar section
|
|
CUtlBuffer bufCvarCrypt, bufCvarInfo;
|
|
bool bMainFilePresent = g_pFullFileSystem->ReadFile( "scripts/main.nut", NULL, bufCvarCrypt );
|
|
uint uiXorMaskDecrypt = Q_atoi( szTuRequired ) ^ bufCvarCrypt.TellPut();
|
|
if ( bMainFilePresent &&
|
|
DecryptBuffer( bufCvarCrypt, bufCvarInfo, uiXorMaskDecrypt ) )
|
|
{
|
|
KeyValues *cvkv = new KeyValues( "" );
|
|
KeyValues::AutoDelete autodelete_cvkv( cvkv );
|
|
bufCvarInfo.ActivateByteSwapping( !CByteswap::IsMachineBigEndian() );
|
|
if ( cvkv->ReadAsBinary( bufCvarInfo ) )
|
|
{
|
|
// Process the cvar section and patch them up
|
|
for ( KeyValues *kvName = cvkv->FindKey( "cvar" )->GetFirstValue(); kvName; kvName = kvName->GetNextValue() )
|
|
{
|
|
ConVarRef cvRef( kvName->GetName(), true ); // deliberately non-static, enumerating in a loop
|
|
if ( !cvRef.IsValid() )
|
|
{
|
|
#ifndef _CERT
|
|
DevWarning( "CDatacenter::OnDatacenterInfoUpdated failed to update cvar '%s' = '%s'\n", kvName->GetName(), kvName->GetString() );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#ifndef _CERT
|
|
DevMsg( "CDatacenter::OnDatacenterInfoUpdated updating cvar '%s' = '%s' -> '%s'\n", kvName->GetName(), cvRef.GetString(), kvName->GetString() );
|
|
#endif
|
|
cvRef.SetValue( kvName->GetString() );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DevMsg( "CDatacenter::OnDatacenterInfoUpdated failed to mount version %d (was %d)\n", nUpdateVersion, m_nVersionApplied );
|
|
// Give ourselves a retry attempt
|
|
++ m_numDelayedMountAttempts;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Signal all other subscribers about the update
|
|
if ( KeyValues *newEvent = new KeyValues( "OnDatacenterUpdate", "version", nUpdateVersion ) )
|
|
{
|
|
if ( bXlspPatchFileMounted )
|
|
newEvent->SetInt( "filesystempatch", 1 );
|
|
|
|
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( newEvent );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CDatacenterCmdBatchImpl
|
|
//
|
|
|
|
CDatacenterCmdBatchImpl::CDatacenterCmdBatchImpl( CDatacenter *pParent, bool bMustSupportPII ) :
|
|
#ifdef _X360
|
|
m_pXlspConnection( NULL ),
|
|
m_pXlspBatch( NULL ),
|
|
#endif
|
|
m_pParent( pParent ),
|
|
m_arrCommands(),
|
|
m_numRetriesAllowedPerCmd( 0 ),
|
|
m_flRetryCmdTimeout( 0 ),
|
|
m_bDestroyWhenFinished( true ),
|
|
m_bMustSupportPII( bMustSupportPII )
|
|
{
|
|
}
|
|
|
|
void CDatacenterCmdBatchImpl::AddCommand( KeyValues *pCommand )
|
|
{
|
|
if ( !pCommand )
|
|
return;
|
|
|
|
#ifdef _X360
|
|
if ( m_pXlspBatch )
|
|
{
|
|
Warning( "CDatacenterCmdBatchImpl::AddCommand after already initiated batch processing!\n" );
|
|
Assert( 0 );
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
m_arrCommands.AddToTail( pCommand->MakeCopy() );
|
|
}
|
|
|
|
bool CDatacenterCmdBatchImpl::IsFinished()
|
|
{
|
|
#ifdef _X360
|
|
return m_pXlspBatch && m_pXlspBatch->IsFinished();
|
|
#else
|
|
return true;
|
|
#endif
|
|
}
|
|
|
|
int CDatacenterCmdBatchImpl::GetNumResults()
|
|
{
|
|
#ifdef _X360
|
|
return m_pXlspBatch ? m_pXlspBatch->GetResults().Count() : 0;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
KeyValues * CDatacenterCmdBatchImpl::GetResult( int idx )
|
|
{
|
|
#ifdef _X360
|
|
if ( !m_pXlspBatch )
|
|
return NULL;
|
|
if ( !m_pXlspBatch->GetResults().IsValidIndex( idx ) )
|
|
return NULL;
|
|
return m_pXlspBatch->GetResults()[idx];
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
void CDatacenterCmdBatchImpl::Destroy()
|
|
{
|
|
if ( m_pParent )
|
|
m_pParent->OnCmdBatchReleased( this );
|
|
|
|
for ( int k = 0; k < m_arrCommands.Count(); ++ k )
|
|
{
|
|
m_arrCommands[k]->deleteThis();
|
|
}
|
|
m_arrCommands.Purge();
|
|
|
|
#ifdef _X360
|
|
if ( m_pXlspBatch )
|
|
{
|
|
m_pXlspBatch->Destroy();
|
|
m_pXlspBatch = NULL;
|
|
}
|
|
|
|
if ( m_pXlspConnection )
|
|
{
|
|
m_pXlspConnection->Destroy();
|
|
m_pXlspConnection = NULL;
|
|
}
|
|
#endif
|
|
|
|
delete this;
|
|
}
|
|
|
|
void CDatacenterCmdBatchImpl::SetDestroyWhenFinished( bool bDestroyWhenFinished )
|
|
{
|
|
m_bDestroyWhenFinished = bDestroyWhenFinished;
|
|
}
|
|
|
|
void CDatacenterCmdBatchImpl::SetNumRetriesAllowedPerCmd( int numRetriesAllowed )
|
|
{
|
|
m_numRetriesAllowedPerCmd = numRetriesAllowed;
|
|
}
|
|
|
|
void CDatacenterCmdBatchImpl::SetRetryCmdTimeout( float flRetryCmdTimeout )
|
|
{
|
|
m_flRetryCmdTimeout = flRetryCmdTimeout;
|
|
}
|
|
|
|
void CDatacenterCmdBatchImpl::Update()
|
|
{
|
|
#ifdef _X360
|
|
if ( !m_pXlspBatch && m_arrCommands.Count() )
|
|
{
|
|
// Need to kick off XLSP batch processing
|
|
m_pXlspConnection = new CXlspConnection( m_bMustSupportPII );
|
|
m_pXlspBatch = new CXlspConnectionCmdBatch( m_pXlspConnection, m_arrCommands, m_numRetriesAllowedPerCmd, m_flRetryCmdTimeout );
|
|
return;
|
|
}
|
|
|
|
if ( m_pXlspBatch )
|
|
{
|
|
m_pXlspBatch->Update();
|
|
if ( m_pXlspBatch->IsFinished() )
|
|
{
|
|
// Detach this cmd batch processor from the frame updates
|
|
if ( m_pParent )
|
|
m_pParent->OnCmdBatchReleased( this );
|
|
|
|
// Notify listeners
|
|
// Signal that we are finished with cmd batch
|
|
KeyValues *pNotify = new KeyValues( "OnDatacenterCmdBatchUpdate", "update", "finished" );
|
|
pNotify->SetPtr( "cmdbatch", this );
|
|
pNotify->SetInt( "results", m_pXlspBatch->GetResults().Count() );
|
|
g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( pNotify );
|
|
|
|
// Destroy ourselves since we are finished
|
|
if ( m_bDestroyWhenFinished )
|
|
this->Destroy();
|
|
}
|
|
}
|
|
#else
|
|
// Destroy ourselves since we cannot do any work anyway
|
|
if ( m_bDestroyWhenFinished )
|
|
this->Destroy();
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|