Team Fortress 2 Source Code as on 22/4/2020
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.

611 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "Session.h"
  7. #include "strtools.h"
  8. #include "matchmaking.h"
  9. #include "utllinkedlist.h"
  10. #include "tslist.h"
  11. #include "hl2orange.spa.h"
  12. #include "dbg.h"
  13. #ifdef IS_WINDOWS_PC
  14. #include "winlite.h" // for CloseHandle()
  15. #endif
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. extern IXboxSystem *g_pXboxSystem;
  19. #define ASYNC_OK 0
  20. #define ASYNC_FAIL 1
  21. //-----------------------------------------------------------------------------
  22. // Purpose: CSession class
  23. //-----------------------------------------------------------------------------
  24. CSession::CSession()
  25. {
  26. m_pParent = NULL;
  27. m_hSession = INVALID_HANDLE_VALUE;
  28. m_SessionState = SESSION_STATE_NONE;
  29. m_pRegistrationResults = NULL;
  30. ResetSession();
  31. }
  32. CSession::~CSession()
  33. {
  34. ResetSession();
  35. }
  36. //-----------------------------------------------------------------------------
  37. // Purpose: Reset a session to it's initial state
  38. //-----------------------------------------------------------------------------
  39. void CSession::ResetSession()
  40. {
  41. // Cleanup first
  42. switch( m_SessionState )
  43. {
  44. case SESSION_STATE_CREATING:
  45. CancelCreateSession();
  46. break;
  47. case SESSION_STATE_MIGRATING:
  48. // X360TBD:
  49. break;
  50. }
  51. if ( m_hSession != INVALID_HANDLE_VALUE )
  52. {
  53. Msg( "ResetSession: Destroying current session.\n" );
  54. DestroySession();
  55. m_hSession = INVALID_HANDLE_VALUE;
  56. }
  57. SwitchToState( SESSION_STATE_NONE );
  58. m_bIsHost = false;
  59. m_bIsArbitrated = false;
  60. m_bUsingQoS = false;
  61. m_bIsSystemLink = false;
  62. Q_memset( &m_nPlayerSlots, 0, sizeof( m_nPlayerSlots ) );
  63. Q_memset( &m_SessionInfo, 0, sizeof( m_SessionInfo ) );
  64. if ( m_pRegistrationResults )
  65. {
  66. delete m_pRegistrationResults;
  67. }
  68. m_nSessionFlags = 0;
  69. }
  70. //-----------------------------------------------------------------------------
  71. // Purpose: Set session contexts and properties
  72. //-----------------------------------------------------------------------------
  73. void CSession::SetContext( const uint nContextId, const uint nContextValue, const bool bAsync )
  74. {
  75. g_pXboxSystem->UserSetContext( XBX_GetPrimaryUserId(), nContextId, nContextValue, bAsync );
  76. }
  77. void CSession::SetProperty( const uint nPropertyId, const uint cbValue, const void *pvValue, const bool bAsync )
  78. {
  79. g_pXboxSystem->UserSetProperty( XBX_GetPrimaryUserId(), nPropertyId, cbValue, pvValue, bAsync );
  80. }
  81. //-----------------------------------------------------------------------------
  82. // Purpose: Send a notification to GameUI
  83. //-----------------------------------------------------------------------------
  84. void CSession::SendNotification( SESSION_NOTIFY notification )
  85. {
  86. Assert( m_pParent );
  87. m_pParent->SessionNotification( notification );
  88. }
  89. //-----------------------------------------------------------------------------
  90. // Purpose: Update the number of player slots filled
  91. //-----------------------------------------------------------------------------
  92. void CSession::UpdateSlots( const CClientInfo *pClient, bool bAddPlayers )
  93. {
  94. int cPlayers = pClient->m_cPlayers;
  95. if ( bAddPlayers )
  96. {
  97. if ( pClient->m_bInvited )
  98. {
  99. // Fill private slots first, then overflow into public slots
  100. m_nPlayerSlots[SLOTS_FILLEDPRIVATE] += cPlayers;
  101. pClient->m_numPrivateSlotsUsed = cPlayers;
  102. cPlayers = 0;
  103. if ( m_nPlayerSlots[SLOTS_FILLEDPRIVATE] > m_nPlayerSlots[SLOTS_TOTALPRIVATE] )
  104. {
  105. cPlayers = m_nPlayerSlots[SLOTS_FILLEDPRIVATE] - m_nPlayerSlots[SLOTS_TOTALPRIVATE];
  106. pClient->m_numPrivateSlotsUsed -= cPlayers;
  107. m_nPlayerSlots[SLOTS_FILLEDPRIVATE] = m_nPlayerSlots[SLOTS_TOTALPRIVATE];
  108. }
  109. }
  110. m_nPlayerSlots[SLOTS_FILLEDPUBLIC] += cPlayers;
  111. if ( m_nPlayerSlots[SLOTS_FILLEDPUBLIC] > m_nPlayerSlots[SLOTS_TOTALPUBLIC] )
  112. {
  113. //Handle error
  114. Warning( "Too many players!\n" );
  115. }
  116. }
  117. else
  118. {
  119. // The cast to 'int' is needed since otherwise underflow will wrap around to very large
  120. // numbers and the 'max' macro will do nothing.
  121. m_nPlayerSlots[SLOTS_FILLEDPRIVATE] = max( 0, (int)( m_nPlayerSlots[SLOTS_FILLEDPRIVATE] - pClient->m_numPrivateSlotsUsed ) );
  122. m_nPlayerSlots[SLOTS_FILLEDPUBLIC] = max( 0, (int)( m_nPlayerSlots[SLOTS_FILLEDPUBLIC] - ( pClient->m_cPlayers - pClient->m_numPrivateSlotsUsed ) ) );
  123. }
  124. }
  125. //-----------------------------------------------------------------------------
  126. // Purpose: Join players on the local client
  127. //-----------------------------------------------------------------------------
  128. void CSession::JoinLocal( const CClientInfo *pClient )
  129. {
  130. uint nUserIndex[MAX_PLAYERS_PER_CLIENT] = {0};
  131. bool bPrivate[MAX_PLAYERS_PER_CLIENT] = {0};
  132. for( int i = 0; i < pClient->m_cPlayers; ++i )
  133. {
  134. nUserIndex[i] = pClient->m_iControllers[i];
  135. bPrivate[i] = pClient->m_bInvited;
  136. }
  137. // X360TBD: Make async?
  138. uint ret = g_pXboxSystem->SessionJoinLocal( m_hSession, pClient->m_cPlayers, nUserIndex, bPrivate, false );
  139. if ( ret != ERROR_SUCCESS )
  140. {
  141. // Handle error
  142. Warning( "Failed to add local players\n" );
  143. }
  144. else
  145. {
  146. UpdateSlots( pClient, true );
  147. }
  148. }
  149. //-----------------------------------------------------------------------------
  150. // Purpose: Join players on a remote client
  151. //-----------------------------------------------------------------------------
  152. void CSession::JoinRemote( const CClientInfo *pClient )
  153. {
  154. XUID xuids[MAX_PLAYERS_PER_CLIENT] = {0};
  155. bool bPrivate[MAX_PLAYERS_PER_CLIENT] = {0};
  156. for( int i = 0; i < pClient->m_cPlayers; ++i )
  157. {
  158. xuids[i] = pClient->m_xuids[i];
  159. bPrivate[i] = pClient->m_bInvited;
  160. }
  161. // X360TBD: Make async?
  162. uint ret = g_pXboxSystem->SessionJoinRemote( m_hSession, pClient->m_cPlayers, xuids, bPrivate, false );
  163. if ( ret != ERROR_SUCCESS )
  164. {
  165. // Handle error
  166. Warning( "Join Remote Error\n" );
  167. }
  168. else
  169. {
  170. UpdateSlots( pClient, true );
  171. }
  172. }
  173. //-----------------------------------------------------------------------------
  174. // Purpose: Remove players on the local client
  175. //-----------------------------------------------------------------------------
  176. void CSession::RemoveLocal( const CClientInfo *pClient )
  177. {
  178. uint nUserIndex[MAX_PLAYERS_PER_CLIENT] = {0};
  179. for( int i = 0; i < pClient->m_cPlayers; ++i )
  180. {
  181. nUserIndex[i] = pClient->m_iControllers[i];
  182. }
  183. // X360TBD: Make async?
  184. uint ret = g_pXboxSystem->SessionLeaveLocal( m_hSession, pClient->m_cPlayers, nUserIndex, false );
  185. if ( ret != ERROR_SUCCESS )
  186. {
  187. // Handle error
  188. Warning( "Failed to remove local players\n" );
  189. }
  190. else
  191. {
  192. UpdateSlots( pClient, false );
  193. }
  194. }
  195. //-----------------------------------------------------------------------------
  196. // Purpose: Remove players on a remote client
  197. //-----------------------------------------------------------------------------
  198. void CSession::RemoveRemote( const CClientInfo *pClient )
  199. {
  200. XUID xuids[MAX_PLAYERS_PER_CLIENT] = {0};
  201. for( int i = 0; i < pClient->m_cPlayers; ++i )
  202. {
  203. xuids[i] = pClient->m_xuids[i];
  204. }
  205. // X360TBD: Make async?
  206. uint ret = g_pXboxSystem->SessionLeaveRemote( m_hSession, pClient->m_cPlayers, xuids, false );
  207. if ( ret != ERROR_SUCCESS )
  208. {
  209. // Handle error
  210. Warning( "Failed to remove remote players\n" );
  211. }
  212. else
  213. {
  214. UpdateSlots( pClient, false );
  215. }
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Purpose: Create a new session
  219. //-----------------------------------------------------------------------------
  220. bool CSession::CreateSession()
  221. {
  222. if( INVALID_HANDLE_VALUE != m_hSession )
  223. {
  224. Warning( "CreateSession called on existing session!" );
  225. DestroySession();
  226. m_hSession = INVALID_HANDLE_VALUE;
  227. }
  228. uint flags = m_nSessionFlags;
  229. if( m_bIsHost )
  230. {
  231. flags |= XSESSION_CREATE_HOST;
  232. }
  233. if ( flags & XSESSION_CREATE_USES_ARBITRATION )
  234. {
  235. m_bIsArbitrated = true;
  236. }
  237. m_hCreateHandle = g_pXboxSystem->CreateAsyncHandle();
  238. // Create the session
  239. uint ret = g_pXboxSystem->CreateSession( flags,
  240. XBX_GetPrimaryUserId(),
  241. m_nPlayerSlots[SLOTS_TOTALPUBLIC],
  242. m_nPlayerSlots[SLOTS_TOTALPRIVATE],
  243. &m_SessionNonce,
  244. &m_SessionInfo,
  245. &m_hSession,
  246. true,
  247. &m_hCreateHandle );
  248. if( ret != ERROR_SUCCESS && ret != ERROR_IO_PENDING )
  249. {
  250. Warning( "XSessionCreate failed with error %d\n", ret );
  251. return false;
  252. }
  253. SwitchToState( SESSION_STATE_CREATING );
  254. return true;
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose: Check for completion while creating a new session
  258. //-----------------------------------------------------------------------------
  259. void CSession::UpdateCreating()
  260. {
  261. DWORD ret = g_pXboxSystem->GetOverlappedResult( m_hCreateHandle, NULL, false );
  262. if ( ret == ERROR_IO_INCOMPLETE )
  263. {
  264. // Still waiting
  265. return;
  266. }
  267. else
  268. {
  269. SESSION_STATE nextState = SESSION_STATE_IDLE;
  270. // Operation completed
  271. SESSION_NOTIFY notification = IsHost() ? SESSION_NOTIFY_CREATED_HOST : SESSION_NOTIFY_CREATED_CLIENT;
  272. if ( ret != ERROR_SUCCESS )
  273. {
  274. Warning( "CSession: CreateSession failed. Error %d\n", ret );
  275. nextState = SESSION_STATE_NONE;
  276. notification = SESSION_NOTIFY_FAIL_CREATE;
  277. }
  278. g_pXboxSystem->ReleaseAsyncHandle( m_hCreateHandle );
  279. SendNotification( notification );
  280. SwitchToState( nextState );
  281. }
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose: Cancel async session creation
  285. //-----------------------------------------------------------------------------
  286. void CSession::CancelCreateSession()
  287. {
  288. if ( m_SessionState != SESSION_STATE_CREATING )
  289. return;
  290. g_pXboxSystem->CancelOverlappedOperation( &m_hCreateHandle );
  291. g_pXboxSystem->ReleaseAsyncHandle( m_hCreateHandle );
  292. #ifndef POSIX
  293. if( INVALID_HANDLE_VALUE != m_hSession )
  294. {
  295. CloseHandle( m_hSession );
  296. m_hSession = INVALID_HANDLE_VALUE;
  297. }
  298. #endif
  299. }
  300. //-----------------------------------------------------------------------------
  301. // Purpose: Close an existing session
  302. //-----------------------------------------------------------------------------
  303. void CSession::DestroySession()
  304. {
  305. // TODO: Make this async
  306. uint ret = g_pXboxSystem->DeleteSession( m_hSession, false );
  307. if ( ret != ERROR_SUCCESS )
  308. {
  309. Warning( "Failed to delete session with error %d.\n", ret );
  310. }
  311. }
  312. //-----------------------------------------------------------------------------
  313. // Purpose: Register for arbritation in a ranked match
  314. //-----------------------------------------------------------------------------
  315. void CSession::RegisterForArbitration()
  316. {
  317. uint bytes = 0;
  318. m_pRegistrationResults = NULL;
  319. // Call once to determine size of results buffer
  320. int ret = g_pXboxSystem->SessionArbitrationRegister( m_hSession, 0, m_SessionNonce, &bytes, NULL, false );
  321. if ( ret != ERROR_INSUFFICIENT_BUFFER )
  322. {
  323. Warning( "Failed registering for arbitration\n" );
  324. return;
  325. }
  326. m_pRegistrationResults = (XSESSION_REGISTRATION_RESULTS*)new byte[ bytes ];
  327. m_hRegisterHandle = g_pXboxSystem->CreateAsyncHandle();
  328. ret = g_pXboxSystem->SessionArbitrationRegister( m_hSession, 0, m_SessionNonce, &bytes, m_pRegistrationResults, true, &m_hRegisterHandle );
  329. if( ret != ERROR_IO_PENDING )
  330. {
  331. Warning( "Failed registering for arbitration\n" );
  332. }
  333. m_SessionState = SESSION_STATE_REGISTERING;
  334. }
  335. //-----------------------------------------------------------------------------
  336. // Purpose: Check for completion of arbitration registration
  337. //-----------------------------------------------------------------------------
  338. void CSession::UpdateRegistering()
  339. {
  340. DWORD ret = g_pXboxSystem->GetOverlappedResult( m_hRegisterHandle, NULL, false );
  341. if ( ret == ERROR_IO_INCOMPLETE )
  342. {
  343. // Still waiting
  344. return;
  345. }
  346. else
  347. {
  348. SESSION_STATE nextState = SESSION_STATE_IDLE;
  349. // Operation completed
  350. SESSION_NOTIFY notification = SESSION_NOTIFY_REGISTER_COMPLETED;
  351. if ( ret != ERROR_SUCCESS )
  352. {
  353. Warning( "CSession: Registration failed. Error %d\n", ret );
  354. nextState = SESSION_STATE_NONE;
  355. notification = SESSION_NOTIFY_FAIL_REGISTER;
  356. }
  357. g_pXboxSystem->ReleaseAsyncHandle( m_hRegisterHandle );
  358. SendNotification( notification );
  359. SwitchToState( nextState );
  360. }
  361. }
  362. //-----------------------------------------------------------------------------
  363. // Purpose: Migrate the session to a new host
  364. //-----------------------------------------------------------------------------
  365. bool CSession::MigrateHost()
  366. {
  367. if ( IsHost() )
  368. {
  369. // Migrate call will fill this in for us
  370. Q_memcpy( &m_NewSessionInfo, &m_SessionInfo, sizeof( m_NewSessionInfo ) );
  371. }
  372. m_hMigrateHandle = g_pXboxSystem->CreateAsyncHandle();
  373. int ret = g_pXboxSystem->SessionMigrate( m_hSession, m_nOwnerId, &m_NewSessionInfo, true, &m_hMigrateHandle );
  374. if ( ret != ERROR_IO_PENDING )
  375. {
  376. return false;
  377. }
  378. SwitchToState( SESSION_STATE_MIGRATING );
  379. return true;
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose: Check for completion while migrating a session
  383. //-----------------------------------------------------------------------------
  384. void CSession::UpdateMigrating()
  385. {
  386. DWORD ret = g_pXboxSystem->GetOverlappedResult( m_hMigrateHandle, NULL, false );
  387. if ( ret == ERROR_IO_INCOMPLETE )
  388. {
  389. // Still waiting
  390. return;
  391. }
  392. else
  393. {
  394. SESSION_STATE nextState = SESSION_STATE_IDLE;
  395. // Operation completed
  396. SESSION_NOTIFY notification = SESSION_NOTIFY_MIGRATION_COMPLETED;
  397. if ( ret != ERROR_SUCCESS )
  398. {
  399. Warning( "CSession: MigrateSession failed. Error %d\n", ret );
  400. nextState = SESSION_STATE_NONE;
  401. notification = SESSION_NOTIFY_FAIL_MIGRATE;
  402. }
  403. g_pXboxSystem->ReleaseAsyncHandle( m_hMigrateHandle );
  404. SendNotification( notification );
  405. SwitchToState( nextState );
  406. }
  407. }
  408. //-----------------------------------------------------------------------------
  409. // Purpose: Change state
  410. //-----------------------------------------------------------------------------
  411. void CSession::SwitchToState( SESSION_STATE newState )
  412. {
  413. m_SessionState = newState;
  414. }
  415. //-----------------------------------------------------------------------------
  416. // Purpose: Per-frame update
  417. //-----------------------------------------------------------------------------
  418. void CSession::RunFrame()
  419. {
  420. switch( m_SessionState )
  421. {
  422. case SESSION_STATE_CREATING:
  423. UpdateCreating();
  424. break;
  425. case SESSION_STATE_REGISTERING:
  426. UpdateRegistering();
  427. break;
  428. case SESSION_STATE_MIGRATING:
  429. UpdateMigrating();
  430. break;
  431. default:
  432. break;
  433. }
  434. }
  435. //-----------------------------------------------------------------------------
  436. // Accessors
  437. //-----------------------------------------------------------------------------
  438. HANDLE CSession::GetSessionHandle()
  439. {
  440. return m_hSession;
  441. }
  442. void CSession::SetSessionInfo( XSESSION_INFO *pInfo )
  443. {
  444. memcpy( &m_SessionInfo, pInfo, sizeof( XSESSION_INFO ) );
  445. }
  446. void CSession::SetNewSessionInfo( XSESSION_INFO *pInfo )
  447. {
  448. memcpy( &m_NewSessionInfo, pInfo, sizeof( XSESSION_INFO ) );
  449. }
  450. void CSession::GetSessionInfo( XSESSION_INFO *pInfo )
  451. {
  452. Assert( pInfo );
  453. memcpy( pInfo, &m_SessionInfo, sizeof( XSESSION_INFO ) );
  454. }
  455. void CSession::GetNewSessionInfo( XSESSION_INFO *pInfo )
  456. {
  457. Assert( pInfo );
  458. memcpy( pInfo, &m_NewSessionInfo, sizeof( XSESSION_INFO ) );
  459. }
  460. void CSession::SetSessionNonce( int64 nonce )
  461. {
  462. m_SessionNonce = nonce;
  463. }
  464. uint64 CSession::GetSessionNonce()
  465. {
  466. return m_SessionNonce;
  467. }
  468. XNKID CSession::GetSessionId()
  469. {
  470. return m_SessionInfo.sessionID;
  471. }
  472. void CSession::SetSessionSlots(unsigned int nSlot, unsigned int nPlayers)
  473. {
  474. Assert( nSlot < SLOTS_LAST );
  475. m_nPlayerSlots[nSlot] = nPlayers;
  476. }
  477. unsigned int CSession::GetSessionSlots( unsigned int nSlot )
  478. {
  479. Assert( nSlot < SLOTS_LAST );
  480. return m_nPlayerSlots[nSlot];
  481. }
  482. void CSession::SetSessionFlags( uint flags )
  483. {
  484. m_nSessionFlags = flags;
  485. }
  486. uint CSession::GetSessionFlags()
  487. {
  488. return m_nSessionFlags;
  489. }
  490. int CSession::GetPlayerCount()
  491. {
  492. return m_nPlayerSlots[SLOTS_FILLEDPRIVATE] + m_nPlayerSlots[SLOTS_FILLEDPUBLIC];
  493. }
  494. void CSession::SetFlag( uint nFlag )
  495. {
  496. m_nSessionFlags |= nFlag;
  497. }
  498. void CSession::SetIsHost( bool bHost )
  499. {
  500. m_bIsHost = bHost;
  501. }
  502. void CSession::SetIsSystemLink( bool bSystemLink )
  503. {
  504. m_bIsSystemLink = bSystemLink;
  505. }
  506. void CSession::SetOwnerId( uint id )
  507. {
  508. m_nOwnerId = id;
  509. }
  510. bool CSession::IsHost()
  511. {
  512. return m_bIsHost;
  513. }
  514. bool CSession::IsFull()
  515. {
  516. return ( GetSessionSlots( SLOTS_TOTALPRIVATE ) == GetSessionSlots( SLOTS_FILLEDPRIVATE ) &&
  517. GetSessionSlots( SLOTS_TOTALPUBLIC ) == GetSessionSlots( SLOTS_FILLEDPUBLIC ) );
  518. }
  519. bool CSession::IsArbitrated()
  520. {
  521. return m_bIsArbitrated;
  522. }
  523. bool CSession::IsSystemLink()
  524. {
  525. return m_bIsSystemLink;
  526. }
  527. void CSession::SetParent( CMatchmaking *pParent )
  528. {
  529. m_pParent = pParent;
  530. }
  531. double CSession::GetTime()
  532. {
  533. return Plat_FloatTime();
  534. }