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.

1245 lines
37 KiB

  1. //========= Copyright � 1996-2009, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=====================================================================================//
  6. #ifndef _X360
  7. #include "xbox/xboxstubs.h"
  8. #endif
  9. #include "mm_framework.h"
  10. #include "fmtstr.h"
  11. // NOTE: This has to be the last file included!
  12. #include "tier0/memdbgon.h"
  13. #ifndef SWDS
  14. #pragma warning (disable : 4355 )
  15. static ConVar mm_player_search_requests_limit( "mm_player_search_requests_limit", "-1", FCVAR_DEVELOPMENTONLY, "How many friend requests are displayed." );
  16. static ConVar mm_player_search_update_interval( "mm_player_search_update_interval", "10", FCVAR_DEVELOPMENTONLY, "Interval between players searches." );
  17. static ConVar mm_player_search_lan_ping_interval( "mm_player_search_lan_ping_interval", "0.2", FCVAR_DEVELOPMENTONLY, "Interval between LAN discovery pings." );
  18. static ConVar mm_player_search_lan_ping_duration( "mm_player_search_lan_ping_duration", "0.6", FCVAR_DEVELOPMENTONLY, "Duration of LAN discovery ping phase." );
  19. PlayerManager::PlayerManager() :
  20. #if defined( _PS3 ) && !defined( NO_STEAM )
  21. m_CallbackOnPS3PSNStatusChange( this, &PlayerManager::Steam_OnPS3PSNStatusChange ),
  22. #endif
  23. m_bUpdateEnabled( true ),
  24. m_flNextUpdateTime( 0.0f ),
  25. m_searchesPending( 0 ),
  26. m_bRequestStoreStats( false )
  27. {
  28. memset( mLocalPlayer, 0, sizeof( mLocalPlayer ) );
  29. memset( m_searchData, 0, sizeof( m_searchData ) );
  30. }
  31. PlayerManager::~PlayerManager()
  32. {
  33. #ifdef _X360
  34. for ( int i = 0; i < ARRAYSIZE( m_searchData ); ++i )
  35. {
  36. delete m_searchData[i].mFriendBuffer;
  37. }
  38. #endif
  39. memset( mLocalPlayer, 0, sizeof( mLocalPlayer ) );
  40. memset( m_searchData, 0, sizeof( m_searchData ) );
  41. m_bUpdateEnabled = false;
  42. m_searchesPending = 0;
  43. // We are leaking player objects here, but it's during destruction of a global (app shutdown).
  44. // We don't want to Destroy() because doing so may call into Xbox libs that have already shutdown.
  45. mFriendsList.Purge();
  46. }
  47. static PlayerManager g_PlayerManager;
  48. PlayerManager *g_pPlayerManager = &g_PlayerManager;
  49. IPlayerLocal * PlayerManager::GetLocalPlayer(int playerIndex)
  50. {
  51. if( ( playerIndex >= 0 ) && ( playerIndex < ARRAYSIZE(mLocalPlayer) ) && mLocalPlayer[ playerIndex ] )
  52. {
  53. return mLocalPlayer[ playerIndex ];
  54. }
  55. return NULL;
  56. }
  57. int PlayerManager::GetNumFriends()
  58. {
  59. return mFriendsList.Count();
  60. }
  61. IPlayerFriend * PlayerManager::GetFriendByIndex( int index )
  62. {
  63. return mFriendsList.IsValidIndex( index ) ? mFriendsList[ index ] : NULL;
  64. }
  65. IPlayerFriend * PlayerManager::GetFriendByXUID( XUID xuid )
  66. {
  67. return FindPlayerFriend( xuid );
  68. }
  69. IPlayer * PlayerManager::FindPlayer( XUID xuid )
  70. {
  71. if ( IPlayer *player = FindPlayerLocal( xuid ) )
  72. return player;
  73. if ( IPlayer *player = FindPlayerFriend( xuid ) )
  74. return player;
  75. return NULL;
  76. }
  77. PlayerFriend * PlayerManager::FindPlayerFriend( XUID xuid )
  78. {
  79. for ( int iIndex = 0; iIndex < mFriendsList.Count(); ++ iIndex )
  80. {
  81. PlayerFriend *player = mFriendsList[iIndex];
  82. if ( player && player->GetXUID() == xuid )
  83. return player;
  84. }
  85. return NULL;
  86. }
  87. PlayerLocal * PlayerManager::FindPlayerLocal( XUID xuid )
  88. {
  89. for ( int iIndex = 0; iIndex < ARRAYSIZE( mLocalPlayer ); ++ iIndex )
  90. {
  91. PlayerLocal *player = mLocalPlayer[iIndex];
  92. if ( player && player->GetXUID() == xuid )
  93. return player;
  94. }
  95. return NULL;
  96. }
  97. void PlayerManager::MarkOldFriends()
  98. {
  99. for ( int iIndex = 0; iIndex < mFriendsList.Count(); iIndex++ )
  100. {
  101. PlayerFriend &player = * mFriendsList[iIndex];
  102. player.SetIsStale( true );
  103. player.SetFriendMark( 0 );
  104. }
  105. }
  106. void PlayerManager::RemoveOldFriends()
  107. {
  108. #if !defined( NO_STEAM )
  109. static bool bPerfectWorld = !!CommandLine()->FindParm( "-perfectworld" );
  110. CUtlMap< int, PlayerFriend*, int, CDefLess< int > > mapFriendRequests;
  111. #endif
  112. for ( int iIndex = 0; iIndex < mFriendsList.Count(); iIndex++ )
  113. {
  114. PlayerFriend &player = * mFriendsList[iIndex];
  115. if ( player.GetIsStale() || !player.GetFriendMark() )
  116. {
  117. mFriendsList.FastRemove( iIndex -- );
  118. player.Destroy();
  119. }
  120. else if ( !bPerfectWorld && ( player.GetTitleID() == uint64( -3 ) || player.GetTitleID() == uint64( -2 ) ) )
  121. {
  122. int nLevel = steamapicontext->SteamFriends()->GetFriendSteamLevel( player.GetXUID() );
  123. mapFriendRequests.Insert( nLevel, &player );
  124. #if !defined( NO_STEAM )
  125. if ( !nLevel ) // force the information to be downloaded
  126. steamapicontext->SteamFriends()->RequestUserInformation( player.GetXUID(), false );
  127. #endif
  128. }
  129. }
  130. #if !defined( NO_STEAM )
  131. int nLimit = mm_player_search_requests_limit.GetInt();
  132. if ( !bPerfectWorld && ( nLimit >= 0 ) )
  133. {
  134. while ( mapFriendRequests.Count() > nLimit )
  135. {
  136. int iMap = mapFriendRequests.FirstInorder();
  137. PlayerFriend *pCullFriendRequest = mapFriendRequests.Element( iMap );
  138. mapFriendRequests.RemoveAt( iMap );
  139. if ( mFriendsList.FindAndFastRemove( pCullFriendRequest ) )
  140. pCullFriendRequest->Destroy();
  141. }
  142. }
  143. #endif
  144. }
  145. void PlayerManager::OnLocalPlayerDisconnectedFromLive( int iCtrlr )
  146. {
  147. for ( int iIndex = 0; iIndex < mFriendsList.Count(); iIndex++ )
  148. {
  149. PlayerFriend &player = * mFriendsList[iIndex];
  150. uint uiMask = player.GetFriendMark();
  151. uiMask &=~ (1 << iCtrlr );
  152. if ( !uiMask )
  153. {
  154. mFriendsList.FastRemove( iIndex -- );
  155. player.Destroy();
  156. }
  157. else
  158. {
  159. player.SetFriendMark( uiMask );
  160. }
  161. }
  162. }
  163. void PlayerManager::Update()
  164. {
  165. if ( m_searchesPending )
  166. {
  167. for ( int i = 0; i < XUSER_MAX_COUNT; ++i )
  168. {
  169. SFriendSearchData &data = m_searchData[ i ];
  170. if( data.mSearchInProgress )
  171. {
  172. #ifdef _X360
  173. if( XHasOverlappedIoCompleted( & data.mFriendsOverlapped ) )
  174. {
  175. // Local users
  176. CUtlVectorFixed< XUID, XUSER_MAX_COUNT > arrLocalXuids;
  177. for ( int k = 0; k < XUSER_MAX_COUNT; ++ k )
  178. {
  179. XUSER_SIGNIN_INFO xsi;
  180. if ( ERROR_SUCCESS != XUserGetSigninInfo( k, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) ||
  181. !xsi.xuid )
  182. {
  183. if ( ERROR_SUCCESS != XUserGetXUID( k, &xsi.xuid ) )
  184. xsi.xuid = NULL;
  185. }
  186. if ( xsi.xuid )
  187. arrLocalXuids.AddToTail( xsi.xuid );
  188. }
  189. // Check if the user is the same
  190. int iCtrlr = i;
  191. XUID xuidNow = 0ull;
  192. XUserGetXUID( iCtrlr, &xuidNow );
  193. if ( !IsEqualXUID( xuidNow, data.mXuid ) )
  194. xuidNow = 0ull;
  195. if ( XBX_GetSlotByUserId( iCtrlr ) < 0 )
  196. xuidNow = 0ull;
  197. DWORD result = 0;
  198. if( XGetOverlappedResult( & data.mFriendsOverlapped, & result, false ) == ERROR_SUCCESS &&
  199. xuidNow &&
  200. XUserGetSigninState( iCtrlr ) == eXUserSigninState_SignedInToLive ) // Search for friends succeeded and the user is still signed in
  201. {
  202. XONLINE_FRIEND * friendBuffer = ( XONLINE_FRIEND * )data.mFriendBuffer;
  203. for( DWORD index = 0; index < result ; ++index )
  204. {
  205. XUID xuidFriend = friendBuffer[ index ].xuid;
  206. static const DWORD dwTitlesSupported[2] = { g_pMatchFramework->GetMatchTitle()->GetTitleID(),
  207. g_pMatchFramework->GetMatchTitle()->GetTitleID() }; // 0x45410830 }; // TODO: add another supported titles
  208. if ( ( friendBuffer[ index ].dwTitleID == dwTitlesSupported[0] ||
  209. friendBuffer[ index ].dwTitleID == dwTitlesSupported[1] )
  210. && arrLocalXuids.Find( xuidFriend ) == arrLocalXuids.InvalidIndex() )
  211. #elif !defined( NO_STEAM )
  212. if ( 1 ) // XHasOverlappedIoCompleted
  213. {
  214. if ( 1 ) // XUserGetSigninState
  215. {
  216. uint64 ullTitleFlags = g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags();
  217. bool bFetchAllFriends = !!( ullTitleFlags & MATCHTITLE_PLAYERMGR_ALLFRIENDS );
  218. bool bManageFriendRequests = !!( ullTitleFlags & MATCHTITLE_PLAYERMGR_FRIENDREQS );
  219. int nSteamFriendsQueryMask = k_EFriendFlagImmediate;
  220. if ( bManageFriendRequests )
  221. nSteamFriendsQueryMask |= ( k_EFriendFlagFriendshipRequested | k_EFriendFlagRequestingFriendship );
  222. int iCtrlr = 0;
  223. int numFriends = steamapicontext->SteamFriends() ? steamapicontext->SteamFriends()->GetFriendCount( nSteamFriendsQueryMask ) : 0;
  224. uint64 uiAppID = steamapicontext->SteamUtils()->GetAppID();
  225. for ( int index = 0; index < numFriends; ++ index )
  226. {
  227. CSteamID steamIDFriend = steamapicontext->SteamFriends()->GetFriendByIndex( index, nSteamFriendsQueryMask );
  228. XUID xuidFriend = steamIDFriend.ConvertToUint64();
  229. FriendGameInfo_t fgi;
  230. bool bInGame = steamapicontext->SteamFriends()->GetFriendGamePlayed( xuidFriend, &fgi );
  231. EFriendRelationship eRelationship = bManageFriendRequests ? steamapicontext->SteamFriends()->GetFriendRelationship( xuidFriend ) : k_EFriendRelationshipFriend;
  232. EPersonaState ePersonaState = steamapicontext->SteamFriends()->GetFriendPersonaState( steamIDFriend );
  233. static bool bPerfectWorld = !!CommandLine()->FindParm( "-perfectworld" );
  234. if ( ( bInGame && fgi.m_gameID.AppID() == uiAppID ) ||
  235. ( eRelationship == k_EFriendRelationshipRequestRecipient ) || ( eRelationship == k_EFriendRelationshipRequestInitiator ) ||
  236. ( bFetchAllFriends && ( ( ePersonaState != k_EPersonaStateOffline ) || bPerfectWorld ) ) )
  237. #else
  238. if ( 1 ) // XHasOverlappedIoCompleted
  239. {
  240. if ( 1 ) // XUserGetSigninState
  241. {
  242. int iCtrlr = 0;
  243. int numFriends = 0;
  244. uint64 uiAppID = 0;
  245. for ( int index = 0; index < numFriends; ++ index )
  246. {
  247. XUID xuidFriend = 0ull;
  248. bool bInGame = false;
  249. if ( bInGame )
  250. #endif
  251. {
  252. PlayerFriend * player = FindPlayerFriend( xuidFriend );
  253. if( ! player )
  254. {
  255. player = new PlayerFriend( xuidFriend );
  256. mFriendsList.AddToTail( player );
  257. }
  258. player->SetIsStale( false );
  259. PlayerFriend::FriendInfo_t fi = {0};
  260. #ifdef _X360
  261. fi.m_szName = friendBuffer[ index ].szGamertag;
  262. fi.m_wszRichPresence = friendBuffer[ index ].wszRichPresence;
  263. fi.m_uiTitleID = friendBuffer[ index ].dwTitleID;
  264. fi.m_xSessionID = friendBuffer[ index ].sessionID;
  265. #elif !defined( NO_STEAM )
  266. uint64 uiLobbyIdFriend = fgi.m_steamIDLobby.ConvertToUint64();
  267. fi.m_uiTitleID = bInGame ? fgi.m_gameID.AppID() : 0;
  268. if ( bInGame && fgi.m_gameID.AppID() == uiAppID )
  269. {
  270. fi.m_xSessionID = ( const XNKID & ) uiLobbyIdFriend;
  271. fi.m_uiGameServerIP = fgi.m_unGameIP;
  272. }
  273. fi.m_szName = steamapicontext->SteamFriends()->GetFriendPersonaName( xuidFriend );
  274. fi.m_wszRichPresence = L"";
  275. switch ( ePersonaState )
  276. {
  277. case k_EPersonaStateOffline: fi.m_wszRichPresence = L"Offline"; fi.m_uiTitleID = uint64( -1 ); break;
  278. case k_EPersonaStateOnline: fi.m_wszRichPresence = L"Online"; break;
  279. case k_EPersonaStateBusy: fi.m_wszRichPresence = L"Busy"; break;
  280. case k_EPersonaStateAway: fi.m_wszRichPresence = L"Away"; break;
  281. case k_EPersonaStateSnooze: fi.m_wszRichPresence = L"Snooze"; break;
  282. case k_EPersonaStateLookingToTrade: fi.m_wszRichPresence = L"LookingToTrade"; break;
  283. case k_EPersonaStateLookingToPlay: fi.m_wszRichPresence = L"LookingToPlay"; break;
  284. }
  285. if ( bManageFriendRequests )
  286. { // When trying to manage friend requests, pass the status via rich presence
  287. if ( eRelationship == k_EFriendRelationshipRequestInitiator )
  288. {
  289. fi.m_wszRichPresence = L"AwaitingRemoteAccept";
  290. fi.m_uiTitleID = uint64( -2 );
  291. }
  292. else if ( eRelationship == k_EFriendRelationshipRequestRecipient )
  293. {
  294. fi.m_wszRichPresence = L"AwaitingLocalAccept";
  295. fi.m_uiTitleID = uint64( -3 );
  296. }
  297. }
  298. #else
  299. uint64 uiLobbyIdFriend = 0ull;
  300. fi.m_szName = "";
  301. fi.m_wszRichPresence = L"";
  302. fi.m_xSessionID = ( const XNKID & ) uiLobbyIdFriend;
  303. #endif
  304. player->UpdateFriendInfo( &fi );
  305. unsigned uiMask = player->GetFriendMark();
  306. uiMask |= ( 1 << iCtrlr );
  307. player->SetFriendMark( uiMask );
  308. }
  309. }
  310. }
  311. // This search has completed
  312. --m_searchesPending;
  313. data.mSearchInProgress = false;
  314. #ifdef _X360
  315. CloseHandle( data.mFriendEnumHandle );
  316. data.mFriendEnumHandle = NULL;
  317. #endif
  318. }
  319. }
  320. }
  321. UpdateLanSearch();
  322. if ( !m_searchesPending ) // Have all searches completed?
  323. {
  324. //we are done searching for friends, remove any that are still marked as old
  325. RemoveOldFriends();
  326. // Signal that we are finished with a search
  327. MEM_ALLOC_CREDIT();
  328. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  329. "OnMatchPlayerMgrUpdate", "update", "searchfinished" ) );
  330. // If nobody request an immediate update, then nudge the next update time
  331. if ( m_flNextUpdateTime )
  332. {
  333. m_flNextUpdateTime = Plat_FloatTime() + mm_player_search_update_interval.GetFloat();
  334. }
  335. }
  336. }
  337. else if( m_bUpdateEnabled && Plat_FloatTime() > m_flNextUpdateTime &&
  338. #ifndef NO_STEAM
  339. steamapicontext->SteamFriends() &&
  340. #endif
  341. !IsLocalClientConnectedToServer() )
  342. {
  343. MarkOldFriends();
  344. #ifdef _GAMECONSOLE
  345. for ( DWORD i = 0; i < XBX_GetNumGameUsers(); ++i )
  346. {
  347. CreateFriendEnumeration( XBX_GetUserId( i ) );
  348. }
  349. #else
  350. CreateFriendEnumeration( 0 );
  351. #endif
  352. CreateLanSearch();
  353. // Signal that we are starting a search
  354. MEM_ALLOC_CREDIT();
  355. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  356. "OnMatchPlayerMgrUpdate", "update", "searchstarted" ) );
  357. // Nudge the next update time to indicate that update has started
  358. m_flNextUpdateTime = Plat_FloatTime() + mm_player_search_update_interval.GetFloat();
  359. }
  360. //
  361. // Let all the player classes run the update loop
  362. //
  363. for ( int iIndex = 0; iIndex < ARRAYSIZE( mLocalPlayer ); ++ iIndex )
  364. {
  365. PlayerLocal *player = mLocalPlayer[iIndex];
  366. if ( player )
  367. player->Update();
  368. }
  369. for ( int iIndex = 0; iIndex < mFriendsList.Count(); ++ iIndex )
  370. {
  371. PlayerFriend *player = mFriendsList[iIndex];
  372. if ( player )
  373. player->Update();
  374. }
  375. ExecuteStoreStatsRequest();
  376. }
  377. void PlayerManager::UpdateLanSearch()
  378. {
  379. if ( !m_lanSearchData.m_bSearchInProgress )
  380. return;
  381. if ( m_lanSearchData.m_flStartTime && m_lanSearchData.m_flLastBroadcastTime )
  382. {
  383. if ( Plat_FloatTime() > m_lanSearchData.m_flStartTime + mm_player_search_lan_ping_duration.GetFloat() )
  384. {
  385. m_lanSearchData.m_bSearchInProgress = false;
  386. -- m_searchesPending;
  387. return;
  388. }
  389. if ( Plat_FloatTime() < m_lanSearchData.m_flLastBroadcastTime + mm_player_search_lan_ping_interval.GetFloat() )
  390. {
  391. // waiting out interval between pings
  392. return;
  393. }
  394. }
  395. else
  396. {
  397. // Initialize the start time of the lan broadcast
  398. m_lanSearchData.m_flStartTime = Plat_FloatTime();
  399. }
  400. //
  401. // Send the packet
  402. //
  403. m_lanSearchData.m_flLastBroadcastTime = Plat_FloatTime();
  404. MEM_ALLOC_CREDIT();
  405. g_pConnectionlessLanMgr->SendPacket( KeyValues::AutoDeleteInline( new KeyValues(
  406. "LanSearch"
  407. ) ) );
  408. }
  409. enum SyncKeyValueDirection_t
  410. {
  411. KVSTAT_WRITE_STAT,
  412. KVSTAT_READ_STAT
  413. };
  414. static void SyncKeyValueWithStatField( KeyValues *kvValue, IPlayerLocal *pPlayerLocal, TitleDataFieldsDescription_t const *pField, SyncKeyValueDirection_t eOp )
  415. {
  416. switch( pField->m_eDataType )
  417. {
  418. case TitleDataFieldsDescription_t::DT_BITFIELD:
  419. if ( eOp == KVSTAT_WRITE_STAT )
  420. TitleDataFieldsDescriptionSetBit( pField, pPlayerLocal, !!kvValue->GetInt( "" ) );
  421. else
  422. kvValue->SetInt( "", TitleDataFieldsDescriptionGetBit( pField, pPlayerLocal ) ? 1 : 0 );
  423. break;
  424. case TitleDataFieldsDescription_t::DT_uint8:
  425. if ( eOp == KVSTAT_WRITE_STAT )
  426. TitleDataFieldsDescriptionSetValue<uint8>( pField, pPlayerLocal, (uint8)kvValue->GetInt( "" ) );
  427. else
  428. kvValue->SetInt( "", TitleDataFieldsDescriptionGetValue<uint8>( pField, pPlayerLocal ) );
  429. break;
  430. case TitleDataFieldsDescription_t::DT_uint16:
  431. if ( eOp == KVSTAT_WRITE_STAT )
  432. TitleDataFieldsDescriptionSetValue<uint16>( pField, pPlayerLocal, (uint16)kvValue->GetInt( "" ) );
  433. else
  434. kvValue->SetInt( "", TitleDataFieldsDescriptionGetValue<uint16>( pField, pPlayerLocal ) );
  435. break;
  436. case TitleDataFieldsDescription_t::DT_uint32:
  437. if ( eOp == KVSTAT_WRITE_STAT )
  438. TitleDataFieldsDescriptionSetValue<uint32>( pField, pPlayerLocal, (uint32)kvValue->GetInt( "" ) );
  439. else
  440. kvValue->SetInt( "", TitleDataFieldsDescriptionGetValue<uint32>( pField, pPlayerLocal ) );
  441. break;
  442. case TitleDataFieldsDescription_t::DT_float:
  443. if ( eOp == KVSTAT_WRITE_STAT )
  444. TitleDataFieldsDescriptionSetValue<float>( pField, pPlayerLocal, (float)kvValue->GetFloat( "" ) );
  445. else
  446. kvValue->SetInt( "", TitleDataFieldsDescriptionGetValue<float>( pField, pPlayerLocal ) );
  447. break;
  448. case TitleDataFieldsDescription_t::DT_uint64:
  449. if ( eOp == KVSTAT_WRITE_STAT )
  450. TitleDataFieldsDescriptionSetValue<uint64>( pField, pPlayerLocal, (uint64)kvValue->GetUint64( "" ) );
  451. else
  452. kvValue->SetUint64( "", TitleDataFieldsDescriptionGetValue<uint64>( pField, pPlayerLocal ) );
  453. break;
  454. }
  455. }
  456. void PlayerManager::OnEvent( KeyValues *pEvent )
  457. {
  458. char const *szName = pEvent->GetName();
  459. if ( !Q_stricmp( szName, "OnNetLanConnectionlessPacket" ) )
  460. {
  461. if ( IsPC() && !m_lanSearchData.m_bSearchInProgress )
  462. return;
  463. if ( IsLocalClientConnectedToServer() )
  464. return;
  465. if ( KeyValues *pFriendGame = pEvent->FindKey( "GameDetailsPlayer" ) )
  466. {
  467. // Incoming data:
  468. //
  469. // Options
  470. // sessioninfo
  471. // Player
  472. // xuid
  473. // xuidonline
  474. // name
  475. // binary
  476. // ptr -> QOS block
  477. XUID xuid = pFriendGame->GetUint64( "player/xuidOnline", 0ull );
  478. if ( !xuid )
  479. xuid = pFriendGame->GetUint64( "player/xuid", 0ull );
  480. if ( !xuid )
  481. return;
  482. // Check if this is not our local client
  483. for ( int k = 0; k < ARRAYSIZE( mLocalPlayer ); ++ k )
  484. {
  485. if ( !mLocalPlayer[k] )
  486. continue;
  487. XUID xuidLocal = mLocalPlayer[k]->GetXUID();
  488. if ( xuidLocal == xuid )
  489. return;
  490. }
  491. // Unpack the QOS data block
  492. MM_GameDetails_QOS_t gd = {
  493. pFriendGame->GetPtr( "binary/ptr" ),
  494. pFriendGame->GetInt( "binary/size" ),
  495. 0 };
  496. KeyValues *pGameDetails = g_pMatchFramework->GetMatchNetworkMsgController()->UnpackGameDetailsFromQOS( &gd );
  497. KeyValues::AutoDelete autodelete( pGameDetails );
  498. // On X360 do NOT let through unsolicited packets unless they are system link info
  499. if ( IsX360() && !m_lanSearchData.m_bSearchInProgress && Q_stricmp( "lan", pGameDetails->GetString( "system/network" ) ) )
  500. return;
  501. // Find or create the player friend that these game details belong to
  502. PlayerFriend *player = FindPlayerFriend( xuid );
  503. if ( !player )
  504. {
  505. player = new PlayerFriend( xuid );
  506. mFriendsList.AddToTail( player );
  507. }
  508. player->SetIsStale( false );
  509. player->SetFriendMark( ~0u );
  510. if ( pGameDetails )
  511. {
  512. // Append "player" and "options" subkeys
  513. if ( KeyValues *kvSubkey = pFriendGame->FindKey( "options" ) )
  514. pGameDetails->FindKey( "options", true )->MergeFrom( kvSubkey, KeyValues::MERGE_KV_UPDATE );
  515. if ( KeyValues *kvSubkey = pFriendGame->FindKey( "player" ) )
  516. pGameDetails->FindKey( "player", true )->MergeFrom( kvSubkey, KeyValues::MERGE_KV_UPDATE );
  517. }
  518. //
  519. // Set friend data
  520. //
  521. PlayerFriend::FriendInfo_t fi = {0};
  522. fi.m_szName = pFriendGame->GetString( "player/name", "" );
  523. fi.m_pGameDetails = pGameDetails;
  524. fi.m_uiTitleID = g_pMatchFramework->GetMatchTitle()->GetTitleID();
  525. fi.m_uiGameServerIP = ~0u;
  526. player->UpdateFriendInfo( &fi );
  527. }
  528. }
  529. else if( !Q_stricmp( szName, "OnSysSigninChange" ) )
  530. {
  531. OnSigninChange( pEvent );
  532. }
  533. else if ( !Q_stricmp( szName, "OnProfilesChanged" ) )
  534. {
  535. OnGameUsersChanged();
  536. }
  537. else if ( !Q_stricmp( szName, "OnUnlockArcadeTitle" ) )
  538. {
  539. #if defined ( _X360 )
  540. for ( int k = 0; k < ARRAYSIZE( mLocalPlayer ); ++ k )
  541. {
  542. if ( mLocalPlayer[k] )
  543. {
  544. SignalXWriteOpportunity( MMXWO_SETTINGS );
  545. mLocalPlayer[k]->WriteTitleData();
  546. }
  547. }
  548. #endif
  549. }
  550. else if ( !Q_stricmp( szName, "OnSysProfileSettingsChanged" ) )
  551. {
  552. for ( int k = 0; k < ARRAYSIZE( mLocalPlayer ); ++ k )
  553. {
  554. if ( mLocalPlayer[k] && pEvent->GetInt( CFmtStr( "user%d", k ) ) )
  555. {
  556. DevMsg( "Reloading player profile data for ctrlr%d (%s)\n", k, mLocalPlayer[k]->GetName() );
  557. mLocalPlayer[k]->LoadPlayerProfileData();
  558. }
  559. }
  560. }
  561. #ifdef _X360
  562. else if ( !Q_stricmp( szName, "OnSysStorageDlcInstalled" ) )
  563. {
  564. // New content requires users to sign in again,
  565. // the sender of the notification guarantees that there
  566. // is new content available for this game and requires
  567. // a search path update.
  568. g_pMatchFramework->CloseSession();
  569. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchPlayerMgrReset", "reason", "OnSysStorageDlcInstalled" ) );
  570. XBX_SetNumGameUsers( 0 );
  571. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnProfilesChanged", "numProfiles", int(0) ) );
  572. }
  573. #endif
  574. else if ( !Q_stricmp( szName, "OnProfilesWriteOpportunity" ) )
  575. {
  576. char const *szReason = pEvent->GetString( "reason" );
  577. MM_XWriteOpportunity mmxwo = MMXWO_NONE;
  578. if ( !Q_stricmp( "checkpoint", szReason ) )
  579. mmxwo = MMXWO_CHECKPOINT;
  580. else if ( !Q_stricmp( "sessionstart", szReason ) )
  581. mmxwo = MMXWO_SESSION_STARTED;
  582. else if ( !Q_stricmp( "sessionend", szReason ) )
  583. mmxwo = MMXWO_SESSION_FINISHED;
  584. else if ( !Q_stricmp( "settings", szReason ) )
  585. mmxwo = MMXWO_SETTINGS;
  586. else if ( !Q_stricmp( "deactivation", szReason ) )
  587. {
  588. // The controllers are about to be deactivated, but
  589. // the actual signed in users at the controllers indices
  590. // are not changing.
  591. // Use this opportunity to write profile data if
  592. // XWriteOpportunity is allowing to do so.
  593. // This is the last chance to use currently signed in
  594. // players before they will be deactivated and destroyed.
  595. for ( int k = 0; k < ARRAYSIZE( mLocalPlayer ); ++ k )
  596. {
  597. if ( mLocalPlayer[k] )
  598. mLocalPlayer[k]->WriteTitleData();
  599. }
  600. ExecuteStoreStatsRequest();
  601. return;
  602. }
  603. else
  604. return;
  605. // Signal a write opportunity
  606. SignalXWriteOpportunity( mmxwo );
  607. }
  608. else if ( !Q_stricmp( szName, "Client::CmdKeyValues" ) )
  609. {
  610. KeyValues *pCmd = pEvent->GetFirstTrueSubKey();
  611. if ( !pCmd )
  612. return;
  613. int nSlot = pEvent->GetInt( "slot" );
  614. int iCtrlr = XBX_GetUserId( nSlot );
  615. IPlayerLocal *pPlayerLocal = GetLocalPlayer( iCtrlr );
  616. char const *szCmd = pCmd->GetName();
  617. if ( !Q_stricmp( "write_awards", szCmd ) )
  618. {
  619. if ( pPlayerLocal )
  620. {
  621. pPlayerLocal->UpdateAwardsData( pCmd );
  622. }
  623. else
  624. {
  625. DevWarning( "pPlayerLocal(#%d)->write_awards UNKNOWN SLOT!\n", nSlot );
  626. }
  627. }
  628. else if ( !Q_stricmp( "read_awards", szCmd ) )
  629. {
  630. KeyValues *kvReply = pCmd->MakeCopy();
  631. if ( pPlayerLocal )
  632. {
  633. pPlayerLocal->GetAwardsData( kvReply );
  634. }
  635. else
  636. {
  637. DevWarning( "pPlayerLocal(#%d)->read_awards UNKNOWN SLOT!\n", nSlot );
  638. }
  639. // Send the reply to server
  640. int nActiveSlot = g_pMatchExtensions->GetIVEngineClient()->GetActiveSplitScreenPlayerSlot();
  641. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nSlot );
  642. g_pMatchExtensions->GetIVEngineClient()->ServerCmdKeyValues( kvReply );
  643. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nActiveSlot );
  644. }
  645. else if ( !Q_stricmp( "write_stats", szCmd ) )
  646. {
  647. if ( pPlayerLocal )
  648. {
  649. TitleDataFieldsDescription_t const *pFields = g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage();
  650. for ( KeyValues *kvValue = pCmd->GetFirstValue(); kvValue; kvValue = kvValue->GetNextValue() )
  651. {
  652. char const *szStatName = kvValue->GetName();
  653. // Try to find the stat to write
  654. if ( TitleDataFieldsDescription_t const *pField = TitleDataFieldsDescriptionFindByString( pFields, szStatName ) )
  655. {
  656. // Found the stat to write
  657. DevMsg( "pPlayerLocal(%s)->write_stat(%s)\n", pPlayerLocal->GetName(), pField->m_szFieldName );
  658. SyncKeyValueWithStatField( kvValue, pPlayerLocal, pField, KVSTAT_WRITE_STAT );
  659. szStatName = NULL;
  660. }
  661. if ( szStatName )
  662. {
  663. DevWarning( "pPlayerLocal(%s)->write_stat(%s) UNKNOWN STAT!\n", pPlayerLocal->GetName(), szStatName );
  664. }
  665. }
  666. }
  667. else
  668. {
  669. DevWarning( "pPlayerLocal(#%d)->write_stat UNKNOWN SLOT!\n", nSlot );
  670. }
  671. }
  672. else if ( !Q_stricmp( "read_stats", szCmd ) )
  673. {
  674. KeyValues *kvReply = pCmd->MakeCopy();
  675. if ( pPlayerLocal )
  676. {
  677. TitleDataFieldsDescription_t const *pFields = g_pMatchFramework->GetMatchTitle()->DescribeTitleDataStorage();
  678. for ( KeyValues *kvValue = kvReply->GetFirstValue(); kvValue; kvValue = kvValue->GetNextValue() )
  679. {
  680. char const *szStatName = kvValue->GetName();
  681. // Try to find the stat to read
  682. if ( TitleDataFieldsDescription_t const *pField = TitleDataFieldsDescriptionFindByString( pFields, szStatName ) )
  683. {
  684. // Found the stat to read
  685. SyncKeyValueWithStatField( kvValue, pPlayerLocal, pField, KVSTAT_READ_STAT );
  686. szStatName = NULL;
  687. }
  688. if ( szStatName )
  689. {
  690. DevWarning( "pPlayerLocal(%s)->read_stat(%s) UNKNOWN STAT!\n", pPlayerLocal->GetName(), szStatName );
  691. }
  692. }
  693. }
  694. else
  695. {
  696. DevWarning( "pPlayerLocal(#%d)->read_stats UNKNOWN SLOT!\n", nSlot );
  697. }
  698. // Send the reply to server
  699. int nActiveSlot = g_pMatchExtensions->GetIVEngineClient()->GetActiveSplitScreenPlayerSlot();
  700. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nSlot );
  701. g_pMatchExtensions->GetIVEngineClient()->ServerCmdKeyValues( kvReply );
  702. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nActiveSlot );
  703. }
  704. else if ( !Q_stricmp( "write_leaderboard", szCmd ) )
  705. {
  706. if ( pPlayerLocal )
  707. {
  708. pPlayerLocal->UpdateLeaderboardData( pCmd );
  709. }
  710. else
  711. {
  712. DevWarning( "pPlayerLocal(#%d)->write_leaderboard UNKNOWN SLOT!\n", nSlot );
  713. }
  714. }
  715. else if ( !Q_stricmp( "read_leaderboard", szCmd ) )
  716. {
  717. KeyValues *kvReply = pCmd->MakeCopy();
  718. if ( pPlayerLocal )
  719. {
  720. pPlayerLocal->GetLeaderboardData( kvReply );
  721. }
  722. else
  723. {
  724. DevWarning( "pPlayerLocal(#%d)->read_leaderboard UNKNOWN SLOT!\n", nSlot );
  725. }
  726. // Send the reply to server
  727. int nActiveSlot = g_pMatchExtensions->GetIVEngineClient()->GetActiveSplitScreenPlayerSlot();
  728. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nSlot );
  729. g_pMatchExtensions->GetIVEngineClient()->ServerCmdKeyValues( kvReply );
  730. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nActiveSlot );
  731. }
  732. }
  733. else if ( !Q_stricmp( szName, "OnProfileLeaderboardData" ) )
  734. {
  735. if ( !g_pMatchExtensions->GetIVEngineClient()->IsConnected() )
  736. return;
  737. #ifdef _GAMECONSOLE
  738. int iController = pEvent->GetInt( "iController" );
  739. int iPlayerSlot = XBX_GetSlotByUserId( iController );
  740. #else
  741. int iPlayerSlot = 0;
  742. #endif
  743. // Send the leaderboard data to server
  744. int nActiveSlot = g_pMatchExtensions->GetIVEngineClient()->GetActiveSplitScreenPlayerSlot();
  745. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( iPlayerSlot );
  746. KeyValues *kvForServer = pEvent->MakeCopy();
  747. kvForServer->SetName( "read_leaderboard" );
  748. g_pMatchExtensions->GetIVEngineClient()->ServerCmdKeyValues( kvForServer );
  749. g_pMatchExtensions->GetIVEngineClient()->SetActiveSplitScreenPlayerSlot( nActiveSlot );
  750. }
  751. }
  752. bool IsUserSignedInProperly( int iCtrlr )
  753. {
  754. #ifdef _X360
  755. XUSER_SIGNIN_INFO xsi;
  756. if ( iCtrlr >= 0 && iCtrlr < XUSER_MAX_COUNT &&
  757. XUserGetSigninState( iCtrlr ) != eXUserSigninState_NotSignedIn &&
  758. ERROR_SUCCESS == XUserGetSigninInfo( iCtrlr, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) &&
  759. !(xsi.dwInfoFlags & XUSER_INFO_FLAG_GUEST) )
  760. return true;
  761. else
  762. return false;
  763. #else
  764. return true;
  765. #endif
  766. }
  767. void PlayerManager::OnSigninChange( KeyValues *pEvent )
  768. {
  769. #ifdef _X360
  770. char const *szAction = pEvent->GetString( "action" );
  771. int numUsers = pEvent->GetInt( "numUsers" );
  772. bool bCommittedSignOutExplicitNotification = false;
  773. if ( !Q_stricmp( "signout", szAction ) )
  774. {
  775. for ( int iSignedOut = 0; iSignedOut < numUsers; ++ iSignedOut )
  776. {
  777. int iCtrlrSignedOut = pEvent->GetInt( CFmtStr( "user%d", iSignedOut ) );
  778. XBX_SetStorageDeviceId( iCtrlrSignedOut, XBX_INVALID_STORAGE_ID );
  779. for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
  780. {
  781. int iController = XBX_GetUserId( k );
  782. if ( iCtrlrSignedOut == iController &&
  783. !XBX_GetPrimaryUserIsGuest() )
  784. {
  785. bCommittedSignOutExplicitNotification = true;
  786. }
  787. }
  788. }
  789. }
  790. // To maintain a list of selected storage devices, walk the list of
  791. // currently signed in users and drop ones that are no longer signed in
  792. for ( int k = 0; k < XUSER_MAX_COUNT; ++ k )
  793. {
  794. XUSER_SIGNIN_INFO xsi;
  795. if ( ERROR_SUCCESS != XUserGetSigninInfo( k, XUSER_GET_SIGNIN_INFO_OFFLINE_XUID_ONLY, &xsi ) ||
  796. !xsi.xuid )
  797. {
  798. XBX_SetStorageDeviceId( k, XBX_INVALID_STORAGE_ID );
  799. }
  800. }
  801. //
  802. // Check if either of the committed ctrlrs signed out
  803. //
  804. bool bCommittedCtrlrSignedOut = false;
  805. bool bLiveChangeDetected = false;
  806. //
  807. // Now handle users signing in and out
  808. //
  809. if ( XBX_GetNumGameUsers() > 0 &&
  810. !XBX_GetPrimaryUserIsGuest() &&
  811. !bCommittedSignOutExplicitNotification )
  812. {
  813. for ( DWORD k = 0; k < XBX_GetNumGameUsers(); ++ k )
  814. {
  815. int iController = XBX_GetUserId( k );
  816. uint state = XUserGetSigninState( iController );
  817. if( state == eXUserSigninState_NotSignedIn )
  818. {
  819. bCommittedCtrlrSignedOut = true;
  820. break;
  821. }
  822. else if ( PlayerLocal *player = ( PlayerLocal * ) GetLocalPlayer( iController ) )
  823. {
  824. IPlayer::OnlineState_t eOnlineState = player->GetOnlineState();
  825. player->DetectOnlineState();
  826. if ( eOnlineState == IPlayer::STATE_ONLINE &&
  827. player->GetOnlineState() != IPlayer::STATE_ONLINE )
  828. {
  829. bLiveChangeDetected = true;
  830. OnLocalPlayerDisconnectedFromLive( iController );
  831. }
  832. }
  833. }
  834. }
  835. //
  836. // Check the invited user
  837. //
  838. bool bInviteAbandon = false;
  839. if ( XBX_INVALID_USER_ID != XBX_GetInvitedUserId() )
  840. {
  841. int iController = XBX_GetInvitedUserId();
  842. uint state = XUserGetSigninState( iController );
  843. if( state == eXUserSigninState_NotSignedIn )
  844. {
  845. bInviteAbandon = true;
  846. }
  847. else
  848. {
  849. bool bLiveEnabled = false;
  850. if ( state == eXUserSigninState_SignedInToLive )
  851. {
  852. BOOL bValue = false;
  853. if ( ERROR_SUCCESS == XUserCheckPrivilege( iController, XPRIVILEGE_MULTIPLAYER_SESSIONS, &bValue ) )
  854. bLiveEnabled = bValue ? true : false;
  855. }
  856. if ( !bLiveEnabled )
  857. {
  858. bInviteAbandon = true;
  859. }
  860. }
  861. }
  862. // if ( bInviteAbandon )
  863. // {
  864. // if ( s_pbInviteApproved )
  865. // {
  866. // // Was still waiting for approval
  867. // s_nInviteApprovalConf = -2; // will decline invite acceptance next frame
  868. // }
  869. // else
  870. // {
  871. // // On the way into the invite game
  872. // bCommittedCtrlrSignedOut = true;
  873. // }
  874. // DevMsg( "[L4DMM] InviteCancel due to abandoned user.\n" );
  875. // matchmaking->InviteCancel();
  876. // }
  877. // A guest just signed in mid-game, so kick them!
  878. if ( XBX_GetNumGameUsers() > 0 && !Q_stricmp( "signin", szAction ) && XBX_GetPrimaryUserIsGuest() )
  879. {
  880. for ( int iSignedIn = 0; iSignedIn < numUsers; ++ iSignedIn )
  881. {
  882. int iCtrlrSignedIn = pEvent->GetInt( CFmtStr( "user%d", iSignedIn ) );
  883. if ( (unsigned int) iCtrlrSignedIn == XBX_GetPrimaryUserId() )
  884. {
  885. if ( IsUserSignedInProperly( XBX_GetPrimaryUserId() ) )
  886. {
  887. MEM_ALLOC_CREDIT();
  888. IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession();
  889. KeyValues *pSessionSettings = pIMatchSession ? pIMatchSession->GetSessionSettings() : NULL;
  890. KeyValues *kvGuestSignedInEvent = new KeyValues( "OnMatchPlayerMgrReset", "reason", "GuestSignedIn" );
  891. if ( pSessionSettings )
  892. kvGuestSignedInEvent->AddSubKey( pSessionSettings->MakeCopy() );
  893. g_pMatchFramework->CloseSession();
  894. g_pMatchEventsSubscription->BroadcastEvent( kvGuestSignedInEvent );
  895. XBX_SetNumGameUsers( 0 );
  896. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnProfilesChanged", "numProfiles", int(0) ) );
  897. return;
  898. }
  899. }
  900. }
  901. }
  902. if( ( XBX_GetNumGameUsers() > 0 && bCommittedSignOutExplicitNotification ) ||
  903. bCommittedCtrlrSignedOut )
  904. {
  905. MEM_ALLOC_CREDIT();
  906. g_pMatchFramework->CloseSession();
  907. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnMatchPlayerMgrReset", "reason", "GameUserSignedOut" ) );
  908. XBX_SetNumGameUsers( 0 );
  909. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnProfilesChanged", "numProfiles", int(0) ) );
  910. return;
  911. }
  912. if ( bLiveChangeDetected )
  913. {
  914. OnLostConnectionToConsoleNetwork();
  915. }
  916. #endif
  917. }
  918. void PlayerManager::OnLostConnectionToConsoleNetwork()
  919. {
  920. EnableFriendsUpdate( m_bUpdateEnabled );
  921. if ( IMatchSession *pIMatchSession = g_pMatchFramework->GetMatchSession() )
  922. {
  923. char const *szNetwork = pIMatchSession->GetSessionSettings()->GetString( "system/network", "LIVE" );
  924. if ( !Q_stricmp( szNetwork, "LIVE" ) )
  925. {
  926. // There is an active LIVE session
  927. g_pMatchFramework->CloseSession();
  928. g_pMatchEventsSubscription->BroadcastEvent( new KeyValues( "OnEngineDisconnectReason", "reason", "Lost connection to LIVE" ) );
  929. }
  930. }
  931. }
  932. #if defined( _PS3 ) && !defined( NO_STEAM )
  933. void PlayerManager::Steam_OnPS3PSNStatusChange( PS3PSNStatusChange_t *pParam )
  934. {
  935. if ( !pParam->m_bPSNOnline )
  936. {
  937. OnLostConnectionToConsoleNetwork();
  938. }
  939. }
  940. #endif
  941. void PlayerManager::OnGameUsersChanged()
  942. {
  943. DevMsg( "PlayerManager::OnGameUsersChanged\n" );
  944. //
  945. // Cleanup all players currently created
  946. //
  947. for ( int k = 0; k < mFriendsList.Count(); ++ k )
  948. {
  949. PlayerFriend *&player = mFriendsList[ k ];
  950. if ( player )
  951. player->Destroy();
  952. player = NULL;
  953. }
  954. mFriendsList.RemoveAll();
  955. for ( int k = 0; k < ARRAYSIZE( mLocalPlayer ); ++ k )
  956. {
  957. PlayerLocal *&player = mLocalPlayer[k];
  958. if ( player )
  959. player->Destroy();
  960. player = NULL;
  961. }
  962. #ifdef _GAMECONSOLE
  963. DWORD dwPresenceValue[ XUSER_MAX_COUNT ] = {0};
  964. for ( int idx = 0; idx < (int) XBX_GetNumGameUsers(); ++ idx )
  965. {
  966. int iController = XBX_GetUserId( idx );
  967. PlayerLocal *player = new PlayerLocal( iController );
  968. mLocalPlayer[ iController ] = player;
  969. dwPresenceValue[ iController ] = !XBX_GetUserIsGuest( idx );
  970. }
  971. // Set all players rich presence to idle (0) or main menu (1)
  972. for ( int iCtrlr = 0; iCtrlr < XUSER_MAX_COUNT; ++ iCtrlr )
  973. {
  974. #ifdef _X360
  975. XUserSetContextEx( iCtrlr, X_CONTEXT_PRESENCE, dwPresenceValue[iCtrlr], MMX360_NewOverlappedDormant() );
  976. #endif
  977. }
  978. #else
  979. #if !defined( NO_STEAM )
  980. if ( !steamapicontext->SteamUser() )
  981. return;
  982. #endif
  983. PlayerLocal * player = new PlayerLocal( 0 );
  984. mLocalPlayer[0] = player;
  985. #endif
  986. // Start a search when the sign-on changes
  987. EnableFriendsUpdate( true );
  988. #if !defined( NO_STEAM )
  989. Update(); // Update immediately to start friends search
  990. Update(); // Update one more time to actually pick up friends
  991. #endif
  992. }
  993. void PlayerManager::RecomputePlayerXUIDs( char const *szNetwork )
  994. {
  995. for ( int k = 0; k < ARRAYSIZE( mLocalPlayer ); ++ k )
  996. {
  997. PlayerLocal *player = mLocalPlayer[k];
  998. if ( player )
  999. {
  1000. player->RecomputeXUID( szNetwork );
  1001. }
  1002. }
  1003. }
  1004. void PlayerManager::RequestStoreStats()
  1005. {
  1006. m_bRequestStoreStats = true;
  1007. }
  1008. void PlayerManager::ExecuteStoreStatsRequest()
  1009. {
  1010. if ( !m_bRequestStoreStats )
  1011. return;
  1012. m_bRequestStoreStats = false;
  1013. #ifndef NO_STEAM
  1014. if ( steamapicontext->SteamUserStats() )
  1015. {
  1016. steamapicontext->SteamUserStats()->StoreStats();
  1017. }
  1018. #endif
  1019. }
  1020. void PlayerManager::EnableFriendsUpdate( bool bEnable )
  1021. {
  1022. if ( bEnable &&
  1023. !IsX360() && ( g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_PLAYERMGR_DISABLED ) ) // On X360 system link games still must use lan probes
  1024. bEnable = false;
  1025. m_bUpdateEnabled = bEnable;
  1026. m_flNextUpdateTime = 0.0f;
  1027. m_lanSearchData.m_flStartTime = 0.0f;
  1028. m_lanSearchData.m_flLastBroadcastTime = 0.0f;
  1029. // If enabled the search, then we'll pick it up next frame
  1030. if ( bEnable )
  1031. return;
  1032. // Otherwise searches are disabled, cancel everything
  1033. // TODO: cancel
  1034. }
  1035. void PlayerManager::CreateLanSearch()
  1036. {
  1037. if ( !IsX360() && ( g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_PLAYERMGR_DISABLED ) )
  1038. return;
  1039. if ( !m_lanSearchData.m_bSearchInProgress )
  1040. {
  1041. m_lanSearchData.m_bSearchInProgress = true;
  1042. ++ m_searchesPending;
  1043. }
  1044. m_lanSearchData.m_flStartTime = 0.0f;
  1045. m_lanSearchData.m_flLastBroadcastTime = 0.0f;
  1046. }
  1047. void PlayerManager::CreateFriendEnumeration( int iCtrlr )
  1048. {
  1049. SFriendSearchData &data = m_searchData[ iCtrlr ];
  1050. if ( g_pMatchFramework->GetMatchTitle()->GetTitleSettingsFlags() & MATCHTITLE_PLAYERMGR_DISABLED )
  1051. return;
  1052. #ifdef _X360
  1053. // Check if we are still doing the previous search - it this
  1054. // case we will just search again later
  1055. if ( data.mFriendEnumHandle )
  1056. return;
  1057. DWORD bufferSize = 0;
  1058. data.mFriendsStartIndex = 0;
  1059. XUserGetXUID( iCtrlr, &data.mXuid );
  1060. const uint numFriendsRequest = 100;
  1061. DWORD ret = g_pMatchExtensions->GetIXOnline()->XFriendsCreateEnumerator(
  1062. iCtrlr, data.mFriendsStartIndex, numFriendsRequest,
  1063. &bufferSize, &data.mFriendEnumHandle );
  1064. if ( ret == ERROR_SUCCESS )
  1065. {
  1066. //we are good to start the enumeration
  1067. if ( bufferSize > (DWORD)data.mFriendBufferSize )
  1068. {
  1069. delete data.mFriendBuffer;
  1070. data.mFriendBuffer = new char[bufferSize];
  1071. data.mFriendBufferSize = bufferSize;
  1072. }
  1073. ret = XEnumerate( data.mFriendEnumHandle,
  1074. data.mFriendBuffer, data.mFriendBufferSize, NULL, &data.mFriendsOverlapped );
  1075. if ( ret == ERROR_IO_PENDING )
  1076. {
  1077. data.mSearchInProgress = true;
  1078. ++m_searchesPending;
  1079. }
  1080. else
  1081. {
  1082. CloseHandle( data.mFriendEnumHandle );
  1083. data.mFriendEnumHandle = NULL;
  1084. }
  1085. }
  1086. else
  1087. {
  1088. ExecuteNTimes( 5, DevWarning( "XFriendsCreateEnumerator failed (code = 0x%08X)!\n", ret ) );
  1089. data.mFriendEnumHandle = NULL;
  1090. }
  1091. #else
  1092. if ( data.mSearchInProgress )
  1093. return;
  1094. // We need to look at all friends
  1095. #if !defined( NO_STEAM )
  1096. if ( !steamapicontext->SteamFriends() )
  1097. return;
  1098. #endif
  1099. data.mSearchInProgress = true;
  1100. ++ m_searchesPending;
  1101. #endif
  1102. }
  1103. #else // SWDS
  1104. class PlayerManager *g_pPlayerManager = NULL;
  1105. #endif