Counter Strike : Global Offensive Source Code
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.

1007 lines
29 KiB

  1. //========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=====================================================================================//
  6. #include "uigamedata.h"
  7. #include "engineinterface.h"
  8. #include "vgui/ILocalize.h"
  9. #include "matchmaking/imatchframework.h"
  10. #include "filesystem.h"
  11. #include "fmtstr.h"
  12. #ifndef NO_STEAM
  13. #include "steam/steam_api.h"
  14. #endif
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. using namespace BaseModUI;
  18. using namespace vgui;
  19. #ifndef ERROR_SUCCESS
  20. #define ERROR_SUCCESS 0
  21. #endif
  22. #ifndef ERROR_IO_INCOMPLETE
  23. #define ERROR_IO_INCOMPLETE 996L
  24. #endif
  25. #if 0
  26. //
  27. // Storage device selection
  28. //
  29. //-----------------------------------------------------------------------------
  30. // Purpose: A storage device has been connected, update our settings and anything else
  31. //-----------------------------------------------------------------------------
  32. namespace BaseModUI {
  33. class CAsyncCtxUIOnDeviceAttached : public CUIGameData::CAsyncJobContext
  34. {
  35. public:
  36. explicit CAsyncCtxUIOnDeviceAttached( int iController );
  37. ~CAsyncCtxUIOnDeviceAttached();
  38. virtual void ExecuteAsync();
  39. virtual void Completed();
  40. uint GetContainerOpenResult( void ) { return m_ContainerOpenResult; }
  41. int GetController() const { return m_iController; }
  42. private:
  43. uint m_ContainerOpenResult;
  44. int m_iController;
  45. };
  46. CAsyncCtxUIOnDeviceAttached::CAsyncCtxUIOnDeviceAttached( int iController ) :
  47. CUIGameData::CAsyncJobContext( 3.0f ), // Storage device info for at least 3 seconds
  48. m_ContainerOpenResult( ERROR_SUCCESS ),
  49. m_iController( iController )
  50. {
  51. //CUIGameData::Get()->ShowMessageDialog( MD_CHECKING_STORAGE_DEVICE );
  52. }
  53. CAsyncCtxUIOnDeviceAttached::~CAsyncCtxUIOnDeviceAttached()
  54. {
  55. //CUIGameData::Get()->CloseMessageDialog( 0 );
  56. }
  57. void CAsyncCtxUIOnDeviceAttached::ExecuteAsync()
  58. {
  59. // Asynchronously do the tasks that don't interact with the command buffer
  60. // g_pFullFileSystem->DiscoverDLC( GetController() ); - don't call DiscoverDLC here
  61. // Open user settings and save game container here
  62. m_ContainerOpenResult = engine->OnStorageDeviceAttached( GetController() );
  63. if ( m_ContainerOpenResult != ERROR_SUCCESS )
  64. return;
  65. }
  66. ConVar ui_start_dlc_time_pump( "ui_start_dlc_time_pump", "30" );
  67. ConVar ui_start_dlc_time_loaded( "ui_start_dlc_time_loaded", "150" );
  68. ConVar ui_start_dlc_time_corrupt( "ui_start_dlc_time_corrupt", "300" );
  69. CON_COMMAND_F( ui_pump_dlc_mount_corrupt, "", FCVAR_DEVELOPMENTONLY )
  70. {
  71. int nStage = -1;
  72. if ( args.ArgC() > 1 )
  73. {
  74. nStage = Q_atoi( args.Arg( 1 ) );
  75. }
  76. DevMsg( 2, "ui_pump_dlc_mount_corrupt %d\n", nStage );
  77. int nCorruptDLCs = g_pFullFileSystem->IsAnyCorruptDLC();
  78. while ( nStage >= 0 && nStage < nCorruptDLCs )
  79. {
  80. static wchar_t wszDlcInfo[ 3 * MAX_PATH ] = {0};
  81. if ( !g_pFullFileSystem->GetAnyCorruptDLCInfo( nStage, wszDlcInfo, sizeof( wszDlcInfo ) ) )
  82. {
  83. ++ nStage;
  84. continue;
  85. }
  86. // information text
  87. if ( wchar_t *wszExplanation = g_pVGuiLocalize->Find( "#SFUI_MsgBx_DlcCorruptTxt" ) )
  88. {
  89. int wlen = Q_wcslen( wszDlcInfo );
  90. Q_wcsncpy( wszDlcInfo + wlen, wszExplanation, sizeof( wszDlcInfo ) - 2 * wlen );
  91. }
  92. // We've got a corrupt DLC, put it up on the spinner
  93. CUIGameData::Get()->UpdateWaitPanel( wszDlcInfo, 0.0f );
  94. engine->ClientCmd( CFmtStr( "echo corruptdlc%d; wait %d; ui_pump_dlc_mount_corrupt %d;",
  95. nStage + 1, ui_start_dlc_time_corrupt.GetInt(), nStage + 1 ) );
  96. return;
  97. }
  98. // end of dlc mounting phases
  99. CUIGameData::Get()->OnCompletedAsyncDeviceAttached( NULL );
  100. }
  101. CON_COMMAND_F( ui_pump_dlc_mount_content, "", FCVAR_DEVELOPMENTONLY )
  102. {
  103. int nStage = -1;
  104. if ( args.ArgC() > 1 )
  105. {
  106. nStage = Q_atoi( args.Arg( 1 ) );
  107. }
  108. DevMsg( 2, "ui_pump_dlc_mount_content %d\n", nStage );
  109. bool bSearchPathMounted = false;
  110. int numDlcsContent = g_pFullFileSystem->IsAnyDLCPresent( &bSearchPathMounted );
  111. while ( nStage >= 0 && nStage < numDlcsContent )
  112. {
  113. static wchar_t wszDlcInfo[ 3 * MAX_PATH ] = {0};
  114. unsigned int ulMask;
  115. if ( !g_pFullFileSystem->GetAnyDLCInfo( nStage, &ulMask, wszDlcInfo, sizeof( wszDlcInfo ) ) )
  116. {
  117. ++ nStage;
  118. continue;
  119. }
  120. // information text
  121. if ( wchar_t *wszExplanation = g_pVGuiLocalize->Find( "#SFUI_MsgBx_DlcMountedTxt" ) )
  122. {
  123. int wlen = Q_wcslen( wszDlcInfo );
  124. Q_wcsncpy( wszDlcInfo + wlen, wszExplanation, sizeof( wszDlcInfo ) - 2 * wlen );
  125. }
  126. // We've got a corrupt DLC, put it up on the spinner
  127. CUIGameData::Get()->UpdateWaitPanel( wszDlcInfo, 0.0f );
  128. engine->ClientCmd( CFmtStr( "echo mounteddlc%d (0x%08X); wait %d; ui_pump_dlc_mount_content %d;",
  129. nStage + 1, ulMask, ui_start_dlc_time_loaded.GetInt(), nStage + 1 ) );
  130. return;
  131. }
  132. // Done displaying found content, show corrupt
  133. engine->ClientCmd( "ui_pump_dlc_mount_corrupt 0" );
  134. }
  135. CON_COMMAND_F( ui_pump_dlc_mount_stage, "", FCVAR_DEVELOPMENTONLY )
  136. {
  137. // execute in order
  138. int nStage = -1;
  139. if ( args.ArgC() > 1 )
  140. {
  141. nStage = Q_atoi( args.Arg( 1 ) );
  142. }
  143. DevMsg( 2, "ui_pump_dlc_mount_stage %d\n", nStage );
  144. static char const *s_arrClientCmdsDlcMount[] =
  145. {
  146. "net_reloadgameevents",
  147. "hud_reloadscheme",
  148. "gameinstructor_reload_lessons",
  149. "scenefilecache_reload",
  150. "cc_reload",
  151. "rr_reloadresponsesystems",
  152. "cl_soundemitter_reload",
  153. "sv_soundemitter_reload",
  154. };
  155. if ( nStage >= 0 && nStage < ARRAYSIZE( s_arrClientCmdsDlcMount ) )
  156. {
  157. // execute in phases, each command deferred occurs on main thread as required
  158. // adding a wait <frames> to let spinner clock a little
  159. // no way to solve any one phase that blocks for too long...this is good enough
  160. engine->ClientCmd( CFmtStr( "wait %d; %s; ui_pump_dlc_mount_stage %d;",
  161. ui_start_dlc_time_pump.GetInt(),
  162. s_arrClientCmdsDlcMount[ nStage ],
  163. nStage + 1 ) );
  164. return;
  165. }
  166. // Done mounting
  167. engine->ClientCmd( "ui_pump_dlc_mount_content 0" );
  168. }
  169. void CAsyncCtxUIOnDeviceAttached::Completed()
  170. {
  171. bool bDLCSearchPathMounted = false;
  172. if ( GetContainerOpenResult() == ERROR_SUCCESS &&
  173. g_pFullFileSystem->IsAnyDLCPresent( &bDLCSearchPathMounted ) )
  174. {
  175. if ( !( CUIGameData::Get()->SelectStorageDevicePolicy() & STORAGE_DEVICE_ASYNC ) )
  176. {
  177. Warning( "<vitaliy> DLC discovered during sync storage mount (not mounted)!\n" );
  178. goto completed_done;
  179. }
  180. if ( !bDLCSearchPathMounted )
  181. {
  182. // add the DLC search paths if they exist
  183. // this must be done on the main thread
  184. // the DLC search path mount will incur a quick synchronous hit due to zip mounting
  185. g_pFullFileSystem->AddDLCSearchPaths();
  186. // new DLC data may trump prior data, so need to signal isolated system reloads
  187. engine->ClientCmd( "ui_pump_dlc_mount_stage 0" );
  188. return;
  189. }
  190. }
  191. // No valid DLC was discovered, check if we discovered some corrupt DLC
  192. if ( g_pFullFileSystem->IsAnyCorruptDLC() )
  193. {
  194. if ( !( CUIGameData::Get()->SelectStorageDevicePolicy() & STORAGE_DEVICE_ASYNC ) )
  195. {
  196. Warning( "<vitaliy> DLC discovered during sync storage mount (corrupt)!\n" );
  197. goto completed_done;
  198. }
  199. // need to show just corrupt DLC information
  200. engine->ClientCmd( CFmtStr( "ui_pump_dlc_mount_corrupt %d", 0 ) );
  201. return;
  202. }
  203. // Otherwise we are done attaching storage right now
  204. completed_done:
  205. CUIGameData::Get()->OnCompletedAsyncDeviceAttached( this );
  206. }
  207. }
  208. #endif
  209. void CUIGameData::RunFrame_Storage()
  210. {
  211. #ifdef _PS3
  212. GetPs3SaveSteamInfoProvider()->RunFrame();
  213. #endif
  214. #if 0
  215. // Check to see if a pending async task has already finished
  216. if ( m_pAsyncJob && !m_pAsyncJob->m_hThreadHandle )
  217. {
  218. m_pAsyncJob->Completed();
  219. delete m_pAsyncJob;
  220. m_pAsyncJob = NULL;
  221. }
  222. if( m_bWaitingForStorageDeviceHandle )
  223. {
  224. //the select device blade just closed, get the selected device
  225. DWORD ret = xboxsystem->GetOverlappedResult( m_hStorageDeviceChangeHandle, NULL, true );
  226. if ( ret != ERROR_IO_INCOMPLETE )
  227. {
  228. // Done waiting
  229. xboxsystem->ReleaseAsyncHandle( m_hStorageDeviceChangeHandle );
  230. m_bWaitingForStorageDeviceHandle = false;
  231. // If we selected something, validate it
  232. if ( m_iStorageID != XBX_INVALID_STORAGE_ID )
  233. {
  234. OnSetStorageDeviceId( m_iStorageController, m_iStorageID );
  235. }
  236. else
  237. {
  238. CloseWaitScreen( NULL, "ReportNoDeviceSelected" );
  239. if ( m_pSelectStorageClient )
  240. {
  241. m_pSelectStorageClient->OnDeviceFail( ISelectStorageDeviceClient::FAIL_NOT_SELECTED );
  242. m_pSelectStorageClient = NULL;
  243. }
  244. }
  245. }
  246. }
  247. #ifdef _PS3
  248. GetPs3SaveSteamInfoProvider()->RunFrame();
  249. #endif
  250. if ( g_pGameSteamCloudSync )
  251. g_pGameSteamCloudSync->RunFrame();
  252. #endif
  253. }
  254. #if 0
  255. //
  256. // CChangeStorageDevice
  257. //
  258. // Should be used when user wants to change storage device
  259. //
  260. static CChangeStorageDevice *s_pChangeStorageDeviceCallback = NULL;
  261. static void CChangeStorageDevice_Continue()
  262. {
  263. s_pChangeStorageDeviceCallback->DeviceChangeCompleted( true );
  264. delete s_pChangeStorageDeviceCallback;
  265. s_pChangeStorageDeviceCallback = NULL;
  266. }
  267. static void CChangeStorageDevice_SelectAgain()
  268. {
  269. CUIGameData::Get()->SelectStorageDevice( s_pChangeStorageDeviceCallback );
  270. s_pChangeStorageDeviceCallback = NULL;
  271. }
  272. CChangeStorageDevice::CChangeStorageDevice( int iCtrlr ) :
  273. m_iCtrlr( iCtrlr ),
  274. m_bAllowDeclined( true ),
  275. m_bForce( true ),
  276. m_nConfirmationData( 0 )
  277. {
  278. // Just in case clean up (if dialogs were cancelled due to user sign out or such)
  279. delete s_pChangeStorageDeviceCallback;
  280. s_pChangeStorageDeviceCallback = NULL;
  281. }
  282. void CChangeStorageDevice::OnDeviceFail( FailReason_t eReason )
  283. {
  284. // Depending if the user had storage device by this moment
  285. // or not we will take different actions:
  286. DWORD dwDevice = XBX_GetStorageDeviceId( GetCtrlrIndex() );
  287. switch ( eReason )
  288. {
  289. case FAIL_ERROR:
  290. case FAIL_NOT_SELECTED:
  291. if ( XBX_DescribeStorageDevice( dwDevice ) )
  292. {
  293. // That's fine user has a valid storage device, didn't want to change
  294. DeviceChangeCompleted( false );
  295. delete this;
  296. return;
  297. }
  298. // otherwise, proceed with the ui msg
  299. }
  300. XBX_SetStorageDeviceId( GetCtrlrIndex(), XBX_STORAGE_DECLINED );
  301. // We don't want to fire notification because there might be unsaved
  302. // preferences changes that were done without a storage device
  303. // no: g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues( "OnProfileStorageAvailable", "iController", GetCtrlrIndex() ) );
  304. m_bAllowDeclined = false;
  305. GenericConfirmation* confirmation =
  306. static_cast<GenericConfirmation*>( CBaseModPanel::GetSingleton().
  307. OpenWindow( WT_GENERICCONFIRMATION, CUIGameData::Get()->GetParentWindowForSystemMessageBox(), false ) );
  308. GenericConfirmation::Data_t data;
  309. switch ( eReason )
  310. {
  311. case FAIL_ERROR:
  312. case FAIL_NOT_SELECTED:
  313. data.pWindowTitle = "#SFUI_MsgBx_AttractDeviceNoneC";
  314. data.pMessageText = "#SFUI_MsgBx_AttractDeviceNoneTxt";
  315. break;
  316. case FAIL_FULL:
  317. data.pWindowTitle = "#SFUI_MsgBx_AttractDeviceFullC";
  318. data.pMessageText = "#SFUI_MsgBx_AttractDeviceFullTxt";
  319. break;
  320. case FAIL_CORRUPT:
  321. default:
  322. data.pWindowTitle = "#SFUI_MsgBx_AttractDeviceCorruptC";
  323. data.pMessageText = "#SFUI_MsgBx_AttractDeviceCorruptTxt";
  324. break;
  325. }
  326. data.bOkButtonEnabled = true;
  327. data.bCancelButtonEnabled = true;
  328. s_pChangeStorageDeviceCallback = this;
  329. data.pfnOkCallback = CChangeStorageDevice_Continue;
  330. data.pfnCancelCallback = CChangeStorageDevice_SelectAgain;
  331. // WARNING! WARNING! WARNING!
  332. // The nature of Generic Confirmation is that it will be silently replaced
  333. // with another Generic Confirmation if a system event occurs
  334. // e.g. user unplugs controller, user changes storage device, etc.
  335. // If that happens neither OK nor CANCEL callbacks WILL NOT BE CALLED
  336. // The state machine cannot depend on either callback advancing the
  337. // state because in some situations neither callback can fire and the
  338. // confirmation dismissed/closed/replaced.
  339. // State machine must implement OnThink and check if the required
  340. // confirmation box is still present!
  341. // This code implements some sort of fallback - it deletes the static
  342. // confirmation data when a new storage device change is requested.
  343. // Vitaliy -- 9/26/2009
  344. //
  345. m_nConfirmationData = confirmation->SetUsageData(data);
  346. }
  347. void CChangeStorageDevice::OnDeviceSelected()
  348. {
  349. if ( UI_IsDebug() )
  350. {
  351. Msg( "[GAMEUI] CChangeStorageDevice::OnDeviceSelected( 0x%08X )\n",
  352. XBX_GetStorageDeviceId( GetCtrlrIndex() ) );
  353. }
  354. CUIGameData::Get()->UpdateWaitPanel( "#SFUI_WaitScreen_SignOnSucceded" );
  355. }
  356. void CChangeStorageDevice::AfterDeviceMounted()
  357. {
  358. DeviceChangeCompleted( true );
  359. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues( "OnProfileStorageAvailable", "iController", GetCtrlrIndex() ) );
  360. delete this;
  361. }
  362. void CChangeStorageDevice::DeviceChangeCompleted( bool bChanged )
  363. {
  364. if ( bChanged )
  365. {
  366. Msg( "CChangeStorageDevice::DeviceChangeCompleted for ctrlr%d device 0x%08X\n",
  367. GetCtrlrIndex(), XBX_GetStorageDeviceId( GetCtrlrIndex() ) );
  368. }
  369. else
  370. {
  371. Msg( "CChangeStorageDevice::DeviceChangeCompleted - ctrlr%d is keeping device 0x%08X\n",
  372. GetCtrlrIndex(), XBX_GetStorageDeviceId( GetCtrlrIndex() ) );
  373. }
  374. }
  375. //=============================================================================
  376. //
  377. //=============================================================================
  378. class CChangeStorageDeviceChained : public CChangeStorageDevice
  379. {
  380. public:
  381. typedef CChangeStorageDevice BaseClass;
  382. explicit CChangeStorageDeviceChained( int iCtrlrs[2] ) :
  383. BaseClass( iCtrlrs[0] ), m_nChainCtrlr( iCtrlrs[1] ) {}
  384. virtual void DeviceChangeCompleted( bool bChanged )
  385. {
  386. // Defer to the base class
  387. BaseClass::DeviceChangeCompleted( bChanged );
  388. // If we have a chain target, then call this off again
  389. if ( m_nChainCtrlr >= 0 )
  390. {
  391. CUIGameData::Get()->SelectStorageDevice( new CChangeStorageDevice( m_nChainCtrlr ) );
  392. }
  393. }
  394. private:
  395. int m_nChainCtrlr;
  396. };
  397. void OnStorageDevicesChangedSelectNewDevice()
  398. {
  399. #ifdef _GAMECONSOLE
  400. int numChangedCtrlrs = 0;
  401. int nChangedCtrlrs[2] = { -1, -1 }; // We can only have two users (split-screen)
  402. for ( DWORD i = 0; i < XBX_GetNumGameUsers(); ++ i )
  403. {
  404. int iController = XBX_GetUserId( i );
  405. // Guests can't choose a storage device!
  406. if ( XBX_GetUserIsGuest( i ) )
  407. continue;
  408. int nStorageID = XBX_GetStorageDeviceId( iController );
  409. if ( nStorageID == XBX_INVALID_STORAGE_ID )
  410. {
  411. // A controller's device has changed, and we'll need to prompt them to replace it
  412. nChangedCtrlrs[numChangedCtrlrs] = iController;
  413. numChangedCtrlrs++;
  414. }
  415. }
  416. // If a controller changed, then start off our device change dialogs
  417. if ( numChangedCtrlrs )
  418. {
  419. CUIGameData::Get()->SelectStorageDevice( new CChangeStorageDeviceChained( nChangedCtrlrs ) );
  420. }
  421. #endif // _GAMECONSOLE
  422. }
  423. void StorageDevice_SelectAllNow()
  424. {
  425. #ifdef _GAMECONSOLE
  426. int numChangedCtrlrs = 0;
  427. int nChangedCtrlrs[2] = { -1, -1 }; // We can only have two users (split-screen)
  428. for ( DWORD i = 0; i < XBX_GetNumGameUsers(); ++ i )
  429. {
  430. int iController = XBX_GetUserId( i );
  431. // Guests can't choose a storage device!
  432. if ( XBX_GetUserIsGuest( i ) )
  433. continue;
  434. int nStorageID = XBX_GetStorageDeviceId( iController );
  435. if ( nStorageID == XBX_INVALID_STORAGE_ID )
  436. {
  437. // A controller's device has changed, and we'll need to prompt them to replace it
  438. nChangedCtrlrs[numChangedCtrlrs] = iController;
  439. numChangedCtrlrs++;
  440. }
  441. }
  442. // If a controller changed, then start off our device change dialogs
  443. if ( numChangedCtrlrs )
  444. {
  445. CUIGameData::Get()->SelectStorageDevice( new CChangeStorageDeviceChained( nChangedCtrlrs ) );
  446. }
  447. #endif // _GAMECONSOLE
  448. }
  449. //=============================================================================
  450. //This is where we open the XUI pannel to let the user select the current storage device.
  451. bool CUIGameData::SelectStorageDevice( ISelectStorageDeviceClient *pSelectClient )
  452. {
  453. #ifdef _GAMECONSOLE
  454. if ( !pSelectClient )
  455. return false;
  456. int iController = pSelectClient->GetCtrlrIndex();
  457. bool bAllowDeclined = pSelectClient->AllowDeclined();
  458. bool bForceDisplay = pSelectClient->ForceSelector();
  459. bool bCheckCtrlr = !pSelectClient->AllowAnyController();
  460. if ( UI_IsDebug() )
  461. {
  462. Msg( "[GAMEUI] SelectStorageDevice( ctrlr=%d; %d, %d ), waiting=%d\n",
  463. iController, bAllowDeclined, bForceDisplay, m_bWaitingForStorageDeviceHandle );
  464. }
  465. if ( bCheckCtrlr )
  466. {
  467. // Check if the game is in guest mode
  468. if ( XBX_GetPrimaryUserIsGuest() )
  469. {
  470. Warning( "[GAMEUI] SelectStorageDevice for guest!\n" );
  471. pSelectClient->OnDeviceFail( ISelectStorageDeviceClient::FAIL_ERROR );
  472. return false; // go away, no storage for guests
  473. }
  474. int nSlot = -1;
  475. for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
  476. {
  477. int iCtrlr = XBX_GetUserId( k );
  478. if ( iCtrlr != iController )
  479. continue;
  480. else if ( XBX_GetUserIsGuest( k ) )
  481. {
  482. Warning( "[GAMEUI] SelectStorageDevice for guest!\n" );
  483. pSelectClient->OnDeviceFail( ISelectStorageDeviceClient::FAIL_ERROR );
  484. return false; // go away, game thinks you are a guest
  485. }
  486. else
  487. nSlot = k;
  488. }
  489. if ( nSlot < 0 )
  490. {
  491. Warning( "[GAMEUI] SelectStorageDevice for not active ctrlr!\n" );
  492. pSelectClient->OnDeviceFail( ISelectStorageDeviceClient::FAIL_ERROR );
  493. return false; // this controller is not involved in the game, go away
  494. }
  495. }
  496. #ifdef _X360
  497. // Is the controller signed in?
  498. if( XUserGetSigninState( iController ) == eXUserSigninState_NotSignedIn )
  499. {
  500. Warning( "[GAMEUI] SelectStorageDevice for not signed in user!\n" );
  501. pSelectClient->OnDeviceFail( ISelectStorageDeviceClient::FAIL_ERROR );
  502. return false; // not signed in, no device selector
  503. }
  504. // Maybe a guest buddy?
  505. XUSER_SIGNIN_INFO xsi;
  506. if ( ERROR_SUCCESS == XUserGetSigninInfo( iController, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) &&
  507. (xsi.dwInfoFlags & XUSER_INFO_FLAG_GUEST) != 0 )
  508. {
  509. Warning( "[GAMEUI] SelectStorageDevice for LIVE-guest!\n" );
  510. pSelectClient->OnDeviceFail( ISelectStorageDeviceClient::FAIL_ERROR );
  511. return false; // guests don't have device selectors, go away
  512. }
  513. if ( ERROR_SUCCESS != XUserGetSigninInfo( iController, XUSER_GET_SIGNIN_INFO_OFFLINE_XUID_ONLY, &xsi ) )
  514. {
  515. Warning( "[GAMEUI] SelectStorageDevice failed to obtain XUID!\n" );
  516. pSelectClient->OnDeviceFail( ISelectStorageDeviceClient::FAIL_ERROR );
  517. return false; // failed to obtain XUID?
  518. }
  519. #endif
  520. //
  521. // Prevent reentry
  522. //
  523. if( m_bWaitingForStorageDeviceHandle )
  524. {
  525. Warning( "[GAMEUI] SelectStorageDevice is already busy selecting storage device! Cannot re-enter!\n" );
  526. pSelectClient->OnDeviceFail( ISelectStorageDeviceClient::FAIL_ERROR );
  527. return false; // Somebody already selecting a device
  528. }
  529. #if defined( _DEMO ) && defined( _GAMECONSOLE )
  530. // Demo mode cannot have access to storage devices anyway
  531. if ( IsGameConsole() )
  532. {
  533. m_iStorageID = XBX_STORAGE_DECLINED;
  534. m_iStorageController = iController;
  535. m_pSelectStorageClient = pSelectClient;
  536. m_pSelectStorageClient->OnDeviceSelected();
  537. OnCompletedAsyncDeviceAttached( NULL );
  538. return true;
  539. }
  540. #endif
  541. if ( IsPS3() )
  542. {
  543. // PS3 will have only two storage partitions: primary and secondary
  544. if ( iController == (int) XBX_GetPrimaryUserId() )
  545. XBX_SetStorageDeviceId( iController, 1 );
  546. else
  547. XBX_SetStorageDeviceId( iController, 2 );
  548. bForceDisplay = false; // PS3 doesn't display anything, so don't force it
  549. }
  550. // Check if we already have a valid storage device
  551. if ( XBX_GetStorageDeviceId( iController ) != XBX_INVALID_STORAGE_ID &&
  552. ( bAllowDeclined || XBX_GetStorageDeviceId( iController ) != XBX_STORAGE_DECLINED ) &&
  553. ! bForceDisplay )
  554. {
  555. if ( UI_IsDebug() )
  556. {
  557. Msg( "[GAMEUI] SelectStorageDevice - storage id = 0x%08X\n",
  558. XBX_GetStorageDeviceId( iController ) );
  559. }
  560. // Put up a progress that we are loading profile...
  561. if ( SelectStorageDevicePolicy() & STORAGE_DEVICE_ASYNC )
  562. OpenWaitScreen( "#SFUI_WaitScreen_SigningOn" );
  563. m_iStorageID = XBX_GetStorageDeviceId( iController );
  564. m_iStorageController = iController;
  565. m_pSelectStorageClient = pSelectClient;
  566. OnSetStorageDeviceId( iController, XBX_GetStorageDeviceId( iController ) );
  567. // Already have a storage device
  568. return true;
  569. }
  570. // Put up a progress that we are loading profile...
  571. OpenWaitScreen( "#SFUI_WaitScreen_SigningOn", 0.0f );
  572. // NOTE: this shouldn't have a 3 sec time-out as a new wait message is taking over
  573. // the progress when container starts mounting
  574. //open the dialog
  575. m_bWaitingForStorageDeviceHandle = true;
  576. m_hStorageDeviceChangeHandle = xboxsystem->CreateAsyncHandle();
  577. m_iStorageID = XBX_INVALID_STORAGE_ID;
  578. m_iStorageController = iController;
  579. m_pSelectStorageClient = pSelectClient;
  580. xboxsystem->ShowDeviceSelector( iController, bForceDisplay, &m_iStorageID, &m_hStorageDeviceChangeHandle );
  581. #endif
  582. return false;
  583. }
  584. uint32 CUIGameData::SelectStorageDevicePolicy()
  585. {
  586. return
  587. ( IsX360() ? STORAGE_DEVICE_ASYNC : 0 )
  588. ;
  589. }
  590. //=============================================================================
  591. void CUIGameData::OnDeviceAttached()
  592. {
  593. //This is straight from CBasePanel
  594. ExecuteAsync( new CAsyncCtxUIOnDeviceAttached( m_iStorageController ) );
  595. }
  596. //=============================================================================
  597. void CUIGameData::OnCompletedAsyncDeviceAttached( CAsyncCtxUIOnDeviceAttached * job )
  598. {
  599. ISelectStorageDeviceClient *pStorageDeviceClient = m_pSelectStorageClient;
  600. m_pSelectStorageClient = NULL;
  601. uint nRet = job ? job->GetContainerOpenResult() : ERROR_SUCCESS;
  602. if ( nRet != ERROR_SUCCESS )
  603. {
  604. if ( SelectStorageDevicePolicy() & STORAGE_DEVICE_ASYNC )
  605. CloseWaitScreen( NULL, "ReportDeviceCorrupt" );
  606. pStorageDeviceClient->OnDeviceFail( ISelectStorageDeviceClient::FAIL_CORRUPT );
  607. }
  608. else
  609. {
  610. // Notify that data has loaded
  611. pStorageDeviceClient->AfterDeviceMounted();
  612. // Check for opening a new storage device immediately
  613. if ( m_pSelectStorageClient == NULL )
  614. {
  615. // Close down the waiting screen
  616. if ( SelectStorageDevicePolicy() & STORAGE_DEVICE_ASYNC )
  617. CloseWaitScreen( NULL, "OnCompletedAsyncDeviceAttached" );
  618. }
  619. }
  620. }
  621. #ifdef _WIN32
  622. //-------------------------
  623. // Purpose: Job wrapper
  624. //-------------------------
  625. static unsigned UIGameDataJobWrapperFn( void *pvContext )
  626. {
  627. CUIGameData::CAsyncJobContext *pAsync = reinterpret_cast< CUIGameData::CAsyncJobContext * >( pvContext );
  628. float const flTimeStart = Plat_FloatTime();
  629. pAsync->ExecuteAsync();
  630. float const flElapsedTime = Plat_FloatTime() - flTimeStart;
  631. if ( flElapsedTime < pAsync->m_flLeastExecuteTime )
  632. {
  633. ThreadSleep( ( pAsync->m_flLeastExecuteTime - flElapsedTime ) * 1000 );
  634. }
  635. ReleaseThreadHandle( ( ThreadHandle_t ) pAsync->m_hThreadHandle );
  636. pAsync->m_hThreadHandle = NULL;
  637. return 0;
  638. }
  639. #endif
  640. //-----------------------------------------------------------------------------
  641. // Purpose: Enqueues a job function to be called on a separate thread
  642. //-----------------------------------------------------------------------------
  643. void CUIGameData::ExecuteAsync( CAsyncJobContext *pAsync )
  644. {
  645. Assert( !m_pAsyncJob );
  646. Assert( pAsync && !pAsync->m_hThreadHandle );
  647. m_pAsyncJob = pAsync;
  648. #ifdef _WIN32
  649. ThreadHandle_t hHandle = CreateSimpleThread( UIGameDataJobWrapperFn, reinterpret_cast< void * >( pAsync ) );
  650. pAsync->m_hThreadHandle = hHandle;
  651. #ifdef _X360
  652. ThreadSetAffinity( hHandle, XBOX_PROCESSOR_3 );
  653. #endif
  654. #else
  655. // Since we are not running on a separate thread, just fire it all here
  656. m_pAsyncJob = NULL;
  657. pAsync->ExecuteAsync();
  658. pAsync->Completed();
  659. delete pAsync;
  660. #endif
  661. }
  662. //////////////////////////////////////////////////////////////////////////
  663. //
  664. // Gamestats reporting
  665. //
  666. static int GameStats_GetActionIndex( char const *szReportAction )
  667. {
  668. char const * arrActions[] =
  669. {
  670. "", "newgame", "commentary",
  671. "load", "loadlast",
  672. "continuenew", "continueload",
  673. "saveover", "savenew", "savedel",
  674. "changelevel", "changelevelcomm"
  675. };
  676. for ( int k = 0; k < ARRAYSIZE( arrActions ); ++ k )
  677. {
  678. if ( !V_stricmp( arrActions[k], szReportAction ) )
  679. return k + 1;
  680. }
  681. return 0;
  682. }
  683. static int GameStats_GetReportMapNameIndex( char const *szMapName )
  684. {
  685. int nCounter = 1000; // resolve it as single player map
  686. #define CFG( spmap, ... ) ++ nCounter; if ( !V_stricmp( szMapName, #spmap ) ) return nCounter;
  687. #include "xlast_portal2/inc_sp_maps.inc"
  688. #undef CFG
  689. nCounter = 0; // resolve it as coop map
  690. #define CFG( mpcoopmap, ... ) ++ nCounter; if ( !V_stricmp( szMapName, #mpcoopmap ) ) return nCounter;
  691. #include "xlast_portal2/inc_coop_maps.inc"
  692. #undef CFG
  693. return 0;
  694. }
  695. void CUIGameData::GameStats_ReportAction( char const *szReportAction, char const *szMapName, uint64 uiFlags )
  696. {
  697. KeyValues *kv = new KeyValues( "gamestat_action" );
  698. KeyValues::AutoDelete autodelete_kv( kv );
  699. kv->SetInt( "version", 1 );
  700. kv->SetUint64( "xuid", g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( XBX_GetPrimaryUserId() )->GetXUID() );
  701. kv->SetUint64( "*mac", 0ull ); // this will be filled out with console MAC-address
  702. kv->SetInt( "game_action", GameStats_GetActionIndex( szReportAction ) );
  703. kv->SetInt( "game_mapid", GameStats_GetReportMapNameIndex( szMapName ) );
  704. kv->SetUint64( "game_flags", uiFlags );
  705. IDatacenterCmdBatch *pBatch = g_pMatchFramework->GetMatchSystem()->GetDatacenter()->CreateCmdBatch();
  706. pBatch->SetDestroyWhenFinished( true );
  707. pBatch->SetRetryCmdTimeout( 30.0f );
  708. pBatch->AddCommand( kv );
  709. }
  710. #endif
  711. #ifdef _PS3
  712. //////////////////////////////////////////////////////////////////////////
  713. //
  714. // Steam info provider implementation
  715. //
  716. //////////////////////////////////////////////////////////////////////////
  717. class CPS3SaveSteamInfoProvider : public IPS3SaveSteamInfoProviderUiGameData
  718. {
  719. public:
  720. virtual CUtlBuffer * GetInitialLoadBuffer()
  721. {
  722. GetBufferForSaveUtil().EnsureCapacity( 512*1024 - 128 );
  723. return &GetBufferForSaveUtil();
  724. }
  725. virtual CUtlBuffer * GetSaveBufferForCommit()
  726. {
  727. // called inside the saveutil callback (cannot cause allocations)
  728. return &GetBufferForSaveUtil();
  729. }
  730. virtual CUtlBuffer * PrepareSaveBufferForCommit()
  731. {
  732. // called on the main thread
  733. if ( !FillSteamBuffer() )
  734. {
  735. m_uiState &=~ ( STATE_DIRTY | STATE_INITIATED | STATE_COMMITTING );
  736. return NULL;
  737. }
  738. m_uiState |= STATE_COMMITTING; // flipping the committing flag early
  739. ++ m_idxSaveUtilOwned; // now saveutil owns the buffer we were writing into
  740. m_uiState &=~ STATE_DIRTY; // mark the steam data buffer as not yet dirty
  741. return &GetBufferForSaveUtil();
  742. }
  743. virtual void RunFrame();
  744. virtual void WriteSteamStats();
  745. protected:
  746. CUtlBuffer m_arrBuffers[2];
  747. int m_idxSaveUtilOwned;
  748. inline CUtlBuffer& GetBufferForSaveUtil() { return m_arrBuffers[ m_idxSaveUtilOwned%2 ]; }
  749. inline CUtlBuffer& GetBufferForSteamData() { return m_arrBuffers[ !(m_idxSaveUtilOwned%2) ]; }
  750. bool FillSteamBuffer();
  751. CPS3SaveRestoreAsyncStatus m_ps3AsyncSaveStatus;
  752. enum AsyncSaveState_t
  753. {
  754. STATE_DEFAULT = 0x00, // default state, no save data ready, no save pending
  755. STATE_DIRTY = 0x01, // save data ready, will trigger saveutil when possible
  756. STATE_INITIATED = 0x10, // saveutil triggered, can still slipstream updated data
  757. STATE_COMMITTING = 0x20, // saveutil committing data, new updates must wait
  758. };
  759. uint32 m_uiState;
  760. uint32 m_numDirtyFrames;
  761. }
  762. g_ps3saveSteamInfoProvider;
  763. IPS3SaveSteamInfoProviderUiGameData * GetPs3SaveSteamInfoProvider()
  764. {
  765. return &g_ps3saveSteamInfoProvider;
  766. }
  767. bool CPS3SaveSteamInfoProvider::FillSteamBuffer()
  768. {
  769. // Write the data into save buffer
  770. CUtlBuffer &buf = GetBufferForSteamData();
  771. #ifndef NO_STEAM
  772. uint32 uiSizeRequired = buf.Size();
  773. bool bResult = steamapicontext->SteamUserStats()->GetUserStatsData( buf.Base(), buf.Size(), &uiSizeRequired );
  774. if ( !bResult && uiSizeRequired > buf.Size() )
  775. {
  776. buf.EnsureCapacity( uiSizeRequired );
  777. bResult = steamapicontext->SteamUserStats()->GetUserStatsData( buf.Base(), buf.Size(), &uiSizeRequired );
  778. }
  779. if ( !bResult )
  780. {
  781. buf.Purge();
  782. return false;
  783. }
  784. else
  785. {
  786. buf.SeekPut( CUtlBuffer::SEEK_HEAD, uiSizeRequired );
  787. return true;
  788. }
  789. #else
  790. buf.Purge();
  791. return true;
  792. #endif
  793. }
  794. void CPS3SaveSteamInfoProvider::RunFrame()
  795. {
  796. // if save has been initiated, see if it has completed already
  797. if ( m_uiState & STATE_INITIATED )
  798. {
  799. if ( m_ps3AsyncSaveStatus.JobDone() )
  800. {
  801. m_uiState &=~( STATE_INITIATED | STATE_COMMITTING );
  802. Msg( "%.3f CPS3SaveSteamInfoProvider::WriteSteamStats completed!\n", Plat_FloatTime() );
  803. GetBufferForSaveUtil().Purge();
  804. }
  805. }
  806. // if we have some new dirty data, then see if we can kick off a save
  807. if ( ( m_uiState & STATE_DIRTY ) && !( m_uiState & STATE_INITIATED ) )
  808. {
  809. if ( m_numDirtyFrames )
  810. -- m_numDirtyFrames;
  811. if ( ps3saveuiapi && !ps3saveuiapi->IsSaveUtilBusy() &&
  812. !m_numDirtyFrames
  813. /*
  814. // Not needed for CSGO
  815. &&
  816. ( !CBaseModPanel::GetSingleton().GetWindow( WT_ATTRACTSCREEN ) ||
  817. ( ( CAttractScreen * ) CBaseModPanel::GetSingleton().GetWindow( WT_ATTRACTSCREEN ) )->IsGameBootReady() )
  818. */
  819. )
  820. {
  821. m_ps3AsyncSaveStatus.m_nCurrentOperationTag = kSAVE_TAG_WRITE_STEAMINFO;
  822. ps3saveuiapi->WriteSteamInfo( &m_ps3AsyncSaveStatus );
  823. Msg( "%.3f CPS3SaveSteamInfoProvider::WriteSteamStats kicked off saveutil work\n", Plat_FloatTime() );
  824. m_uiState |= STATE_INITIATED; // we kicked off a save operation successfully
  825. }
  826. }
  827. }
  828. void CPS3SaveSteamInfoProvider::WriteSteamStats()
  829. {
  830. Msg( "%.3f CPS3SaveSteamInfoProvider::WriteSteamStats prepared data\n", Plat_FloatTime() );
  831. m_uiState |= STATE_DIRTY;
  832. m_numDirtyFrames = 3;
  833. }
  834. #endif