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.

1076 lines
34 KiB

  1. //========= Copyright � 1996-2008, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=====================================================================================//
  6. #include "cbase.h"
  7. #include "basemodpanel.h"
  8. #include "UIGameData.h"
  9. #include <ctype.h>
  10. #include "./GameUI/IGameUI.h"
  11. #include "ienginevgui.h"
  12. #include "icommandline.h"
  13. #include "vgui/ISurface.h"
  14. #include "EngineInterface.h"
  15. #include "tier0/dbg.h"
  16. #include "ixboxsystem.h"
  17. #include "GameUI_Interface.h"
  18. #include "game/client/IGameClientExports.h"
  19. #include "fmtstr.h"
  20. #include "vstdlib/random.h"
  21. #include "utlbuffer.h"
  22. #include "filesystem/IXboxInstaller.h"
  23. #include "tier1/tokenset.h"
  24. #include "FileSystem.h"
  25. #include "filesystem/IXboxInstaller.h"
  26. #include <time.h>
  27. // vgui controls
  28. #include "vgui/ILocalize.h"
  29. #include "netmessages.h"
  30. #ifndef _GAMECONSOLE
  31. #include "steam/steam_api.h"
  32. #endif
  33. // memdbgon must be the last include file in a .cpp file!!!
  34. #include "tier0/memdbgon.h"
  35. using namespace BaseModUI;
  36. using namespace vgui;
  37. //setup in GameUI_Interface.cpp
  38. extern const char *COM_GetModDirectory( void );
  39. ConVar demo_ui_enable( "demo_ui_enable", "", FCVAR_DEVELOPMENTONLY, "Suffix for the demo UI" );
  40. ConVar demo_connect_string( "demo_connect_string", "", FCVAR_DEVELOPMENTONLY, "Connect string for demo UI" );
  41. ///Asyncronous Operations
  42. ConVar mm_ping_max_green( "ping_max_green", "70" );
  43. ConVar mm_ping_max_yellow( "ping_max_yellow", "140" );
  44. ConVar mm_ping_max_red( "ping_max_red", "250" );
  45. //=============================================================================
  46. //=============================================================================
  47. // Xbox 360 Marketplace entry point
  48. //=============================================================================
  49. struct X360MarketPlaceEntryPoint
  50. {
  51. DWORD dwEntryPoint;
  52. uint64 uiOfferID;
  53. };
  54. static X360MarketPlaceEntryPoint g_MarketplaceEntryPoint;
  55. #ifdef _GAMECONSOLE
  56. struct X360MarketPlaceQuery
  57. {
  58. uint64 uiOfferID;
  59. HRESULT hResult;
  60. XOVERLAPPED xOverlapped;
  61. };
  62. static CUtlVector< X360MarketPlaceQuery * > g_arrMarketPlaceQueries;
  63. #endif
  64. static void GoToMarketplaceForOffer()
  65. {
  66. #ifdef _GAMECONSOLE
  67. // Stop installing to the hard drive, otherwise STFC fragmentation hazard, as multiple non sequential HDD writes will occur.
  68. // This needs to be done before the DLC might be downloaded to the HDD, otherwise it could be fragmented.
  69. // We restart the installer on DLC download completion. We do not handle the cancel/abort case. The installer
  70. // will restart through the pre-dlc path, i.e. after attract or exiting a map back to the main menu.
  71. if ( g_pXboxInstaller )
  72. g_pXboxInstaller->Stop();
  73. // See if we need to free some of the queries
  74. for ( int k = 0; k < g_arrMarketPlaceQueries.Count(); ++ k )
  75. {
  76. X360MarketPlaceQuery *pQuery = g_arrMarketPlaceQueries[k];
  77. if ( XHasOverlappedIoCompleted( &pQuery->xOverlapped ) )
  78. {
  79. delete pQuery;
  80. g_arrMarketPlaceQueries.FastRemove( k -- );
  81. }
  82. }
  83. // Allocate a new query
  84. X360MarketPlaceQuery *pQuery = new X360MarketPlaceQuery;
  85. memset( pQuery, 0, sizeof( *pQuery ) );
  86. pQuery->uiOfferID = g_MarketplaceEntryPoint.uiOfferID;
  87. g_arrMarketPlaceQueries.AddToTail( pQuery );
  88. // Open the marketplace entry point
  89. int iSlot = CBaseModPanel::GetSingleton().GetLastActiveUserId();
  90. int iCtrlr = XBX_GetUserIsGuest( iSlot ) ? XBX_GetPrimaryUserId() : XBX_GetUserId( iSlot );
  91. xonline->XShowMarketplaceDownloadItemsUI( iCtrlr,
  92. g_MarketplaceEntryPoint.dwEntryPoint, &pQuery->uiOfferID, 1,
  93. &pQuery->hResult, &pQuery->xOverlapped );
  94. #endif
  95. }
  96. static void ShowMarketplaceUiForOffer()
  97. {
  98. #ifdef _GAMECONSOLE
  99. // Stop installing to the hard drive, otherwise STFC fragmentation hazard, as multiple non sequential HDD writes will occur.
  100. // This needs to be done before the DLC might be downloaded to the HDD, otherwise it could be fragmented.
  101. // We restart the installer on DLC download completion. We do not handle the cancel/abort case. The installer
  102. // will restart through the pre-dlc path, i.e. after attract or exiting a map back to the main menu.
  103. if ( g_pXboxInstaller )
  104. g_pXboxInstaller->Stop();
  105. // Open the marketplace entry point
  106. int iSlot = CBaseModPanel::GetSingleton().GetLastActiveUserId();
  107. int iCtrlr = XBX_GetUserIsGuest( iSlot ) ? XBX_GetPrimaryUserId() : XBX_GetUserId( iSlot );
  108. DWORD ret = xonline->XShowMarketplaceUI( iCtrlr, g_MarketplaceEntryPoint.dwEntryPoint, g_MarketplaceEntryPoint.uiOfferID, DWORD( -1 ) );
  109. DevMsg( "XShowMarketplaceUI for offer %llx entry point %d ctrlr%d returned %d\n",
  110. g_MarketplaceEntryPoint.uiOfferID, g_MarketplaceEntryPoint.dwEntryPoint, iCtrlr, ret );
  111. #endif
  112. }
  113. #ifdef _GAMECONSOLE
  114. CON_COMMAND( x360_marketplace_offer, "Get a known offer from x360 marketplace" )
  115. {
  116. if ( args.ArgC() != 4 )
  117. {
  118. Warning( "Usage: x360_marketplace_offer type 0xOFFERID ui|dl\n" );
  119. return;
  120. }
  121. int iEntryPoint = Q_atoi( args.Arg( 1 ) );
  122. char const *szArg2 = args.Arg( 2 );
  123. uint64 uiOfferId = 0ull;
  124. if ( 1 != sscanf( szArg2, "0x%llx", &uiOfferId ) )
  125. uiOfferId = 0ull;
  126. // Go to marketplace
  127. g_MarketplaceEntryPoint.dwEntryPoint = iEntryPoint;
  128. g_MarketplaceEntryPoint.uiOfferID = uiOfferId;
  129. if ( !Q_stricmp( args.Arg( 3 ), "ui" ) )
  130. ShowMarketplaceUiForOffer();
  131. else
  132. GoToMarketplaceForOffer();
  133. }
  134. #endif
  135. //=============================================================================
  136. //
  137. //=============================================================================
  138. CUIGameData* CUIGameData::m_Instance = 0;
  139. bool CUIGameData::m_bModuleShutDown = false;
  140. //=============================================================================
  141. CUIGameData::CUIGameData() :
  142. #if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  143. m_CallbackPersonaStateChanged( this, &CUIGameData::Steam_OnPersonaStateChanged ),
  144. #endif
  145. m_CGameUIPostInit( false )
  146. {
  147. m_flShowConnectionProblemTimer = 0.0f;
  148. m_flTimeLastFrame = Plat_FloatTime();
  149. m_bShowConnectionProblemActive = false;
  150. g_pMatchFramework->GetEventsSubscription()->Subscribe( this );
  151. m_bXUIOpen = false;
  152. m_bWaitingForStorageDeviceHandle = false;
  153. m_iStorageID = XBX_INVALID_STORAGE_ID;
  154. m_pAsyncJob = NULL;
  155. m_pSelectStorageClient = NULL;
  156. SetDefLessFunc( m_mapUserXuidToAvatar );
  157. SetDefLessFunc( m_mapUserXuidToName );
  158. }
  159. //=============================================================================
  160. CUIGameData::~CUIGameData()
  161. {
  162. // Unsubscribe from events system
  163. g_pMatchFramework->GetEventsSubscription()->Unsubscribe( this );
  164. }
  165. //=============================================================================
  166. CUIGameData* CUIGameData::Get()
  167. {
  168. if ( !m_Instance && !m_bModuleShutDown )
  169. {
  170. m_Instance = new CUIGameData();
  171. }
  172. return m_Instance;
  173. }
  174. void CUIGameData::Shutdown()
  175. {
  176. if ( !m_bModuleShutDown )
  177. {
  178. m_bModuleShutDown = true;
  179. delete m_Instance;
  180. m_Instance = NULL;
  181. }
  182. }
  183. #ifdef _GAMECONSOLE
  184. CON_COMMAND( ui_fake_connection_problem, "" )
  185. {
  186. int numMilliSeconds = 1000;
  187. if ( args.ArgC() > 1 )
  188. {
  189. numMilliSeconds = Q_atoi( args.Arg( 1 ) );
  190. }
  191. float flTime = Plat_FloatTime();
  192. DevMsg( "ui_fake_connection_problem %d @%.2f\n", numMilliSeconds, flTime );
  193. int numTries = 2;
  194. while ( ( 1000 * ( Plat_FloatTime() - flTime ) < numMilliSeconds ) &&
  195. numTries --> 0 )
  196. {
  197. ThreadSleep( numMilliSeconds + 50 );
  198. }
  199. flTime = Plat_FloatTime();
  200. DevMsg( "ui_fake_connection_problem finished @%.2f\n", flTime );
  201. }
  202. #endif
  203. //=============================================================================
  204. void CUIGameData::RunFrame()
  205. {
  206. RunFrame_Storage();
  207. RunFrame_Invite();
  208. if ( m_flShowConnectionProblemTimer > 0.0f )
  209. {
  210. float flCurrentTime = Plat_FloatTime();
  211. float flTimeElapsed = ( flCurrentTime - m_flTimeLastFrame );
  212. m_flTimeLastFrame = flCurrentTime;
  213. if ( flTimeElapsed > 0.0f )
  214. {
  215. m_flShowConnectionProblemTimer -= flTimeElapsed;
  216. }
  217. #if 0 // TODO: UI: Connection problem waitscreen
  218. if ( m_flShowConnectionProblemTimer > 0.0f )
  219. {
  220. if ( !m_bShowConnectionProblemActive &&
  221. !CBaseModPanel::GetSingleton().IsVisible() )
  222. {
  223. GameUI().ActivateGameUI();
  224. OpenWaitScreen( "#GameUI_RetryingConnectionToServer", 0.0f );
  225. m_bShowConnectionProblemActive = true;
  226. }
  227. }
  228. else
  229. {
  230. if ( m_bShowConnectionProblemActive )
  231. {
  232. // Before closing this particular waitscreen we need to establish
  233. // a correct navback, otherwise it will not close - Vitaliy (bugbait #51272)
  234. if ( CBaseModFrame *pWaitScreen = CBaseModPanel::GetSingleton().GetWindow( WT_GENERICWAITSCREEN ) )
  235. {
  236. if ( !pWaitScreen->GetNavBack() )
  237. {
  238. if ( CBaseModFrame *pIngameMenu = CBaseModPanel::GetSingleton().GetWindow( WT_INGAMEMAINMENU ) )
  239. pWaitScreen->SetNavBack( pIngameMenu );
  240. }
  241. if ( !pWaitScreen->GetNavBack() )
  242. {
  243. // This waitscreen will fail to close, force the close!
  244. pWaitScreen->Close();
  245. }
  246. }
  247. CloseWaitScreen( NULL, "Connection Problems" );
  248. GameUI().HideGameUI();
  249. m_bShowConnectionProblemActive = false;
  250. }
  251. }
  252. #endif
  253. }
  254. }
  255. void CUIGameData::OnSetStorageDeviceId( int iController, uint nDeviceId )
  256. {
  257. #if 0 // TODO: UI: OnSetStorageDeviceId
  258. // Check to see if there is enough room on this storage device
  259. if ( nDeviceId == XBX_STORAGE_DECLINED || nDeviceId == XBX_INVALID_STORAGE_ID )
  260. {
  261. CloseWaitScreen( NULL, "ReportNoDeviceSelected" );
  262. m_pSelectStorageClient->OnDeviceFail( ISelectStorageDeviceClient::FAIL_NOT_SELECTED );
  263. m_pSelectStorageClient = NULL;
  264. }
  265. else if ( xboxsystem->DeviceCapacityAdequate( iController, nDeviceId, COM_GetModDirectory() ) == false )
  266. {
  267. CloseWaitScreen( NULL, "ReportDeviceFull" );
  268. m_pSelectStorageClient->OnDeviceFail( ISelectStorageDeviceClient::FAIL_FULL );
  269. m_pSelectStorageClient = NULL;
  270. }
  271. else
  272. {
  273. // Set the storage device
  274. XBX_SetStorageDeviceId( iController, nDeviceId );
  275. OnDeviceAttached();
  276. m_pSelectStorageClient->OnDeviceSelected();
  277. }
  278. #endif
  279. }
  280. //=============================================================================
  281. void CUIGameData::OnGameUIPostInit()
  282. {
  283. m_CGameUIPostInit = true;
  284. }
  285. //=============================================================================
  286. void CUIGameData::OpenFriendRequestPanel(int index, uint64 playerXuid)
  287. {
  288. #ifdef _GAMECONSOLE
  289. XShowFriendRequestUI(index, playerXuid);
  290. #endif
  291. }
  292. //=============================================================================
  293. void CUIGameData::OpenInviteUI( char const *szInviteUiType )
  294. {
  295. #ifdef _GAMECONSOLE
  296. int iSlot = CBaseModPanel::GetSingleton().GetLastActiveUserId();
  297. int iCtrlr = XBX_GetUserIsGuest( iSlot ) ? XBX_GetPrimaryUserId() : XBX_GetUserId( iSlot );
  298. if ( !Q_stricmp( szInviteUiType, "friends" ) )
  299. ::XShowFriendsUI( iCtrlr );
  300. else if ( !Q_stricmp( szInviteUiType, "players" ) )
  301. xonline->XShowGameInviteUI( iCtrlr, NULL, 0, 0 );
  302. else if ( !Q_stricmp( szInviteUiType, "party" ) )
  303. xonline->XShowPartyUI( iCtrlr );
  304. else if ( !Q_stricmp( szInviteUiType, "inviteparty" ) )
  305. xonline->XPartySendGameInvites( iCtrlr, NULL );
  306. else if ( !Q_stricmp( szInviteUiType, "community" ) )
  307. xonline->XShowCommunitySessionsUI( iCtrlr, XSHOWCOMMUNITYSESSION_SHOWPARTY );
  308. else if ( !Q_stricmp( szInviteUiType, "voiceui" ) )
  309. ::XShowVoiceChannelUI( iCtrlr );
  310. else if ( !Q_stricmp( szInviteUiType, "gamevoiceui" ) )
  311. ::XShowGameVoiceChannelUI();
  312. else
  313. {
  314. DevWarning( "OpenInviteUI with wrong parameter `%s`!\n", szInviteUiType );
  315. Assert( 0 );
  316. }
  317. #endif
  318. }
  319. void CUIGameData::ExecuteOverlayCommand( char const *szCommand )
  320. {
  321. #if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  322. if ( steamapicontext && steamapicontext->SteamFriends() &&
  323. steamapicontext->SteamUtils() && steamapicontext->SteamUtils()->IsOverlayEnabled() )
  324. {
  325. steamapicontext->SteamFriends()->ActivateGameOverlay( szCommand );
  326. }
  327. else
  328. {
  329. // TODO: UI: DisplayOkOnlyMsgBox( NULL, "#L4D360UI_SteamOverlay_Title", "#L4D360UI_SteamOverlay_Text" );
  330. // DisplayOkOnlyMsgBox( NULL, "#L4D360UI_SteamOverlay_Title", "#L4D360UI_SteamOverlay_Text" );
  331. }
  332. #else
  333. ExecuteNTimes( 5, DevWarning( "ExecuteOverlayCommand( %s ) is unsupported\n", szCommand ) );
  334. Assert( !"ExecuteOverlayCommand" );
  335. #endif
  336. }
  337. //=============================================================================
  338. bool CUIGameData::SignedInToLive()
  339. {
  340. #ifdef _GAMECONSOLE
  341. if ( XBX_GetNumGameUsers() <= 0 ||
  342. XBX_GetPrimaryUserIsGuest() )
  343. return false;
  344. for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
  345. {
  346. int iController = XBX_GetUserId( k );
  347. IPlayer *player = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iController );
  348. if ( !player )
  349. return false;
  350. if ( player->GetOnlineState() != IPlayer::STATE_ONLINE )
  351. return false;
  352. }
  353. #endif
  354. return true;
  355. }
  356. bool CUIGameData::AnyUserSignedInToLiveWithMultiplayerDisabled()
  357. {
  358. #ifdef _GAMECONSOLE
  359. if ( XBX_GetNumGameUsers() <= 0 ||
  360. XBX_GetPrimaryUserIsGuest() )
  361. return false;
  362. for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
  363. {
  364. int iController = XBX_GetUserId( k );
  365. IPlayer *player = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iController );
  366. if ( player && player->GetOnlineState() == IPlayer::STATE_NO_MULTIPLAYER )
  367. return true;
  368. }
  369. #endif
  370. return false;
  371. }
  372. const char *CUIGameData::GetLocalPlayerName( int iController )
  373. {
  374. static ConVarRef cl_names_debug( "cl_names_debug" );
  375. if ( cl_names_debug.GetInt() )
  376. return "WWWWWWWWWWWWWWW";
  377. IPlayer *player = g_pMatchFramework->GetMatchSystem()->GetPlayerManager()->GetLocalPlayer( iController );
  378. if ( !player )
  379. {
  380. return "";
  381. }
  382. return player->GetName();
  383. }
  384. //////////////////////////////////////////////////////////////////////////
  385. bool CUIGameData::IsXUIOpen()
  386. {
  387. return m_bXUIOpen;
  388. }
  389. void CUIGameData::NeedConnectionProblemWaitScreen ( void )
  390. {
  391. m_flShowConnectionProblemTimer = 1.0f;
  392. }
  393. void CUIGameData::ShowPasswordUI( char const *pchCurrentPW )
  394. {
  395. #if 0 // TODO: UI: ShowPasswordUI
  396. PasswordEntry *pwEntry = static_cast<PasswordEntry*>( CBaseModPanel::GetSingleton().OpenWindow( WT_PASSWORDENTRY, NULL, false ) );
  397. if ( pwEntry )
  398. {
  399. PasswordEntry::Data_t data;
  400. data.pWindowTitle = "#L4D360UI_PasswordEntry_Title";
  401. data.pMessageText = "#L4D360UI_PasswordEntry_Prompt";
  402. data.bOkButtonEnabled = true;
  403. data.bCancelButtonEnabled = true;
  404. data.m_szCurrentPW = pchCurrentPW;
  405. data.pfnOkCallback = PasswordEntered;
  406. data.pfnCancelCallback = PasswordNotEntered;
  407. pwEntry->SetUsageData(data);
  408. }
  409. #else
  410. engine->SetConnectionPassword( "" );
  411. #endif
  412. }
  413. IImage *CUIGameData::GetAvatarImage( XUID playerID )
  414. {
  415. #ifdef _GAMECONSOLE
  416. return NULL;
  417. #else
  418. if ( !playerID )
  419. return NULL;
  420. // do we already have this image cached?
  421. CGameUiAvatarImage *pImage = NULL;
  422. int iIndex = m_mapUserXuidToAvatar.Find( playerID );
  423. if ( iIndex == m_mapUserXuidToAvatar.InvalidIndex() )
  424. {
  425. // cache a new image
  426. pImage = new CGameUiAvatarImage();
  427. // We may fail to set the steam ID - if the player is not our friend and we are not in a lobby or game, eg
  428. if ( !pImage->SetAvatarSteamID( playerID ) )
  429. {
  430. delete pImage;
  431. return NULL;
  432. }
  433. iIndex = m_mapUserXuidToAvatar.Insert( playerID, pImage );
  434. }
  435. else
  436. {
  437. pImage = m_mapUserXuidToAvatar.Element( iIndex );
  438. }
  439. return pImage;
  440. #endif // !_GAMECONSOLE
  441. }
  442. char const * CUIGameData::GetPlayerName( XUID playerID, char const *szPlayerNameSpeculative )
  443. {
  444. static ConVarRef cl_names_debug( "cl_names_debug" );
  445. if ( cl_names_debug.GetInt() )
  446. return "WWWWWWWWWWWWWWW";
  447. #if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  448. if ( steamapicontext && steamapicontext->SteamUtils() &&
  449. steamapicontext->SteamFriends() && steamapicontext->SteamUser() )
  450. {
  451. int iIndex = m_mapUserXuidToName.Find( playerID );
  452. if ( iIndex == m_mapUserXuidToName.InvalidIndex() )
  453. {
  454. char const *szName = steamapicontext->SteamFriends()->GetFriendPersonaName( playerID );
  455. if ( szName && *szName )
  456. {
  457. iIndex = m_mapUserXuidToName.Insert( playerID, szName );
  458. }
  459. }
  460. if ( iIndex != m_mapUserXuidToName.InvalidIndex() )
  461. return m_mapUserXuidToName.Element( iIndex ).Get();
  462. }
  463. #endif
  464. return szPlayerNameSpeculative;
  465. }
  466. #if !defined( _GAMECONSOLE ) && !defined( NO_STEAM )
  467. void CUIGameData::Steam_OnPersonaStateChanged( PersonaStateChange_t *pParam )
  468. {
  469. if ( !pParam->m_ulSteamID )
  470. return;
  471. if ( pParam->m_nChangeFlags & k_EPersonaChangeName )
  472. {
  473. int iIndex = m_mapUserXuidToName.Find( pParam->m_ulSteamID );
  474. if ( iIndex != m_mapUserXuidToName.InvalidIndex() )
  475. {
  476. CUtlString utlName = m_mapUserXuidToName.Element( iIndex );
  477. m_mapUserXuidToName.RemoveAt( iIndex );
  478. GetPlayerName( pParam->m_ulSteamID, utlName.Get() );
  479. }
  480. }
  481. if ( pParam->m_nChangeFlags & k_EPersonaChangeAvatar )
  482. {
  483. CGameUiAvatarImage *pImage = NULL;
  484. int iIndex = m_mapUserXuidToAvatar.Find( pParam->m_ulSteamID );
  485. if ( iIndex != m_mapUserXuidToAvatar.InvalidIndex() )
  486. {
  487. pImage = m_mapUserXuidToAvatar.Element( iIndex );
  488. }
  489. // Re-fetch the image if we have it cached
  490. if ( pImage )
  491. {
  492. pImage->SetAvatarSteamID( pParam->m_ulSteamID );
  493. }
  494. }
  495. }
  496. #endif
  497. CON_COMMAND_F( ui_reloadscheme, "Reloads the resource files for the active UI window", 0 )
  498. {
  499. g_pFullFileSystem->SyncDvdDevCache();
  500. CUIGameData::Get()->ReloadScheme();
  501. }
  502. void CUIGameData::ReloadScheme()
  503. {
  504. CBaseModPanel::GetSingleton().ReloadScheme();
  505. }
  506. bool CUIGameData::IsActiveSplitScreenPlayerSpectating( void )
  507. {
  508. // int iLocalPlayerTeam;
  509. // if ( GameClientExports()->GetPlayerTeamIdByUserId( -1, iLocalPlayerTeam ) )
  510. // {
  511. // if ( iLocalPlayerTeam != GameClientExports()->GetTeamId_Survivor() &&
  512. // iLocalPlayerTeam != GameClientExports()->GetTeamId_Infected() )
  513. // return true;
  514. // }
  515. return false;
  516. }
  517. void CUIGameData::OnEvent( KeyValues *pEvent )
  518. {
  519. char const *szEvent = pEvent->GetName();
  520. if ( !Q_stricmp( "OnSysXUIEvent", szEvent ) )
  521. {
  522. m_bXUIOpen = !Q_stricmp( "opening", pEvent->GetString( "action", "" ) );
  523. }
  524. else if ( !Q_stricmp( "OnProfileUnavailable", szEvent ) )
  525. {
  526. #if defined( _DEMO ) && defined( _GAMECONSOLE )
  527. return;
  528. #endif
  529. // Activate game ui to see the dialog
  530. if ( !CBaseModPanel::GetSingleton().IsVisible() )
  531. {
  532. engine->ExecuteClientCmd( "gameui_activate" );
  533. }
  534. #if 0 // TODO: UI: L4D360UI_MsgBx_AchievementNotWritten
  535. // Pop a message dialog if their storage device was changed
  536. GenericConfirmation* confirmation = static_cast<GenericConfirmation*>( CBaseModPanel::GetSingleton().OpenWindow( WT_GENERICCONFIRMATION,
  537. GetParentWindowForSystemMessageBox(), false ) );
  538. GenericConfirmation::Data_t data;
  539. data.pWindowTitle = "#L4D360UI_MsgBx_AchievementNotWrittenTitle";
  540. data.pMessageText = "#L4D360UI_MsgBx_AchievementNotWritten";
  541. data.bOkButtonEnabled = true;
  542. confirmation->SetUsageData( data );
  543. #endif
  544. }
  545. else if ( !Q_stricmp( "OnInvite", szEvent ) )
  546. {
  547. // Check if the user just accepted invite
  548. if ( !Q_stricmp( "accepted", pEvent->GetString( "action" ) ) )
  549. {
  550. // Check if we have an outstanding session
  551. IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
  552. if ( !pIMatchSession )
  553. {
  554. Invite_Connecting();
  555. return;
  556. }
  557. // User is accepting an invite and has an outstanding
  558. // session, TCR requires confirmation of destructive actions
  559. if ( int *pnConfirmed = ( int * ) pEvent->GetPtr( "confirmed" ) )
  560. {
  561. *pnConfirmed = 0;
  562. }
  563. // Show the dialog
  564. Invite_Confirm();
  565. }
  566. else if ( !Q_stricmp( "storage", pEvent->GetString( "action" ) ) )
  567. {
  568. if ( !Invite_IsStorageDeviceValid() )
  569. {
  570. if ( int *pnConfirmed = ( int * ) pEvent->GetPtr( "confirmed" ) )
  571. {
  572. *pnConfirmed = 0; // make the invite accepting code wait
  573. }
  574. }
  575. }
  576. else if ( !Q_stricmp( "error", pEvent->GetString( "action" ) ) )
  577. {
  578. char const *szReason = pEvent->GetString( "error", "" );
  579. if ( XBX_GetNumGameUsers() < 2 )
  580. {
  581. RemapText_t arrText[] = {
  582. { "", "#InviteError_Unknown", RemapText_t::MATCH_FULL },
  583. { "NotOnline", "#InviteError_NotOnline1", RemapText_t::MATCH_FULL },
  584. { "NoMultiplayer", "#InviteError_NoMultiplayer1", RemapText_t::MATCH_FULL },
  585. { "SameConsole", "#InviteError_SameConsole1", RemapText_t::MATCH_FULL },
  586. { NULL, NULL, RemapText_t::MATCH_FULL }
  587. };
  588. szReason = RemapText_t::RemapRawText( arrText, szReason );
  589. }
  590. else
  591. {
  592. RemapText_t arrText[] = {
  593. { "", "#InviteError_Unknown", RemapText_t::MATCH_FULL },
  594. { "NotOnline", "#InviteError_NotOnline2", RemapText_t::MATCH_FULL },
  595. { "NoMultiplayer", "#InviteError_NoMultiplayer2", RemapText_t::MATCH_FULL },
  596. { "SameConsole", "#InviteError_SameConsole2", RemapText_t::MATCH_FULL },
  597. { NULL, NULL, RemapText_t::MATCH_FULL }
  598. };
  599. szReason = RemapText_t::RemapRawText( arrText, szReason );
  600. }
  601. #if 0 // TODO: UI: L4D360UI_XboxLive required msg box
  602. // Show the message box
  603. GenericConfirmation* confirmation = static_cast<GenericConfirmation*>( CBaseModPanel::GetSingleton().OpenWindow( WT_GENERICCONFIRMATION,
  604. GetParentWindowForSystemMessageBox(), false ) );
  605. GenericConfirmation::Data_t data;
  606. data.pWindowTitle = "#L4D360UI_XboxLive";
  607. data.pMessageText = szReason;
  608. data.bOkButtonEnabled = true;
  609. confirmation->SetUsageData(data);
  610. #endif
  611. }
  612. }
  613. else if ( !Q_stricmp( "OnSysStorageDevicesChanged", szEvent ) )
  614. {
  615. #if defined( _DEMO ) && defined( _GAMECONSOLE )
  616. return;
  617. #endif
  618. // If a storage device change is in progress, the simply ignore
  619. // the notification callback, but pop the dialog
  620. if ( m_pSelectStorageClient )
  621. {
  622. DevWarning( "Ignored OnSysStorageDevicesChanged while the storage selection was in progress...\n" );
  623. }
  624. // Activate game ui to see the dialog
  625. if ( !CBaseModPanel::GetSingleton().IsVisible() )
  626. {
  627. engine->ExecuteClientCmd( "gameui_activate" );
  628. }
  629. #if 0 // TODO: UI: Pop a message dialog if their storage device was changed
  630. // Pop a message dialog if their storage device was changed
  631. GenericConfirmation* confirmation = static_cast<GenericConfirmation*>( CBaseModPanel::GetSingleton().OpenWindow( WT_GENERICCONFIRMATION,
  632. GetParentWindowForSystemMessageBox(), false ) );
  633. GenericConfirmation::Data_t data;
  634. data.pWindowTitle = "#GameUI_Console_StorageRemovedTitle";
  635. data.pMessageText = "#L4D360UI_MsgBx_StorageDeviceRemoved";
  636. data.bOkButtonEnabled = true;
  637. extern void OnStorageDevicesChangedSelectNewDevice();
  638. data.pfnOkCallback = m_pSelectStorageClient ? NULL : &OnStorageDevicesChangedSelectNewDevice; // No callback if already in the middle of selecting a storage device
  639. confirmation->SetUsageData( data );
  640. #endif
  641. }
  642. else if ( !Q_stricmp( "OnSysInputDevicesChanged", szEvent ) )
  643. {
  644. unsigned int nInactivePlayers = 0; // Number of users on the spectating team (ie. idle), or disconnected in this call
  645. int iOldSlot = engine->GetActiveSplitScreenPlayerSlot();
  646. int nDisconnectedDevices = pEvent->GetInt( "mask" );
  647. for ( unsigned int nSlot = 0; nSlot < XBX_GetNumGameUsers(); ++nSlot, nDisconnectedDevices >>= 1 )
  648. {
  649. engine->SetActiveSplitScreenPlayerSlot( nSlot );
  650. // See if this player is spectating (ie. idle)
  651. bool bSpectator = IsActiveSplitScreenPlayerSpectating();
  652. if ( bSpectator )
  653. {
  654. nInactivePlayers++;
  655. }
  656. if ( nDisconnectedDevices & 0x1 )
  657. {
  658. // Only count disconnections if that player wasn't idle
  659. if ( !bSpectator )
  660. {
  661. nInactivePlayers++;
  662. }
  663. engine->ClientCmd( "go_away_from_keyboard" );
  664. }
  665. }
  666. engine->SetActiveSplitScreenPlayerSlot( iOldSlot );
  667. // If all the spectators and all the disconnections account for all possible users, we need to pop a message
  668. // Also, if the GameUI is up, always show the disconnection message
  669. if ( CBaseModPanel::GetSingleton().IsVisible() || nInactivePlayers == XBX_GetNumGameUsers() )
  670. {
  671. if ( !CBaseModPanel::GetSingleton().IsVisible() )
  672. {
  673. engine->ExecuteClientCmd( "gameui_activate" );
  674. }
  675. #if 0 // TODO: UI: Pop a message if a valid controller was removed!
  676. // Pop a message if a valid controller was removed!
  677. GenericConfirmation* confirmation = static_cast<GenericConfirmation*>( CBaseModPanel::GetSingleton().OpenWindow( WT_GENERICCONFIRMATION,
  678. GetParentWindowForSystemMessageBox(), false ) );
  679. GenericConfirmation::Data_t data;
  680. data.pWindowTitle = "#L4D360UI_MsgBx_ControllerUnpluggedTitle";
  681. data.pMessageText = "#L4D360UI_MsgBx_ControllerUnplugged";
  682. data.bOkButtonEnabled = true;
  683. confirmation->SetUsageData(data);
  684. #endif
  685. }
  686. }
  687. else if ( !Q_stricmp( "OnMatchPlayerMgrReset", szEvent ) )
  688. {
  689. char const *szReason = pEvent->GetString( "reason", "" );
  690. bool bShowDisconnectedMsgBox = true;
  691. if ( !Q_stricmp( szReason, "GuestSignedIn" ) )
  692. {
  693. char const *szDestroyedSessionState = pEvent->GetString( "settings/game/state", "lobby" );
  694. if ( !Q_stricmp( "lobby", szDestroyedSessionState ) )
  695. bShowDisconnectedMsgBox = false;
  696. }
  697. engine->HideLoadingPlaque(); // This may not go away unless we force it to hide
  698. #if 0 // TODO: UI: Go to the attract screen
  699. // Go to the attract screen
  700. CBaseModPanel::GetSingleton().CloseAllWindows( CBaseModPanel::CLOSE_POLICY_EVEN_MSGS );
  701. // Show the message box
  702. GenericConfirmation* confirmation = bShowDisconnectedMsgBox ? static_cast<GenericConfirmation*>( CBaseModPanel::GetSingleton().OpenWindow( WT_GENERICCONFIRMATION, NULL, false ) ) : NULL;
  703. CAttractScreen::SetAttractMode( CAttractScreen::ATTRACT_GAMESTART );
  704. CBaseModPanel::GetSingleton().OpenWindow( WT_ATTRACTSCREEN, NULL );
  705. if ( confirmation )
  706. {
  707. GenericConfirmation::Data_t data;
  708. data.pWindowTitle = "#L4D360UI_MsgBx_SignInChangeC";
  709. data.pMessageText = "#L4D360UI_MsgBx_SignInChange";
  710. data.bOkButtonEnabled = true;
  711. if ( !Q_stricmp( szReason, "GuestSignedIn" ) )
  712. {
  713. data.pWindowTitle = "#L4D360UI_MsgBx_DisconnectedFromSession"; // "Disconnect"
  714. data.pMessageText = "#L4D360UI_MsgBx_SignInChange"; // "Sign-in change has occured."
  715. }
  716. confirmation->SetUsageData(data);
  717. #else
  718. {
  719. #endif
  720. #ifdef _GAMECONSOLE
  721. // When a confirmation shows up it prevents attract screen from opening, so reset user slots here:
  722. XBX_ResetUserIdSlots();
  723. XBX_SetPrimaryUserId( XBX_INVALID_USER_ID );
  724. XBX_SetPrimaryUserIsGuest( 0 );
  725. XBX_SetNumGameUsers( 0 ); // users not selected yet
  726. #endif
  727. }
  728. }
  729. else if ( !Q_stricmp( "OnEngineDisconnectReason", szEvent ) )
  730. {
  731. char const *szReason = pEvent->GetString( "reason", "" );
  732. if ( char const *szDisconnectHdlr = pEvent->GetString( "disconnecthdlr", NULL ) )
  733. {
  734. // If a disconnect handler was set during the event, then we don't interfere with
  735. // the dialog explaining disconnection, just let the disconnect handler do everything.
  736. return;
  737. }
  738. RemapText_t arrText[] = {
  739. { "", "#DisconnectReason_Unknown", RemapText_t::MATCH_FULL },
  740. { "Lost connection to LIVE", "#DisconnectReason_LostConnectionToLIVE", RemapText_t::MATCH_FULL },
  741. { "Player removed from host session", "#DisconnectReason_PlayerRemovedFromSession", RemapText_t::MATCH_SUBSTR },
  742. { "Connection to server timed out", "#L4D360UI_MsgBx_DisconnectedFromServer", RemapText_t::MATCH_SUBSTR },
  743. { "Added to banned list", "#SessionError_Kicked", RemapText_t::MATCH_SUBSTR },
  744. { "Kicked and banned", "#SessionError_Kicked", RemapText_t::MATCH_SUBSTR },
  745. { "You have been voted off", "#SessionError_Kicked", RemapText_t::MATCH_SUBSTR },
  746. { "All players idle", "#L4D_ServerShutdownIdle", RemapText_t::MATCH_SUBSTR },
  747. #ifdef _GAMECONSOLE
  748. { "", "#DisconnectReason_Unknown", RemapText_t::MATCH_START }, // Catch all cases for X360
  749. #endif
  750. { NULL, NULL, RemapText_t::MATCH_FULL }
  751. };
  752. szReason = RemapText_t::RemapRawText( arrText, szReason );
  753. //
  754. // Go back to main menu and display the disconnection reason
  755. //
  756. engine->HideLoadingPlaque(); // This may not go away unless we force it to hide
  757. #if 0 // TODO: UI: Go to the main menu
  758. // Go to the main menu
  759. CBaseModPanel::GetSingleton().CloseAllWindows( CBaseModPanel::CLOSE_POLICY_EVEN_MSGS );
  760. // Show the message box
  761. GenericConfirmation* confirmation = static_cast<GenericConfirmation*>( CBaseModPanel::GetSingleton().OpenWindow( WT_GENERICCONFIRMATION, NULL, false ) );
  762. CBaseModPanel::GetSingleton().OpenWindow( WT_MAINMENU, NULL );
  763. GenericConfirmation::Data_t data;
  764. data.pWindowTitle = "#L4D360UI_MsgBx_DisconnectedFromSession"; // "Disconnect"
  765. data.pMessageText = szReason;
  766. data.bOkButtonEnabled = true;
  767. confirmation->SetUsageData(data);
  768. #endif
  769. }
  770. else if ( !Q_stricmp( "OnEngineEndGame", szEvent ) )
  771. {
  772. // If we are connected and there was no session object to handle the event
  773. if ( !g_pMatchFramework->GetMatchSession() )
  774. {
  775. // Issue the disconnect command
  776. engine->ExecuteClientCmd( "disconnect" );
  777. }
  778. }
  779. else if ( !Q_stricmp( "OnMatchSessionUpdate", szEvent ) )
  780. {
  781. if ( !Q_stricmp( "error", pEvent->GetString( "state", "" ) ) )
  782. {
  783. g_pMatchFramework->CloseSession();
  784. char chErrorMsgBuffer[128] = {0};
  785. char chErrorTitleBuffer[128] = {0};
  786. REFERENCE(chErrorTitleBuffer);
  787. char const *szError = pEvent->GetString( "error", "" );
  788. char const *szErrorTitle = "#L4D360UI_MsgBx_DisconnectedFromSession";
  789. RemapText_t arrText[] = {
  790. { "", "#SessionError_Unknown", RemapText_t::MATCH_FULL },
  791. { "n/a", "#SessionError_NotAvailable", RemapText_t::MATCH_FULL },
  792. { "create", "#SessionError_Create", RemapText_t::MATCH_FULL },
  793. { "createclient", "#SessionError_NotAvailable", RemapText_t::MATCH_FULL },
  794. { "connect", "#SessionError_Connect", RemapText_t::MATCH_FULL },
  795. { "full", "#SessionError_Full", RemapText_t::MATCH_FULL },
  796. { "lock", "#SessionError_Lock", RemapText_t::MATCH_FULL },
  797. { "kicked", "#SessionError_Kicked", RemapText_t::MATCH_FULL },
  798. { "migrate", "#SessionError_Migrate", RemapText_t::MATCH_FULL },
  799. { "nomap", "#SessionError_NoMap", RemapText_t::MATCH_FULL },
  800. { "SteamServersDisconnected", "#SessionError_SteamServersDisconnected", RemapText_t::MATCH_FULL },
  801. { NULL, NULL, RemapText_t::MATCH_FULL }
  802. };
  803. szError = RemapText_t::RemapRawText( arrText, szError );
  804. if ( !Q_stricmp( "turequired", szError ) )
  805. {
  806. // Special case for TU required message
  807. // If we have a localization string for the TU message then this means that the other box
  808. // is running and older version of the TU
  809. char const *szTuRequiredCode = pEvent->GetString( "turequired" );
  810. CFmtStr strLocKey( "#SessionError_TU_Required_%s", szTuRequiredCode );
  811. if ( g_pVGuiLocalize->Find( strLocKey ) )
  812. {
  813. Q_strncpy( chErrorMsgBuffer, strLocKey, sizeof( chErrorMsgBuffer ) );
  814. szError = chErrorMsgBuffer;
  815. }
  816. else
  817. {
  818. szError = "#SessionError_TU_RequiredMessage";
  819. }
  820. szErrorTitle = "#SessionError_TU_RequiredTitle";
  821. }
  822. #if 0 // TODO: UI: Go to the main menu
  823. // Go to the main menu
  824. CBaseModPanel::GetSingleton().CloseAllWindows( CBaseModPanel::CLOSE_POLICY_EVEN_MSGS );
  825. // Show the message box
  826. GenericConfirmation* confirmation = static_cast<GenericConfirmation*>( CBaseModPanel::GetSingleton().OpenWindow( WT_GENERICCONFIRMATION, NULL, false ) );
  827. CBaseModPanel::GetSingleton().OpenWindow( WT_MAINMENU, NULL );
  828. GenericConfirmation::Data_t data;
  829. data.pWindowTitle = szErrorTitle;
  830. data.pMessageText = szError;
  831. data.bOkButtonEnabled = true;
  832. if ( !Q_stricmp( "dlcrequired", szError ) )
  833. {
  834. // Special case for DLC required message
  835. uint64 uiDlcRequiredMask = pEvent->GetUint64( "dlcrequired" );
  836. int iDlcRequired = 0;
  837. // Find the first DLC in the reported missing mask that is required
  838. for ( int k = 1; k < sizeof( uiDlcRequiredMask ); ++ k )
  839. {
  840. if ( uiDlcRequiredMask & ( 1ull << k ) )
  841. {
  842. iDlcRequired = k;
  843. break;
  844. }
  845. }
  846. CFmtStr strLocKey( "#SessionError_DLC_RequiredTitle_%d", iDlcRequired );
  847. if ( !g_pVGuiLocalize->Find( strLocKey ) )
  848. iDlcRequired = 0;
  849. // Try to figure out if this DLC is paid/free/unknown
  850. KeyValues *kvDlcDetails = new KeyValues( "" );
  851. KeyValues::AutoDelete autodelete_kvDlcDetails( kvDlcDetails );
  852. if ( !kvDlcDetails->LoadFromFile( g_pFullFileSystem, "resource/UI/BaseModUI/dlcdetailsinfo.res", "MOD" ) )
  853. kvDlcDetails = NULL;
  854. // Determine the DLC offer ID
  855. uint64 uiDlcOfferID = 0ull;
  856. if ( 1 != sscanf( kvDlcDetails->GetString( CFmtStr( "dlc%d/offerid", iDlcRequired ) ), "0x%llx", &uiDlcOfferID ) )
  857. uiDlcOfferID = 0ull;
  858. // Format the strings
  859. bool bKicked = !Q_stricmp( pEvent->GetString( "action" ), "kicked" );
  860. wchar_t const *wszLine1 = g_pVGuiLocalize->Find( CFmtStr( "#SessionError_DLC_Required%s_%d", bKicked ? "Kicked" : "Join", iDlcRequired ) );
  861. wchar_t const *wszLine2 = g_pVGuiLocalize->Find( CFmtStr( "#SessionError_DLC_Required%s_%d", uiDlcOfferID ? "Offer" : "Message", iDlcRequired ) );
  862. int numBytesTwoLines = ( Q_wcslen( wszLine1 ) + Q_wcslen( wszLine2 ) + 4 ) * sizeof( wchar_t );
  863. wchar_t *pwszTwoLines = ( wchar_t * ) stackalloc( numBytesTwoLines );
  864. Q_snwprintf( pwszTwoLines, numBytesTwoLines, L"%s%s", wszLine1, wszLine2 );
  865. data.pMessageTextW = pwszTwoLines;
  866. data.pMessageText = NULL;
  867. Q_snprintf( chErrorTitleBuffer, sizeof( chErrorTitleBuffer ), "#SessionError_DLC_RequiredTitle_%d", iDlcRequired );
  868. data.pWindowTitle = chErrorTitleBuffer;
  869. if ( uiDlcOfferID )
  870. {
  871. data.bCancelButtonEnabled = true;
  872. data.pfnOkCallback = GoToMarketplaceForOffer;
  873. g_MarketplaceEntryPoint.uiOfferID = uiDlcOfferID;
  874. g_MarketplaceEntryPoint.dwEntryPoint = kvDlcDetails->GetInt( CFmtStr( "dlc%d/type", iDlcRequired ) );
  875. }
  876. }
  877. confirmation->SetUsageData(data);
  878. #endif
  879. }
  880. }
  881. }
  882. //////////////////////////////////////////////////////////////////////////
  883. //
  884. //
  885. // A bunch of helper KeyValues hierarchy readers
  886. //
  887. //
  888. //////////////////////////////////////////////////////////////////////////
  889. uint64 GetDlcInstalledMask()
  890. {
  891. static ConVarRef mm_dlcs_mask_fake( "mm_dlcs_mask_fake" );
  892. char const *szFakeDlcsString = mm_dlcs_mask_fake.GetString();
  893. if ( *szFakeDlcsString )
  894. return atoi( szFakeDlcsString );
  895. static ConVarRef mm_dlcs_mask_extras( "mm_dlcs_mask_extras" );
  896. uint64 uiDLCmask = ( unsigned ) mm_dlcs_mask_extras.GetInt();
  897. bool bSearchPath = false;
  898. int numDLCs = g_pFullFileSystem->IsAnyDLCPresent( &bSearchPath );
  899. for ( int j = 0; j < numDLCs; ++ j )
  900. {
  901. unsigned int uiDlcHeader = 0;
  902. if ( !g_pFullFileSystem->GetAnyDLCInfo( j, &uiDlcHeader, NULL, 0 ) )
  903. continue;
  904. int idDLC = DLC_LICENSE_ID( uiDlcHeader );
  905. if ( idDLC < 1 || idDLC >= 31 )
  906. continue; // unsupported DLC id
  907. uiDLCmask |= ( 1ull << idDLC );
  908. }
  909. return uiDLCmask;
  910. }