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.

1350 lines
36 KiB

  1. //===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "mm_framework.h"
  7. #include "fmtstr.h"
  8. // memdbgon must be the last include file in a .cpp file!!!
  9. #include "tier0/memdbgon.h"
  10. #ifdef _X360
  11. //
  12. // Dormant operations
  13. //
  14. struct OverlappedDormant_t
  15. {
  16. XOVERLAPPED *m_pOverlapped;
  17. void ( *m_pfnCompletion )( XOVERLAPPED *, void * );
  18. void *m_pvParam;
  19. };
  20. static CUtlVector< OverlappedDormant_t > s_arrOverlappedDormants;
  21. struct ScheduledCall_t
  22. {
  23. float m_flTimeToCall;
  24. void ( *m_pfnCall )( ScheduledCall_t *pThis );
  25. };
  26. static CUtlVector< ScheduledCall_t * > s_arrScheduledCalls;
  27. static CUtlVector< IDormantOperation * > s_arrDormantInterfaces;
  28. XOVERLAPPED * MMX360_NewOverlappedDormant( void (*pfnCompletion)( XOVERLAPPED *, void * ), void *pvParam )
  29. {
  30. XOVERLAPPED *pOverlapped = new XOVERLAPPED;
  31. memset( pOverlapped, 0, sizeof( XOVERLAPPED ) );
  32. OverlappedDormant_t od = { pOverlapped, pfnCompletion, pvParam };
  33. s_arrOverlappedDormants.AddToTail( od );
  34. return pOverlapped;
  35. }
  36. void MMX360_RegisterDormant( IDormantOperation *pDormant )
  37. {
  38. if ( !pDormant )
  39. return;
  40. if ( s_arrDormantInterfaces.Find( pDormant ) != s_arrDormantInterfaces.InvalidIndex() )
  41. return;
  42. s_arrDormantInterfaces.AddToTail( pDormant );
  43. }
  44. void MMX360_UpdateDormantOperations()
  45. {
  46. int nQueuedOverlapped = s_arrOverlappedDormants.Count();
  47. for ( int k = 0; k < s_arrOverlappedDormants.Count(); ++ k )
  48. {
  49. OverlappedDormant_t od = s_arrOverlappedDormants[k];
  50. if ( XHasOverlappedIoCompleted( od.m_pOverlapped ) )
  51. {
  52. s_arrOverlappedDormants.FastRemove( k -- );
  53. if ( od.m_pfnCompletion )
  54. {
  55. (od.m_pfnCompletion)( od.m_pOverlapped, od.m_pvParam );
  56. }
  57. delete od.m_pOverlapped;
  58. }
  59. }
  60. if ( nQueuedOverlapped && !s_arrOverlappedDormants.Count() )
  61. {
  62. DevMsg( 2, "MMX360_UpdateDormantOperations finished all overlapped calls.\n" );
  63. }
  64. int nQueuedScheduledCalls = s_arrScheduledCalls.Count();
  65. for ( int k = 0; k < s_arrScheduledCalls.Count(); ++ k )
  66. {
  67. ScheduledCall_t *pCall = s_arrScheduledCalls[k];
  68. if ( Plat_FloatTime() > pCall->m_flTimeToCall )
  69. {
  70. s_arrScheduledCalls.FastRemove( k -- );
  71. ( pCall->m_pfnCall )( pCall );
  72. }
  73. }
  74. if ( nQueuedScheduledCalls && !s_arrScheduledCalls.Count() )
  75. {
  76. DevMsg( 2, "MMX360_UpdateDormantOperations finished all scheduled calls.\n" );
  77. }
  78. int nDormantInterfaces = s_arrDormantInterfaces.Count();
  79. for ( int k = 0; k < s_arrDormantInterfaces.Count(); ++ k )
  80. {
  81. IDormantOperation *pDormant = s_arrDormantInterfaces[k];
  82. bool bKeepRunning = pDormant->UpdateDormantOperation();
  83. if ( !bKeepRunning )
  84. s_arrDormantInterfaces.Remove( k -- );
  85. }
  86. if ( nDormantInterfaces && !s_arrDormantInterfaces.Count() )
  87. {
  88. DevMsg( 2, "MMX360_UpdateDormantOperations finished all dormant interfaces.\n" );
  89. }
  90. }
  91. int MMX360_GetUserCtrlrIndex( XUID xuid )
  92. {
  93. if ( !xuid )
  94. return -1;
  95. for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
  96. {
  97. int iCtrlr = XBX_GetUserId( k );
  98. XUID xuidLocal = 0;
  99. if ( ERROR_SUCCESS == XUserGetXUID( iCtrlr, &xuidLocal ) )
  100. {
  101. if ( xuidLocal == xuid )
  102. return iCtrlr;
  103. }
  104. XUSER_SIGNIN_INFO xsi;
  105. if ( ERROR_SUCCESS == XUserGetSigninInfo( iCtrlr, XUSER_GET_SIGNIN_INFO_OFFLINE_XUID_ONLY, &xsi ) )
  106. {
  107. if ( xsi.xuid == xuid )
  108. return iCtrlr;
  109. }
  110. if ( ERROR_SUCCESS == XUserGetSigninInfo( iCtrlr, XUSER_GET_SIGNIN_INFO_ONLINE_XUID_ONLY, &xsi ) )
  111. {
  112. if ( xsi.xuid == xuid )
  113. return iCtrlr;
  114. }
  115. }
  116. return -1;
  117. }
  118. void MMX360_Helper_DataToString( void const *pvData, int numBytes, char *pchBuffer )
  119. {
  120. for ( const unsigned char *pb = ( const unsigned char * ) pvData, *pbEnd = pb + numBytes;
  121. pb < pbEnd; ++ pb )
  122. {
  123. *( pchBuffer ++ ) = '0' + ( unsigned char )( pb[0] & 0x0Fu );
  124. *( pchBuffer ++ ) = '0' + ( unsigned char )( ( pb[0] >> 4 ) & 0x0Fu );
  125. }
  126. *pchBuffer = 0;
  127. }
  128. void MMX360_Helper_DataFromString( void *pvData, int numBytes, char const *pchBuffer )
  129. {
  130. if ( !pchBuffer )
  131. goto parse_error;
  132. for ( unsigned char *pb = ( unsigned char * ) pvData, *pbEnd = pb + numBytes;
  133. pb < pbEnd; ++ pb )
  134. {
  135. if ( !pchBuffer[0] || !pchBuffer[1] )
  136. goto parse_error;
  137. pb[0] = ( unsigned char )( ( pchBuffer[0] - '0' ) & 0x0Fu ) |
  138. ( unsigned char )( ( ( pchBuffer[1] - '0' ) & 0x0Fu ) << 4 );
  139. pchBuffer += 2;
  140. }
  141. return;
  142. parse_error:
  143. memset( pvData, 0, numBytes );
  144. }
  145. void MMX360_SessionInfoToString( XSESSION_INFO const &xsi, char *pchBuffer )
  146. {
  147. MMX360_Helper_DataToString( &xsi, sizeof( xsi ), pchBuffer );
  148. }
  149. void MMX360_SessionInfoFromString( XSESSION_INFO &xsi, char const *pchBuffer )
  150. {
  151. MMX360_Helper_DataFromString( &xsi, sizeof( xsi ), pchBuffer );
  152. }
  153. void MMX360_XnaddrToString( XNADDR const &xsi, char *pchBuffer )
  154. {
  155. MMX360_Helper_DataToString( &xsi, sizeof( xsi ), pchBuffer );
  156. }
  157. void MMX360_XnaddrFromString( XNADDR &xsi, char const *pchBuffer )
  158. {
  159. MMX360_Helper_DataFromString( &xsi, sizeof( xsi ), pchBuffer );
  160. }
  161. //
  162. // Active gameplay sessions stack
  163. //
  164. // Only one session can be considered active gameplay session
  165. // and we will consider the last session that received XSessionStart
  166. //
  167. struct CX360ActiveGameplaySession_t
  168. {
  169. CX360ActiveGameplaySession_t( HANDLE hHandle )
  170. {
  171. m_hHandle = hHandle;
  172. m_flLastFlushTime = Plat_FloatTime();
  173. m_bNeedsFlush = false;
  174. }
  175. HANDLE m_hHandle;
  176. float m_flLastFlushTime;
  177. bool m_bNeedsFlush;
  178. };
  179. static CUtlVector< CX360ActiveGameplaySession_t > s_arrActiveGameplaySessions;
  180. CX360ActiveGameplaySession_t * MMX360_GetActiveGameplaySession()
  181. {
  182. return s_arrActiveGameplaySessions.Count() ? &s_arrActiveGameplaySessions.Tail() : NULL;
  183. }
  184. //
  185. // Session stats flush scheduled call
  186. //
  187. struct ScheduledCall_FlushSessionStats : public ScheduledCall_t
  188. {
  189. ScheduledCall_FlushSessionStats()
  190. {
  191. m_flTimeToCall = Plat_FloatTime() + 0.1f;
  192. m_pfnCall = Callback;
  193. }
  194. protected:
  195. void Run()
  196. {
  197. // Walk all the scheduled sessions and XSessionFlushStats them if needed
  198. for ( int k = 0; k < s_arrActiveGameplaySessions.Count(); ++ k )
  199. {
  200. CX360ActiveGameplaySession_t &ags = s_arrActiveGameplaySessions[k];
  201. if ( !ags.m_bNeedsFlush )
  202. continue;
  203. if ( Plat_FloatTime() < ags.m_flLastFlushTime + 6*60 )
  204. // TCR: cannot flush within 5 minutes of last flush (use 6 minutes just in case)
  205. continue;
  206. // Schedule an overlapped flush
  207. ags.m_flLastFlushTime = Plat_FloatTime();
  208. ags.m_bNeedsFlush = false;
  209. DevMsg( "ScheduledCall_FlushSessionStats for session %p (@%.2f)\n", ags.m_hHandle, ags.m_flLastFlushTime );
  210. g_pMatchExtensions->GetIXOnline()->XSessionFlushStats( ags.m_hHandle, MMX360_NewOverlappedDormant() );
  211. }
  212. delete this;
  213. }
  214. static void Callback( ScheduledCall_t *pThis )
  215. {
  216. ( ( ScheduledCall_FlushSessionStats * )( pThis ) )->Run();
  217. }
  218. };
  219. //
  220. // Leaderboard async requests implementation
  221. //
  222. class CLeaderboardX360_Writer : public IX360LeaderboardBatchWriter
  223. {
  224. public:
  225. explicit CLeaderboardX360_Writer( XUID xuidGamer );
  226. public:
  227. virtual void AddProperty( int dwViewId, XUSER_PROPERTY const &xp );
  228. virtual void WriteBatchAndDestroy();
  229. protected:
  230. bool WriteBatch();
  231. void Destroy();
  232. static void OnXSessionWriteStatsCompleted( XOVERLAPPED *, void *pvThis )
  233. {
  234. ( ( CLeaderboardX360_Writer * )pvThis )->Destroy();
  235. }
  236. public:
  237. CUtlVector< XSESSION_VIEW_PROPERTIES > m_arrLbViews;
  238. CUtlVectorFixed< XUSER_PROPERTY, 64 > m_arrLbProperties;
  239. HANDLE m_hSession;
  240. XUID m_xuidGamer;
  241. };
  242. IX360LeaderboardBatchWriter * MMX360_CreateLeaderboardBatchWriter( XUID xuidGamer )
  243. {
  244. if ( !MMX360_GetActiveGameplaySession() )
  245. {
  246. Warning( "MMX360_CreateLeaderboardBatchWriter called with no active gameplay session!\n" );
  247. Assert( 0 );
  248. return NULL;
  249. }
  250. return new CLeaderboardX360_Writer( xuidGamer );
  251. }
  252. CLeaderboardX360_Writer::CLeaderboardX360_Writer( XUID xuidGamer )
  253. {
  254. CX360ActiveGameplaySession_t *ags = MMX360_GetActiveGameplaySession();
  255. m_hSession = ags ? ags->m_hHandle : NULL;
  256. m_xuidGamer = xuidGamer;
  257. }
  258. void CLeaderboardX360_Writer::AddProperty( int dwViewId, XUSER_PROPERTY const &xp )
  259. {
  260. if ( m_arrLbProperties.Count() >= 63 )
  261. {
  262. Warning( "CLeaderboardX360_Writer: exceeded number of properties for update!\n" );
  263. return;
  264. }
  265. m_arrLbProperties.AddMultipleToTail( 1, &xp );
  266. XUSER_PROPERTY *xProp = &m_arrLbProperties.Tail();
  267. if ( !m_arrLbViews.Count() || m_arrLbViews.Tail().dwViewId != ( DWORD ) dwViewId )
  268. {
  269. m_arrLbViews.AddMultipleToTail( 1 );
  270. XSESSION_VIEW_PROPERTIES *xView = &m_arrLbViews.Tail();
  271. xView->dwViewId = dwViewId;
  272. xView->dwNumProperties = 0;
  273. xView->pProperties = xProp;
  274. }
  275. ++ m_arrLbViews.Tail().dwNumProperties;
  276. }
  277. bool CLeaderboardX360_Writer::WriteBatch()
  278. {
  279. if ( !m_arrLbViews.Count() )
  280. return false;
  281. CX360ActiveGameplaySession_t &ags = *MMX360_GetActiveGameplaySession();
  282. if ( !&ags || !ags.m_hHandle )
  283. {
  284. Warning( "CLeaderboardX360_Writer::WriteBatch called without active gameplay session!\n" );
  285. Assert( 0 );
  286. return false;
  287. }
  288. if ( ags.m_hHandle != m_hSession )
  289. {
  290. Warning( "CLeaderboardX360_Writer::WriteBatch called when active gameplay session changed!\n" );
  291. Assert( 0 );
  292. return false;
  293. }
  294. DWORD ret = g_pMatchExtensions->GetIXOnline()->XSessionWriteStats( ags.m_hHandle,
  295. m_xuidGamer, m_arrLbViews.Count(), m_arrLbViews.Base(),
  296. MMX360_NewOverlappedDormant( OnXSessionWriteStatsCompleted, this ) );
  297. if ( ret != ERROR_SUCCESS && ret != ERROR_IO_PENDING )
  298. {
  299. Warning( "XSessionWriteStats failed, code %d, xuid=%llx, numViews=%d, numProps=%d\n",
  300. ret, m_xuidGamer, m_arrLbViews.Count(), m_arrLbProperties.Count() );
  301. return false;
  302. }
  303. // Mark the session as needing a flush when the TCR allows us to flush session stats
  304. ags.m_bNeedsFlush = true;
  305. DevMsg( "XSessionWriteStats for session %p (%.2f, last flush @%.2f)\n", ags.m_hHandle, Plat_FloatTime(), ags.m_flLastFlushTime );
  306. return true;
  307. }
  308. void CLeaderboardX360_Writer::Destroy()
  309. {
  310. m_hSession = NULL;
  311. delete this;
  312. // Submit a scheduled call to flush session stats for all previously written stats that might need a flush
  313. DevMsg( "ScheduledCall_FlushSessionStats @%.2f\n", Plat_FloatTime() );
  314. s_arrScheduledCalls.AddToTail( new ScheduledCall_FlushSessionStats );
  315. }
  316. void CLeaderboardX360_Writer::WriteBatchAndDestroy()
  317. {
  318. if ( !WriteBatch() )
  319. Destroy();
  320. }
  321. //
  322. // Basic async operation on a lobby
  323. //
  324. class CX360LobbyAsyncOperation : public IX360LobbyAsyncOperation, public IDormantOperation
  325. {
  326. public:
  327. CX360LobbyAsyncOperation() : m_eState( AOS_RUNNING ), m_result( 0 ), m_pCancelOverlappedJob( NULL )
  328. {
  329. memset( &m_xOverlapped, 0, sizeof( m_xOverlapped ) );
  330. }
  331. virtual ~CX360LobbyAsyncOperation()
  332. {
  333. ;
  334. }
  335. //
  336. // IMatchAsyncOperation
  337. //
  338. public:
  339. // Poll if operation has completed
  340. virtual bool IsFinished() { return m_eState >= AOS_ABORTED; }
  341. // Operation state
  342. virtual AsyncOperationState_t GetState() { return m_eState; }
  343. // Retrieve a generic completion result for simple operations
  344. // that return simple results upon success,
  345. // results are operation-specific, may result in undefined behavior
  346. // if operation is still in progress.
  347. virtual uint64 GetResult() { return m_result; }
  348. // Request operation to be aborted
  349. virtual void Abort();
  350. // Release the operation interface and all resources
  351. // associated with the operation. Operation callbacks
  352. // will not be called after Release. Operation object
  353. // cannot be accessed after Release.
  354. virtual void Release();
  355. //
  356. // Overrides
  357. //
  358. public:
  359. virtual CX360LobbyObject const & GetLobby() { return m_lobby; }
  360. virtual void Update();
  361. virtual void OnFinished( DWORD dwRetCode, DWORD dwResult );
  362. virtual bool UpdateDormantOperation();
  363. public:
  364. AsyncOperationState_t m_eState;
  365. XOVERLAPPED m_xOverlapped;
  366. uint64 m_result;
  367. CX360LobbyObject m_lobby;
  368. CJob *m_pCancelOverlappedJob;
  369. };
  370. void CX360LobbyAsyncOperation::Abort()
  371. {
  372. Update();
  373. if ( m_eState != AOS_RUNNING )
  374. return;
  375. m_eState = AOS_ABORTING;
  376. m_pCancelOverlappedJob = ThreadExecute( MMX360_CancelOverlapped, &m_xOverlapped );
  377. m_eState = AOS_ABORTED;
  378. }
  379. void CX360LobbyAsyncOperation::Release()
  380. {
  381. if ( !IsFinished() )
  382. {
  383. Abort();
  384. }
  385. if ( m_pCancelOverlappedJob )
  386. MMX360_RegisterDormant( this );
  387. else
  388. delete this;
  389. }
  390. void CX360LobbyAsyncOperation::Update()
  391. {
  392. if ( IsFinished() )
  393. return;
  394. // Check if the operation has completed
  395. if ( !XHasOverlappedIoCompleted( &m_xOverlapped ) )
  396. return;
  397. DWORD dwResult;
  398. DWORD dwRetCode = XGetOverlappedResult( &m_xOverlapped, &dwResult, FALSE );
  399. m_eState = ( dwRetCode == ERROR_SUCCESS ) ? AOS_SUCCEEDED : AOS_FAILED;
  400. OnFinished( dwRetCode, dwResult );
  401. }
  402. bool CX360LobbyAsyncOperation::UpdateDormantOperation()
  403. {
  404. if ( !m_pCancelOverlappedJob->IsFinished() )
  405. return true; // keep running dormant
  406. m_pCancelOverlappedJob->Release();
  407. m_pCancelOverlappedJob = NULL;
  408. delete this;
  409. return false; // destroyed object, remove from dormant list
  410. }
  411. void CX360LobbyAsyncOperation::OnFinished( DWORD dwRetCode, DWORD dwResult )
  412. {
  413. m_result = dwResult;
  414. }
  415. class CX360LobbyQosOperation : public CX360LobbyAsyncOperation
  416. {
  417. public:
  418. CX360LobbyQosOperation() : m_pXnQos( NULL ) {}
  419. public:
  420. virtual void Abort();
  421. virtual void Update();
  422. public:
  423. XNQOS *m_pXnQos;
  424. };
  425. void CX360LobbyQosOperation::Abort()
  426. {
  427. Update();
  428. if ( m_eState != AOS_RUNNING )
  429. return;
  430. g_pMatchExtensions->GetIXOnline()->XNetQosRelease( m_pXnQos );
  431. m_pXnQos = NULL;
  432. m_eState = AOS_ABORTED;
  433. }
  434. void CX360LobbyQosOperation::Update()
  435. {
  436. if ( IsFinished() )
  437. return;
  438. // Check if the QOS is not running
  439. if ( !m_pXnQos )
  440. {
  441. m_eState = AOS_FAILED;
  442. OnFinished( ERROR_SUCCESS, ERROR_SUCCESS );
  443. return;
  444. }
  445. // Check if still pending
  446. if ( m_pXnQos->cxnqosPending > 0 )
  447. return;
  448. bool bSuccess = true;
  449. BYTE uNeedFlags = XNET_XNQOSINFO_TARGET_CONTACTED | XNET_XNQOSINFO_DATA_RECEIVED;
  450. for ( uint k = 0; k < m_pXnQos->cxnqos; ++ k )
  451. {
  452. XNQOSINFO &xqi = m_pXnQos->axnqosinfo[k];
  453. if ( ( ( xqi.bFlags & uNeedFlags ) != uNeedFlags) ||
  454. ( xqi.bFlags & XNET_XNQOSINFO_TARGET_DISABLED ) ||
  455. !xqi.cbData || !xqi.pbData )
  456. {
  457. bSuccess = false;
  458. break;
  459. }
  460. }
  461. m_eState = ( bSuccess ) ? AOS_SUCCEEDED : AOS_FAILED;
  462. DWORD dwCode = ( bSuccess ) ? ERROR_SUCCESS : ERROR_FILE_NOT_FOUND;
  463. OnFinished( dwCode, dwCode );
  464. // Release the QOS
  465. g_pMatchExtensions->GetIXOnline()->XNetQosRelease( m_pXnQos );
  466. m_pXnQos = NULL;
  467. }
  468. template < typename TBase = CX360LobbyAsyncOperation >
  469. class CX360AsyncOperationDelegating : public TBase
  470. {
  471. public:
  472. typedef CX360AsyncOperationDelegating<TBase> TX360AsyncOperationDelegating;
  473. typedef TBase TX360AsyncOperationDelegatingBase;
  474. public:
  475. CX360AsyncOperationDelegating() : m_pDelegate( NULL )
  476. {
  477. }
  478. //
  479. // IMatchAsyncOperation
  480. //
  481. public:
  482. // Poll if operation has completed
  483. virtual bool IsFinished() { return m_pDelegate ? m_pDelegate->IsFinished() : TBase::IsFinished(); }
  484. // Operation state
  485. virtual AsyncOperationState_t GetState() { return m_pDelegate ? m_pDelegate->GetState() : TBase::GetState(); }
  486. // Retrieve a generic completion result for simple operations
  487. // that return simple results upon success,
  488. // results are operation-specific, may result in undefined behavior
  489. // if operation is still in progress.
  490. virtual uint64 GetResult() { return m_pDelegate ? m_pDelegate->GetResult() : TBase::GetResult(); }
  491. // Request operation to be aborted
  492. virtual void Abort()
  493. {
  494. if ( m_pDelegate )
  495. m_pDelegate->Abort();
  496. else
  497. TBase::Abort();
  498. }
  499. // Release the operation interface and all resources
  500. // associated with the operation. Operation callbacks
  501. // will not be called after Release. Operation object
  502. // cannot be accessed after Release.
  503. virtual void Release()
  504. {
  505. if ( m_pDelegate )
  506. m_pDelegate->Release();
  507. m_pDelegate = NULL;
  508. TBase::Release();
  509. }
  510. //
  511. // Overrides
  512. //
  513. public:
  514. virtual CX360LobbyObject const & GetLobby()
  515. {
  516. if ( m_pDelegate )
  517. return m_pDelegate->GetLobby();
  518. else
  519. return TBase::GetLobby();
  520. }
  521. virtual void Update()
  522. {
  523. if ( m_pDelegate )
  524. m_pDelegate->Update();
  525. else
  526. TBase::Update();
  527. }
  528. public:
  529. IX360LobbyAsyncOperation *m_pDelegate;
  530. };
  531. //
  532. // Deleting a lobby
  533. //
  534. class CX360LobbyAsyncOperation_LobbyDelete : public CX360LobbyAsyncOperation
  535. {
  536. public:
  537. explicit CX360LobbyAsyncOperation_LobbyDelete( CX360LobbyObject &lobby )
  538. {
  539. m_lobby = lobby;
  540. // Submit XSessionDelete
  541. DWORD ret = g_pMatchExtensions->GetIXOnline()->XSessionDelete( m_lobby.m_hHandle, &m_xOverlapped );
  542. if ( ret != ERROR_IO_PENDING )
  543. {
  544. DevWarning( "XSessionDelete failed (code 0x%08X)\n", ret );
  545. Assert( 0 );
  546. m_eState = AOS_FAILED;
  547. m_result = ret;
  548. }
  549. }
  550. virtual void Abort()
  551. {
  552. DevWarning( "XSessionDelete cannot be aborted!\n" );
  553. Assert( 0 );
  554. }
  555. virtual void OnFinished( DWORD dwRetCode, DWORD dwResult )
  556. {
  557. CX360LobbyAsyncOperation::OnFinished( dwRetCode, dwResult );
  558. CloseHandle( m_lobby.m_hHandle );
  559. }
  560. };
  561. void MMX360_LobbyDelete( CX360LobbyObject &lobby, IX360LobbyAsyncOperation **ppOperation )
  562. {
  563. Assert( lobby.m_hHandle );
  564. Assert( ppOperation );
  565. if ( ppOperation )
  566. *ppOperation = NULL;
  567. else
  568. return;
  569. // Release QOS listener
  570. g_pMatchExtensions->GetIXOnline()->XNetQosListen( &lobby.m_xiInfo.sessionID, 0, 0, 0, XNET_QOS_LISTEN_RELEASE );
  571. if ( lobby.m_bXSessionStarted )
  572. {
  573. DevWarning( "LobbyDelete called on an active gameplay session, forcing XSessionEnd!\n" );
  574. // TODO: investigate whether this is not resulting in any side-effects in arbitrated ranked games
  575. // especially when the host leaves the game.
  576. MMX360_LobbySetActiveGameplayState( lobby, false, NULL );
  577. }
  578. *ppOperation = new CX360LobbyAsyncOperation_LobbyDelete( lobby );
  579. lobby = CX360LobbyObject();
  580. }
  581. //
  582. // Describing lobby flags
  583. //
  584. CX360LobbyFlags_t MMX360_DescribeLobbyFlags( KeyValues *pSettings, bool bHost, bool bWantLocked )
  585. {
  586. CX360LobbyFlags_t fl = {0};
  587. char const *szLock = pSettings->GetString( "system/lock", NULL );
  588. char const *szNetwork = pSettings->GetString( "system/network", "LIVE" );
  589. char const *szAccess = pSettings->GetString( "system/access", "public" );
  590. char const *szNetFlag = pSettings->GetString( "system/netflag", NULL );
  591. int numSlots = pSettings->GetInt( "members/numSlots", XBX_GetNumGameUsers() );
  592. // Gametype
  593. fl.m_dwGameType = X_CONTEXT_GAME_TYPE_STANDARD;
  594. // Flags
  595. if ( !Q_stricmp( szNetwork, "LIVE" ) )
  596. {
  597. fl.m_dwFlags = XSESSION_CREATE_LIVE_MULTIPLAYER_STANDARD;
  598. if ( szNetFlag )
  599. {
  600. if ( !Q_stricmp( szNetFlag, "teamlobby" ) )
  601. {
  602. fl.m_dwFlags = XSESSION_CREATE_USES_PRESENCE | XSESSION_CREATE_USES_PEER_NETWORK;
  603. }
  604. else if ( !Q_stricmp( szNetFlag, "teamlink" ) )
  605. {
  606. fl.m_dwFlags = /*XSESSION_CREATE_USES_STATS |*/ XSESSION_CREATE_USES_MATCHMAKING | XSESSION_CREATE_USES_PEER_NETWORK;
  607. }
  608. }
  609. }
  610. else if ( !Q_stricmp( szNetwork, "lan" ) )
  611. {
  612. fl.m_dwFlags = XSESSION_CREATE_SYSTEMLINK;
  613. }
  614. else
  615. {
  616. fl.m_dwFlags = XSESSION_CREATE_SINGLEPLAYER_WITH_STATS;
  617. }
  618. // Whether session can be disabled in XUI
  619. fl.m_bCanLockJoins = ( ( fl.m_dwFlags & XSESSION_CREATE_USES_PRESENCE ) == XSESSION_CREATE_USES_PRESENCE );
  620. // Hosting
  621. if ( bHost )
  622. {
  623. fl.m_dwFlags |= XSESSION_CREATE_HOST;
  624. }
  625. bool bPublic = true;
  626. if ( fl.m_bCanLockJoins )
  627. {
  628. if ( bWantLocked || ( szLock && *szLock && !IsX360() ) )
  629. {
  630. bPublic = false;
  631. fl.m_dwFlags |= ( XSESSION_CREATE_INVITES_DISABLED | XSESSION_CREATE_JOIN_VIA_PRESENCE_DISABLED | XSESSION_CREATE_JOIN_IN_PROGRESS_DISABLED );
  632. }
  633. else if ( !Q_stricmp( "private", szAccess ) )
  634. {
  635. bPublic = false;
  636. fl.m_dwFlags |= XSESSION_CREATE_JOIN_VIA_PRESENCE_DISABLED;
  637. }
  638. else if ( !Q_stricmp( "friends", szAccess ) )
  639. {
  640. fl.m_dwFlags |= XSESSION_CREATE_JOIN_VIA_PRESENCE_FRIENDS_ONLY;
  641. }
  642. }
  643. fl.m_numPublicSlots = bPublic ? numSlots : 0;
  644. fl.m_numPrivateSlots = bPublic ? 0 : numSlots;
  645. return fl;
  646. }
  647. //
  648. // Lobby creation
  649. //
  650. class CX360LobbyAsyncOperation_LobbyCreate : public CX360LobbyAsyncOperation
  651. {
  652. public:
  653. explicit CX360LobbyAsyncOperation_LobbyCreate( CX360LobbyFlags_t const &fl )
  654. {
  655. // Set the required contexts
  656. for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
  657. {
  658. int iCtrlr = XBX_GetUserId( k );
  659. XUserSetContext( iCtrlr, X_CONTEXT_GAME_TYPE, fl.m_dwGameType );
  660. }
  661. // Submit XSessionCreate
  662. DWORD ret = g_pMatchExtensions->GetIXOnline()->XSessionCreate( fl.m_dwFlags, XBX_GetPrimaryUserId(), fl.m_numPublicSlots, fl.m_numPrivateSlots,
  663. &m_lobby.m_uiNonce, &m_lobby.m_xiInfo, &m_xOverlapped, &m_lobby.m_hHandle );
  664. if ( ret != ERROR_IO_PENDING )
  665. {
  666. DevWarning( "XSessionCreate failed (code 0x%08X)\n", ret );
  667. Assert( 0 );
  668. m_eState = AOS_FAILED;
  669. m_result = ret;
  670. }
  671. }
  672. virtual void OnFinished( DWORD dwRetCode, DWORD dwResult )
  673. {
  674. CX360LobbyAsyncOperation::OnFinished( dwRetCode, dwResult );
  675. if ( GetState() == AOS_SUCCEEDED )
  676. {
  677. // Enable QOS listener when session creation succeeds
  678. g_pMatchExtensions->GetIXOnline()->XNetQosListen( &m_lobby.m_xiInfo.sessionID, 0, 0, 0, XNET_QOS_LISTEN_ENABLE );
  679. }
  680. }
  681. };
  682. void MMX360_LobbyCreate( KeyValues *pSettings, IX360LobbyAsyncOperation **ppOperation )
  683. {
  684. Assert( pSettings );
  685. Assert( ppOperation );
  686. if ( ppOperation )
  687. *ppOperation = NULL;
  688. else
  689. return;
  690. if ( !pSettings )
  691. return;
  692. // Determine parameters
  693. CX360LobbyFlags_t fl = MMX360_DescribeLobbyFlags( pSettings, true, true );
  694. if ( KeyValues *pKv = g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareForSessionCreate( pSettings ) )
  695. {
  696. pKv->deleteThis();
  697. }
  698. // Allocate our lobby object
  699. *ppOperation = new CX360LobbyAsyncOperation_LobbyCreate( fl );
  700. }
  701. //
  702. // Lobby connect operation
  703. //
  704. class CX360LobbyAsyncOperation_LobbyConnect : public CX360LobbyAsyncOperation
  705. {
  706. public:
  707. explicit CX360LobbyAsyncOperation_LobbyConnect( KeyValues *pHostSettings, XSESSION_INFO const &xsi )
  708. {
  709. m_lobby.m_xiInfo = xsi;
  710. // Prepare for lobby create
  711. CX360LobbyFlags_t fl = MMX360_DescribeLobbyFlags( pHostSettings, false, true );
  712. if ( KeyValues *pKv = g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareForSessionCreate( pHostSettings ) )
  713. {
  714. pKv->deleteThis();
  715. }
  716. // Set the required contexts
  717. for ( int k = 0; k < ( int ) XBX_GetNumGameUsers(); ++ k )
  718. {
  719. int iCtrlr = XBX_GetUserId( k );
  720. XUserSetContext( iCtrlr, X_CONTEXT_GAME_TYPE, fl.m_dwGameType );
  721. }
  722. // Submit XSessionCreate
  723. DWORD ret = g_pMatchExtensions->GetIXOnline()->XSessionCreate( fl.m_dwFlags, XBX_GetPrimaryUserId(), fl.m_numPublicSlots, fl.m_numPrivateSlots,
  724. &m_lobby.m_uiNonce, &m_lobby.m_xiInfo, &m_xOverlapped, &m_lobby.m_hHandle );
  725. if ( ret != ERROR_IO_PENDING )
  726. {
  727. DevWarning( "XSessionCreate failed (code 0x%08X)\n", ret );
  728. Assert( 0 );
  729. m_eState = AOS_FAILED;
  730. m_result = ret;
  731. }
  732. }
  733. };
  734. class CX360LobbyAsyncOperation_LobbyQosAndConnect : public CX360AsyncOperationDelegating< CX360LobbyQosOperation >
  735. {
  736. public:
  737. explicit CX360LobbyAsyncOperation_LobbyQosAndConnect( XSESSION_INFO const &xsi ) :
  738. m_xsiQosInfo( xsi )
  739. {
  740. // Perform the QOS lookup
  741. m_pXnaddr = &m_xsiQosInfo.hostAddress;
  742. m_pXnkid = &m_xsiQosInfo.sessionID;
  743. m_pXnkey = &m_xsiQosInfo.keyExchangeKey;
  744. INT ret = g_pMatchExtensions->GetIXOnline()->XNetQosLookup( 1, &m_pXnaddr, &m_pXnkid, &m_pXnkey,
  745. 0, NULL, NULL, 2, 0, 0, NULL,
  746. &m_pXnQos );
  747. if ( 0 != ret )
  748. {
  749. DevWarning( "XNetQosLookup failed (code 0x%08X)\n", ret );
  750. Assert( 0 );
  751. m_eState = AOS_FAILED;
  752. m_result = ERROR_NO_SUCH_PRIVILEGE;
  753. return;
  754. }
  755. }
  756. public:
  757. virtual void OnFinished( DWORD dwRetCode, DWORD dwResult )
  758. {
  759. TX360AsyncOperationDelegating::OnFinished( dwRetCode, dwResult );
  760. if ( m_eState == AOS_SUCCEEDED && !m_pDelegate )
  761. {
  762. // We successfully contacted the QOS host and obtained the data
  763. XNQOSINFO &xqi = m_pXnQos->axnqosinfo[0];
  764. MM_GameDetails_QOS_t gd = { xqi.pbData, xqi.cbData, xqi.wRttMedInMsecs };
  765. KeyValues *pHostGameDetails = g_pMatchFramework->GetMatchNetworkMsgController()->UnpackGameDetailsFromQOS( &gd );
  766. KeyValues::AutoDelete autodelete_pHostGameDetails( pHostGameDetails );
  767. // Output unpacked game details from host
  768. KeyValuesDumpAsDevMsg( pHostGameDetails, 1, 2 );
  769. m_pDelegate = new CX360LobbyAsyncOperation_LobbyConnect( pHostGameDetails, m_xsiQosInfo );
  770. return;
  771. }
  772. Assert( !m_pDelegate );
  773. m_eState = AOS_FAILED;
  774. }
  775. protected:
  776. XSESSION_INFO m_xsiQosInfo;
  777. XNADDR const *m_pXnaddr;
  778. XNKID const *m_pXnkid;
  779. XNKEY const *m_pXnkey;
  780. };
  781. class CX360LobbyAsyncOperation_LobbyDiscoverAndQosAndConnect : public CX360AsyncOperationDelegating< CX360LobbyAsyncOperation >
  782. {
  783. public:
  784. explicit CX360LobbyAsyncOperation_LobbyDiscoverAndQosAndConnect( uint64 const &uiSessionId )
  785. {
  786. XNKID xnkid = ( XNKID const & ) uiSessionId;
  787. int iCtrlr = XBX_GetPrimaryUserId();
  788. DWORD dwSize = 0;
  789. DWORD ret = g_pMatchExtensions->GetIXOnline()->XSessionSearchByID( xnkid, iCtrlr, &dwSize, NULL, NULL );
  790. if( ret != ERROR_INSUFFICIENT_BUFFER )
  791. {
  792. DevWarning( "XSessionSearchByID failed to determine size (code 0x%08X)\n", ret );
  793. Assert( 0 );
  794. m_eState = AOS_FAILED;
  795. m_result = ret;
  796. return;
  797. }
  798. // Allocate buffer
  799. m_bufSearchResultHeader.EnsureCapacity( dwSize );
  800. ZeroMemory( m_bufSearchResultHeader.Base(), dwSize );
  801. ret = g_pMatchExtensions->GetIXOnline()->XSessionSearchByID( xnkid, iCtrlr, &dwSize,
  802. (XSESSION_SEARCHRESULT_HEADER *) m_bufSearchResultHeader.Base(),
  803. &m_xOverlapped );
  804. if( ret != ERROR_IO_PENDING )
  805. {
  806. DevWarning( "XSessionSearchByID failed to initiate search (code 0x%08X)\n", ret );
  807. Assert( 0 );
  808. m_eState = AOS_FAILED;
  809. m_result = ret;
  810. return;
  811. }
  812. }
  813. public:
  814. virtual void OnFinished( DWORD dwRetCode, DWORD dwResult )
  815. {
  816. TX360AsyncOperationDelegating::OnFinished( dwRetCode, dwResult );
  817. if ( m_eState == AOS_SUCCEEDED && !m_pDelegate )
  818. {
  819. // We successfully resolved our XNKID
  820. XSESSION_SEARCHRESULT_HEADER *xsh = ( XSESSION_SEARCHRESULT_HEADER * ) m_bufSearchResultHeader.Base();
  821. if( xsh->dwSearchResults >= 1)
  822. {
  823. m_lobby.m_xiInfo = xsh->pResults[0].info;
  824. m_pDelegate = new CX360LobbyAsyncOperation_LobbyQosAndConnect( m_lobby.m_xiInfo );
  825. return;
  826. }
  827. }
  828. Assert( !m_pDelegate );
  829. m_eState = AOS_FAILED;
  830. }
  831. protected:
  832. CUtlBuffer m_bufSearchResultHeader;
  833. };
  834. void MMX360_LobbyConnect( KeyValues *pSettings, IX360LobbyAsyncOperation **ppOperation )
  835. {
  836. Assert( pSettings );
  837. Assert( ppOperation );
  838. if ( ppOperation )
  839. *ppOperation = NULL;
  840. else
  841. return;
  842. if ( !pSettings )
  843. return;
  844. // Parse the information from the settings
  845. uint64 uiSessionID = pSettings->GetUint64( "options/sessionid", 0ull );
  846. char const *szSessionInfo = pSettings->GetString( "options/sessioninfo", NULL );
  847. KeyValues *pSessionHostData = ( KeyValues * ) pSettings->GetPtr( "options/sessionHostData", NULL );
  848. // Handle the case when we have session info and it is up to date
  849. XSESSION_INFO xsi = {0};
  850. if ( szSessionInfo )
  851. MMX360_SessionInfoFromString( xsi, szSessionInfo );
  852. if ( ( const uint64 & ) xsi.sessionID != ( const uint64 & ) uiSessionID )
  853. {
  854. // Need to discover session info first
  855. *ppOperation = new CX360LobbyAsyncOperation_LobbyDiscoverAndQosAndConnect( uiSessionID );
  856. return;
  857. }
  858. if ( !pSessionHostData && !Q_stricmp( "LIVE", pSettings->GetString( "system/network" ) ) )
  859. {
  860. // QOS and connect using the host-supplied information
  861. *ppOperation = new CX360LobbyAsyncOperation_LobbyQosAndConnect( xsi );
  862. return;
  863. }
  864. // Otherwise use the client-settings hoping that they match the host-settings
  865. if ( !pSessionHostData )
  866. pSessionHostData = pSettings;
  867. // Connect immediately with session information that we have
  868. *ppOperation = new CX360LobbyAsyncOperation_LobbyConnect( pSessionHostData, xsi );
  869. }
  870. //
  871. // Host migration implementation
  872. //
  873. struct CX360LobbyMigrateHandleImpl
  874. {
  875. CX360LobbyMigrateOperation_t *m_pListener;
  876. XSESSION_INFO m_xsi;
  877. static void Finished( XOVERLAPPED *pxOverlapped, void *pvThis )
  878. {
  879. CX360LobbyMigrateHandleImpl *pHandle = ( CX360LobbyMigrateHandleImpl * ) pvThis;
  880. if ( pHandle->m_pListener )
  881. {
  882. XGetOverlappedResult( pxOverlapped, &pHandle->m_pListener->m_ret, FALSE );
  883. pHandle->m_pListener->m_pLobby->m_xiInfo = pHandle->m_xsi;
  884. pHandle->m_pListener->m_bFinished = true;
  885. }
  886. delete pHandle;
  887. }
  888. };
  889. CX360LobbyMigrateHandle_t MMX360_LobbyMigrateHost( CX360LobbyObject &lobby, CX360LobbyMigrateOperation_t *pOperation )
  890. {
  891. if ( !XNetXnKidIsSystemLink( &lobby.m_xiInfo.sessionID ) )
  892. {
  893. CX360LobbyMigrateHandleImpl *pHandleImpl = new CX360LobbyMigrateHandleImpl;
  894. pHandleImpl->m_pListener = pOperation;
  895. pHandleImpl->m_xsi = lobby.m_xiInfo;
  896. if ( pOperation )
  897. {
  898. pOperation->m_bFinished = false;
  899. pOperation->m_ret = ERROR_IO_PENDING;
  900. pOperation->m_pLobby = &lobby;
  901. }
  902. // Submit XSessionMigrateHost
  903. DWORD ret = g_pMatchExtensions->GetIXOnline()->XSessionMigrateHost( lobby.m_hHandle, XBX_GetPrimaryUserId(),
  904. &pHandleImpl->m_xsi,
  905. MMX360_NewOverlappedDormant( CX360LobbyMigrateHandleImpl::Finished, pHandleImpl ) );
  906. if ( ret != ERROR_SUCCESS && ret != ERROR_IO_PENDING )
  907. {
  908. DevWarning( "XSessionMigrateHost(hosting) failed (code 0x%08X)\n", ret );
  909. Assert( 0 );
  910. }
  911. return pHandleImpl;
  912. }
  913. else
  914. {
  915. DevWarning( "XSessionMigrateHost(hosting) unavailable because the session is SystemLink\n" );
  916. return NULL;
  917. }
  918. }
  919. CX360LobbyMigrateHandle_t MMX360_LobbyMigrateClient( CX360LobbyObject &lobby, XSESSION_INFO const &xsiNewHost, CX360LobbyMigrateOperation_t *pOperation )
  920. {
  921. if ( !XNetXnKidIsSystemLink( &lobby.m_xiInfo.sessionID ) )
  922. {
  923. CX360LobbyMigrateHandleImpl *pHandleImpl = new CX360LobbyMigrateHandleImpl;
  924. pHandleImpl->m_pListener = pOperation;
  925. pHandleImpl->m_xsi = xsiNewHost;
  926. if ( pOperation )
  927. {
  928. pOperation->m_bFinished = false;
  929. pOperation->m_ret = ERROR_IO_PENDING;
  930. pOperation->m_pLobby = &lobby;
  931. }
  932. // Submit XSessionMigrateHost
  933. DWORD ret = g_pMatchExtensions->GetIXOnline()->XSessionMigrateHost( lobby.m_hHandle, XUSER_INDEX_NONE,
  934. &pHandleImpl->m_xsi,
  935. MMX360_NewOverlappedDormant( CX360LobbyMigrateHandleImpl::Finished, pHandleImpl ) );
  936. if ( ret != ERROR_SUCCESS && ret != ERROR_IO_PENDING )
  937. {
  938. DevWarning( "XSessionMigrateHost(client) failed (code 0x%08X)\n", ret );
  939. Assert( 0 );
  940. }
  941. return pHandleImpl;
  942. }
  943. else
  944. {
  945. DevWarning( "XSessionMigrateHost(client) unavailable because the session is SystemLink\n" );
  946. return NULL;
  947. }
  948. }
  949. CX360LobbyMigrateOperation_t * MMX360_LobbyMigrateSetListener( CX360LobbyMigrateHandle_t hMigrateCall, CX360LobbyMigrateOperation_t *pOperation )
  950. {
  951. CX360LobbyMigrateHandleImpl *pHandle = ( CX360LobbyMigrateHandleImpl * ) hMigrateCall;
  952. CX360LobbyMigrateOperation_t *pOldListener = NULL;
  953. if ( pHandle )
  954. {
  955. pOldListener = pHandle->m_pListener;
  956. if ( pOperation && pHandle->m_pListener )
  957. *pOperation = *pHandle->m_pListener;
  958. pHandle->m_pListener = pOperation;
  959. }
  960. return pOldListener;
  961. }
  962. //
  963. // Join and Leave implementation
  964. //
  965. static DWORD MMX360_Helper_LobbyJoin( CX360LobbyObject &lobby, XUID xuid )
  966. {
  967. static const BOOL bPrivate = TRUE;
  968. int iCtrlr = MMX360_GetUserCtrlrIndex( xuid );
  969. DevMsg( "Session %llx: ADDED %llx%s\n", lobby.m_uiNonce, xuid, ( iCtrlr >= 0 ) ? " local" : "" );
  970. if ( iCtrlr >= 0 )
  971. {
  972. DWORD *pdwUserIndex = new DWORD( iCtrlr );
  973. return g_pMatchExtensions->GetIXOnline()->XSessionJoinLocal( lobby.m_hHandle,
  974. 1, pdwUserIndex, &bPrivate,
  975. MMX360_NewOverlappedDormant( OnCompleted_DeleteData< DWORD >, pdwUserIndex ) );
  976. }
  977. else
  978. {
  979. XUID *pXuids = new XUID( xuid );
  980. return g_pMatchExtensions->GetIXOnline()->XSessionJoinRemote( lobby.m_hHandle,
  981. 1, pXuids, &bPrivate,
  982. MMX360_NewOverlappedDormant( OnCompleted_DeleteData< XUID >, pXuids ) );
  983. }
  984. }
  985. static DWORD MMX360_Helper_LobbyLeave( CX360LobbyObject &lobby, XUID xuid )
  986. {
  987. int iCtrlr = MMX360_GetUserCtrlrIndex( xuid );
  988. DevMsg( "Session %llx: LEAVE %llx%s\n", lobby.m_uiNonce, xuid, ( iCtrlr >= 0 ) ? " local" : "" );
  989. if ( iCtrlr >= 0 )
  990. {
  991. DWORD *pdwUserIndex = new DWORD( iCtrlr );
  992. return g_pMatchExtensions->GetIXOnline()->XSessionLeaveLocal( lobby.m_hHandle,
  993. 1, pdwUserIndex,
  994. MMX360_NewOverlappedDormant( OnCompleted_DeleteData< DWORD >, pdwUserIndex ) );
  995. }
  996. else
  997. {
  998. XUID *pXuids = new XUID( xuid );
  999. return g_pMatchExtensions->GetIXOnline()->XSessionLeaveRemote( lobby.m_hHandle,
  1000. 1, pXuids,
  1001. MMX360_NewOverlappedDormant( OnCompleted_DeleteData< XUID >, pXuids ) );
  1002. }
  1003. }
  1004. static void MMX360_Helper_LobbyJoinLeaveMembers( KeyValues *pSettings, CX360LobbyObject &lobby,
  1005. DWORD (*pfn)( CX360LobbyObject&, XUID ),
  1006. int idxMachineStart, int idxMachineEnd )
  1007. {
  1008. if ( !pSettings )
  1009. return;
  1010. KeyValues *kvMembers = pSettings->FindKey( "members" );
  1011. if ( !kvMembers )
  1012. return;
  1013. int numMachines = kvMembers->GetInt( "numMachines", 0 );
  1014. if ( idxMachineEnd < 0 || idxMachineEnd >= numMachines )
  1015. idxMachineEnd = numMachines - 1;
  1016. for ( int k = idxMachineStart; k <= idxMachineEnd; ++ k )
  1017. {
  1018. KeyValues *kvMachine = kvMembers->FindKey( CFmtStr( "machine%d", k ) );
  1019. if ( !kvMachine )
  1020. continue;
  1021. int numPlayers = kvMachine->GetInt( "numPlayers", 0 );
  1022. for ( int j = 0; j < numPlayers; ++ j )
  1023. {
  1024. KeyValues *kvPlayer = kvMachine->FindKey( CFmtStr( "player%d", j ) );
  1025. if ( !kvPlayer )
  1026. continue;
  1027. XUID xuid = kvPlayer->GetUint64( "xuid", 0ull );
  1028. if ( !xuid )
  1029. continue;
  1030. DWORD ret = pfn( lobby, xuid );
  1031. if ( ERROR_SUCCESS != ret && ERROR_IO_PENDING != ret )
  1032. {
  1033. DevWarning( "XSessionJoin/Leave (pfn=%p) failed (code 0x%08X)!\n", pfn, ret );
  1034. Assert( 0 );
  1035. }
  1036. }
  1037. }
  1038. }
  1039. void MMX360_LobbyJoinMembers( KeyValues *pSettings, CX360LobbyObject &lobby, int idxMachineStart, int idxMachineEnd )
  1040. {
  1041. MMX360_Helper_LobbyJoinLeaveMembers( pSettings, lobby, MMX360_Helper_LobbyJoin, idxMachineStart, idxMachineEnd );
  1042. }
  1043. void MMX360_LobbyLeaveMembers( KeyValues *pSettings, CX360LobbyObject &lobby, int idxMachineStart, int idxMachineEnd )
  1044. {
  1045. MMX360_Helper_LobbyJoinLeaveMembers( pSettings, lobby, MMX360_Helper_LobbyLeave, idxMachineStart, idxMachineEnd );
  1046. }
  1047. bool MMX360_LobbySetActiveGameplayState( CX360LobbyObject &lobby, bool bActive, char const *szSecureServerAddress )
  1048. {
  1049. if ( bActive == lobby.m_bXSessionStarted )
  1050. {
  1051. DevWarning( "LobbySetActiveGameplayState called in same state '%s', ignored!\n",
  1052. bActive ? "active" : "inactive" );
  1053. Assert( bActive != lobby.m_bXSessionStarted );
  1054. return false;
  1055. }
  1056. DWORD ret = ERROR_SUCCESS;
  1057. if ( bActive )
  1058. {
  1059. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  1060. "OnProfilesWriteOpportunity", "reason", "sessionstart"
  1061. ) );
  1062. ret = g_pMatchExtensions->GetIXOnline()->XSessionStart( lobby.m_hHandle, 0, MMX360_NewOverlappedDormant() );
  1063. // Parse the address
  1064. if ( szSecureServerAddress )
  1065. {
  1066. if ( char const *szExternalPeer = StringAfterPrefix( szSecureServerAddress, "SESSIONINFO " ) )
  1067. {
  1068. MMX360_SessionInfoFromString( lobby.m_xiExternalPeer, szExternalPeer );
  1069. }
  1070. else
  1071. {
  1072. netadr_t na;
  1073. na.SetFromString( szSecureServerAddress );
  1074. lobby.m_inaSecure.s_addr = na.GetIPNetworkByteOrder();
  1075. }
  1076. }
  1077. }
  1078. else
  1079. {
  1080. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  1081. "OnProfilesWriteOpportunity", "reason", "sessionend"
  1082. ) );
  1083. // Asynchronous call to XSessionEnd
  1084. ret = g_pMatchExtensions->GetIXOnline()->XSessionEnd( lobby.m_hHandle, MMX360_NewOverlappedDormant() );
  1085. if ( lobby.m_inaSecure.s_addr )
  1086. {
  1087. g_pMatchExtensions->GetIXOnline()->XNetUnregisterInAddr( lobby.m_inaSecure );
  1088. lobby.m_inaSecure.s_addr = 0;
  1089. }
  1090. if ( 0ull != ( const uint64 & ) lobby.m_xiExternalPeer.sessionID )
  1091. {
  1092. g_pMatchExtensions->GetIXOnline()->XNetUnregisterKey( &lobby.m_xiExternalPeer.sessionID );
  1093. memset( &lobby.m_xiExternalPeer, 0, sizeof( lobby.m_xiExternalPeer ) );
  1094. }
  1095. }
  1096. if ( ret != ERROR_SUCCESS && ret != ERROR_IO_PENDING )
  1097. {
  1098. DevWarning( "LobbySetActiveGameplayState failed to become '%s', code = 0x%08X!\n",
  1099. bActive ? "active" : "inactive", ret );
  1100. return false;
  1101. }
  1102. else
  1103. {
  1104. lobby.m_bXSessionStarted = bActive;
  1105. // Update active gameplay sessions stack
  1106. CX360ActiveGameplaySession_t ags( lobby.m_hHandle );
  1107. for ( int k = 0; k < s_arrActiveGameplaySessions.Count(); ++ k )
  1108. {
  1109. CX360ActiveGameplaySession_t const &x = s_arrActiveGameplaySessions[k];
  1110. if ( x.m_hHandle == ags.m_hHandle )
  1111. {
  1112. s_arrActiveGameplaySessions.Remove( k -- );
  1113. }
  1114. }
  1115. if ( lobby.m_bXSessionStarted )
  1116. {
  1117. s_arrActiveGameplaySessions.AddToTail( ags );
  1118. }
  1119. return true;
  1120. }
  1121. }
  1122. void MMX360_CancelOverlapped( XOVERLAPPED *pxOverlapped )
  1123. {
  1124. g_pMatchExtensions->GetIXOnline()->XCancelOverlapped( pxOverlapped );
  1125. }
  1126. #endif