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.

858 lines
23 KiB

  1. //===== Copyright � 1996-2009, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "mm_framework.h"
  7. #include "vstdlib/random.h"
  8. #include "fmtstr.h"
  9. #include "steam_datacenterjobs.h"
  10. // memdbgon must be the last include file in a .cpp file!!!
  11. #include "tier0/memdbgon.h"
  12. ConVar mm_teamsearch_errortime( "mm_teamsearch_errortime", "3.0", FCVAR_DEVELOPMENTONLY, "Time team search is in error state until it self-cancels" );
  13. ConVar mm_teamsearch_nostart( "mm_teamsearch_nostart", "0", FCVAR_DEVELOPMENTONLY, "Team search will fake cancel before searching for server" );
  14. #ifndef _GAMECONSOLE
  15. ConVar sv_search_team_key( "sv_search_team_key", "public", FCVAR_RELEASE, "When initiating team search, set this key to match with known opponents team" );
  16. #endif
  17. //
  18. //
  19. // Specialized implementation of SysSessions for team search
  20. //
  21. //
  22. template < typename TBaseSession >
  23. class CSysSessionStubForTeamSearch : public TBaseSession
  24. {
  25. public:
  26. explicit CSysSessionStubForTeamSearch( KeyValues *pSettings, CMatchSessionOnlineTeamSearch *pMatchSession ) :
  27. TBaseSession( pSettings ),
  28. m_pMatchSession( pMatchSession )
  29. {
  30. }
  31. protected:
  32. virtual void OnSessionEvent( KeyValues *notify )
  33. {
  34. if ( !notify )
  35. return;
  36. if ( m_pMatchSession )
  37. m_pMatchSession->OnSessionEvent( notify );
  38. notify->deleteThis();
  39. }
  40. protected:
  41. // No voice
  42. virtual void Voice_ProcessTalkers( KeyValues *pMachine, bool bAdd ) {}
  43. virtual void Voice_CaptureAndTransmitLocalVoiceData() {}
  44. virtual void Voice_Playback( KeyValues *msg, XUID xuidSrc ) OVERRIDE {}
  45. virtual void Voice_UpdateLocalHeadsetsStatus() {}
  46. virtual void Voice_UpdateMutelist() {}
  47. protected:
  48. // No p2p connections, just two hosts talking
  49. virtual void XP2P_Interconnect() {}
  50. protected:
  51. CMatchSessionOnlineTeamSearch *m_pMatchSession;
  52. };
  53. typedef CSysSessionStubForTeamSearch< CSysSessionClient > CSysTeamSearchClient;
  54. typedef CSysSessionStubForTeamSearch< CSysSessionHost > CSysTeamSearchHost;
  55. //
  56. //
  57. // CMatchSessionOnlineTeamSearch implementation
  58. //
  59. //
  60. CMatchSessionOnlineTeamSearch::CMatchSessionOnlineTeamSearch( KeyValues *pSettings, CMatchSessionOnlineHost *pHost ) :
  61. m_pHostSession( pHost ),
  62. m_eState( STATE_SEARCHING ),
  63. m_pSysSessionHost( NULL ),
  64. m_pSysSessionClient( NULL ),
  65. m_pDsSearcher( NULL ),
  66. m_flActionTime( 0.0f ),
  67. m_pUpdateHostSessionPacket( NULL ),
  68. m_autodelete_pUpdateHostSessionPacket( m_pUpdateHostSessionPacket ),
  69. m_iLinkState( 0 ),
  70. m_xuidLinkPeer( 0ull ),
  71. #ifdef _X360
  72. m_pXlspConnection( NULL ),
  73. m_pXlspCommandBatch( NULL ),
  74. #endif
  75. m_flCreationTime( Plat_FloatTime() )
  76. {
  77. m_pSettings = pSettings->MakeCopy();
  78. m_autodelete_pSettings.Assign( m_pSettings );
  79. #ifndef _GAMECONSOLE
  80. m_pSettings->SetString( "options/searchteamkey", sv_search_team_key.GetString() );
  81. #endif
  82. if ( IsPC() )
  83. {
  84. // On PC limit server selection for team games to official and listen
  85. if ( Q_stricmp( m_pSettings->GetString( "options/server" ), "listen" ) )
  86. m_pSettings->SetString( "options/server", "official" );
  87. }
  88. DevMsg( "Created CMatchSessionOnlineTeamSearch:\n" );
  89. KeyValuesDumpAsDevMsg( m_pSettings, 1 );
  90. }
  91. CMatchSessionOnlineTeamSearch::~CMatchSessionOnlineTeamSearch()
  92. {
  93. DevMsg( "Destroying CMatchSessionOnlineTeamSearch.\n" );
  94. }
  95. CMatchSearcher * CMatchSessionOnlineTeamSearch::OnStartSearching()
  96. {
  97. return new CMatchSearcher_OnlineTeamSearch( this, m_pSettings->MakeCopy() );
  98. }
  99. CMatchSearcher_OnlineTeamSearch::CMatchSearcher_OnlineTeamSearch( CMatchSessionOnlineTeamSearch *pSession, KeyValues *pSettings ) :
  100. CMatchSearcher_OnlineSearch( pSession, pSettings )
  101. {
  102. // Search settings cannot be locked as it will interfere with syssessions
  103. m_pSettings->SetString( "system/lock", "" );
  104. // Team versus searchable lobbies are public
  105. m_pSettings->SetString( "system/access", "public" );
  106. // If we happen to host the session it will be a special teamlink session
  107. m_pSettings->SetString( "system/netflag", "teamlink" );
  108. if ( IMatchSession *pMainSession = g_pMatchFramework->GetMatchSession() )
  109. {
  110. KeyValues *kvSystemData = pMainSession->GetSessionSystemData();
  111. m_pSettings->SetUint64( "System/dependentlobby", kvSystemData->GetUint64( "xuidReserve" ) );
  112. }
  113. // Double the number of slots since we assume two teams playing
  114. m_pSettings->SetInt( "Members/numSlots", g_pMatchFramework->GetMatchTitle()->GetTotalNumPlayersSupported() );
  115. // Let the title extend the game settings
  116. g_pMMF->GetMatchTitleGameSettingsMgr()->InitializeGameSettings( m_pSettings, "search_onlineteam" );
  117. DevMsg( "CMatchSearcher_OnlineTeamSearch title adjusted settings:\n" );
  118. KeyValuesDumpAsDevMsg( m_pSettings, 1 );
  119. }
  120. void CMatchSearcher_OnlineTeamSearch::StartSearchPass( KeyValues *pSearchPass )
  121. {
  122. #ifndef _GAMECONSOLE
  123. pSearchPass->SetString( "Filter=/options:searchteamkey", sv_search_team_key.GetString() );
  124. #endif
  125. CMatchSearcher_OnlineSearch::StartSearchPass( pSearchPass );
  126. }
  127. void CMatchSessionOnlineTeamSearch::OnSearchEvent( KeyValues *pNotify )
  128. {
  129. if ( !pNotify )
  130. return;
  131. if ( m_pHostSession )
  132. m_pHostSession->OnEvent( pNotify );
  133. pNotify->deleteThis();
  134. }
  135. void CMatchSessionOnlineTeamSearch::OnSessionEvent( KeyValues *pEvent )
  136. {
  137. OnEvent( pEvent );
  138. // Searching state is handled entirely by the base class
  139. if ( m_eState == STATE_SEARCHING )
  140. return;
  141. char const *szEvent = pEvent->GetName();
  142. if ( !Q_stricmp( "mmF->SysSessionUpdate", szEvent ) )
  143. {
  144. if ( m_pSysSessionHost && pEvent->GetPtr( "syssession", NULL ) == m_pSysSessionHost )
  145. {
  146. // We had a session error
  147. if ( char const *szError = pEvent->GetString( "error", NULL ) )
  148. {
  149. // Destroy the session
  150. m_pSysSessionHost->Destroy();
  151. m_pSysSessionHost = NULL;
  152. // Handle error
  153. m_eState = STATE_ERROR;
  154. m_flActionTime = Plat_FloatTime() + mm_teamsearch_errortime.GetFloat();
  155. OnSearchEvent( new KeyValues(
  156. "OnMatchSessionUpdate",
  157. "state", "progress",
  158. "progress", "searcherror"
  159. ) );
  160. return;
  161. }
  162. // This is our session
  163. switch ( m_eState )
  164. {
  165. case STATE_CREATING:
  166. // Session created successfully and we are awaiting peer
  167. m_eState = STATE_AWAITING_PEER;
  168. OnSearchEvent( new KeyValues(
  169. "OnMatchSessionUpdate",
  170. "state", "progress",
  171. "progress", "searchawaitingpeer"
  172. ) );
  173. break;
  174. }
  175. }
  176. }
  177. else if ( !Q_stricmp( "mmF->SysSessionCommand", szEvent ) )
  178. {
  179. if ( m_pSysSessionHost && pEvent->GetPtr( "syssession", NULL ) == m_pSysSessionHost )
  180. {
  181. KeyValues *pCommand = pEvent->GetFirstTrueSubKey();
  182. if ( pCommand )
  183. {
  184. OnRunSessionCommand( pCommand );
  185. }
  186. }
  187. if ( m_pSysSessionClient && pEvent->GetPtr( "syssession", NULL ) == m_pSysSessionClient )
  188. {
  189. KeyValues *pCommand = pEvent->GetFirstTrueSubKey();
  190. if ( pCommand )
  191. {
  192. OnRunSessionCommand( pCommand );
  193. }
  194. }
  195. }
  196. else if ( !Q_stricmp( "OnPlayerMachinesConnected", szEvent ) )
  197. {
  198. // Another team is challenging us
  199. m_eState = STATE_LINK_HOST;
  200. m_xuidLinkPeer = pEvent->GetUint64( "id" );
  201. OnSearchEvent( new KeyValues(
  202. "OnMatchSessionUpdate",
  203. "state", "progress",
  204. "progress", "searchlinked"
  205. ) );
  206. // Lock the session to prevent other teams challenges
  207. m_pSettings->SetString( "system/lock", "linked" );
  208. m_pSysSessionHost->OnUpdateSessionSettings( KeyValues::AutoDeleteInline( KeyValues::FromString(
  209. "update",
  210. " update { "
  211. " system { "
  212. " lock linked "
  213. " } "
  214. " } "
  215. ) ) );
  216. LinkHost().LinkInit();
  217. }
  218. else if ( !Q_stricmp( "OnPlayerRemoved", szEvent ) )
  219. {
  220. // Assert( m_xuidLinkPeer == pEvent->GetUint64( "xuid" ) );
  221. // the peer gave up before we finished negotiation, start the process all over
  222. ResetAndRestartTeamSearch();
  223. }
  224. }
  225. void CMatchSessionOnlineTeamSearch::ResetAndRestartTeamSearch()
  226. {
  227. m_eState = STATE_ERROR;
  228. m_flActionTime = 0.0f;
  229. OnSearchEvent( new KeyValues(
  230. "OnMatchSessionUpdate",
  231. "state", "progress",
  232. "progress", "restart"
  233. ) );
  234. }
  235. void CMatchSessionOnlineTeamSearch::RememberHostSessionUpdatePacket( KeyValues *pPacket )
  236. {
  237. if ( m_pUpdateHostSessionPacket )
  238. m_pUpdateHostSessionPacket->deleteThis();
  239. m_pUpdateHostSessionPacket = pPacket;
  240. m_autodelete_pUpdateHostSessionPacket.Assign( m_pUpdateHostSessionPacket );
  241. }
  242. void CMatchSessionOnlineTeamSearch::ApplyHostSessionUpdatePacket()
  243. {
  244. if ( m_pUpdateHostSessionPacket )
  245. {
  246. KeyValues *pUpdatePkt = m_pUpdateHostSessionPacket;
  247. m_pUpdateHostSessionPacket = NULL;
  248. m_autodelete_pUpdateHostSessionPacket.Assign( NULL );
  249. m_pHostSession->UpdateSessionSettings( pUpdatePkt );
  250. pUpdatePkt->deleteThis();
  251. }
  252. }
  253. void CMatchSessionOnlineTeamSearch::OnRunSessionCommand( KeyValues *pCommand )
  254. {
  255. switch ( m_eState )
  256. {
  257. case STATE_LINK_HOST:
  258. LinkHost().LinkCommand( pCommand );
  259. break;
  260. case STATE_LINK_CLIENT:
  261. LinkClient().LinkCommand( pCommand );
  262. break;
  263. }
  264. }
  265. CMatchSessionOnlineTeamSearchLinkHost & CMatchSessionOnlineTeamSearch::LinkHost()
  266. {
  267. return static_cast< CMatchSessionOnlineTeamSearchLinkHost & >( *this );
  268. }
  269. CMatchSessionOnlineTeamSearchLinkClient & CMatchSessionOnlineTeamSearch::LinkClient()
  270. {
  271. return static_cast< CMatchSessionOnlineTeamSearchLinkClient & >( *this );
  272. }
  273. CSysSessionBase * CMatchSessionOnlineTeamSearch::LinkSysSession()
  274. {
  275. switch ( m_eState )
  276. {
  277. case STATE_LINK_HOST:
  278. return m_pSysSessionHost;
  279. case STATE_LINK_CLIENT:
  280. return m_pSysSessionClient;
  281. default:
  282. Assert( !m_pSysSessionClient || !m_pSysSessionHost );
  283. if ( m_pSysSessionHost )
  284. return m_pSysSessionHost;
  285. else if ( m_pSysSessionClient )
  286. return m_pSysSessionClient;
  287. else
  288. return NULL;
  289. }
  290. }
  291. CSysSessionClient * CMatchSessionOnlineTeamSearch::OnBeginJoiningSearchResult()
  292. {
  293. return new CSysTeamSearchClient( m_pSettings, this );
  294. }
  295. void CMatchSessionOnlineTeamSearch::OnSearchCompletedSuccess( CSysSessionClient *pSysSession, KeyValues *pSettings )
  296. {
  297. // Push the settings back into searcher since we are going to be using them
  298. m_pSettings = pSettings;
  299. m_autodelete_pSettings.Assign( m_pSettings );
  300. m_pSysSessionClient = pSysSession;
  301. // Established connection with another team
  302. m_eState = STATE_LINK_CLIENT;
  303. m_xuidLinkPeer = pSysSession->GetHostXuid();
  304. OnSearchEvent( new KeyValues(
  305. "OnMatchSessionUpdate",
  306. "state", "progress",
  307. "progress", "searchlinked"
  308. ) );
  309. LinkClient().LinkInit();
  310. }
  311. void CMatchSessionOnlineTeamSearch::OnSearchCompletedEmpty( KeyValues *pSettings )
  312. {
  313. // Push the settings back into searcher since we are going to be searching again
  314. m_pSettings = pSettings;
  315. m_autodelete_pSettings.Assign( m_pSettings );
  316. // Idle out for some time
  317. m_eState = STATE_CREATING;
  318. // Notify everybody that our search idled out
  319. OnSearchEvent( new KeyValues(
  320. "OnMatchSessionUpdate",
  321. "state", "progress",
  322. "progress", "searchidle"
  323. ) );
  324. // Allocate the hosting object to accept matches
  325. m_pSysSessionHost = new CSysTeamSearchHost( m_pSettings, this );
  326. }
  327. void CMatchSessionOnlineTeamSearch::DebugPrint()
  328. {
  329. DevMsg( "CMatchSessionOnlineTeamSearch::CMatchSessionOnlineSearch\n" );
  330. CMatchSessionOnlineSearch::DebugPrint();
  331. DevMsg( "CMatchSessionOnlineTeamSearch [ state=%d ]\n", m_eState );
  332. DevMsg( " linkstate: %d\n", m_iLinkState );
  333. DevMsg( " linkpeer: %llx\n", m_xuidLinkPeer );
  334. DevMsg( " actiontime:%.3f\n", m_flActionTime ? m_flActionTime - Plat_FloatTime() : 0.0f );
  335. if ( m_pDsSearcher )
  336. DevMsg( "TeamSearch: Dedicated search in progress\n" );
  337. else
  338. DevMsg( "TeamSearch: Dedicated search not active\n" );
  339. DevMsg( "TeamSearch: SysSession host state:\n" );
  340. if ( m_pSysSessionHost )
  341. m_pSysSessionHost->DebugPrint();
  342. else
  343. DevMsg( "SysSession is NULL\n" );
  344. DevMsg( "TeamSearch: SysSession client state:\n" );
  345. if ( m_pSysSessionClient )
  346. m_pSysSessionClient->DebugPrint();
  347. else
  348. DevMsg( "SysSession is NULL\n" );
  349. }
  350. void CMatchSessionOnlineTeamSearch::OnEvent( KeyValues *pEvent )
  351. {
  352. CMatchSessionOnlineSearch::OnEvent( pEvent );
  353. // Let the dedicated search handle the responses too
  354. if ( m_pDsSearcher )
  355. m_pDsSearcher->OnEvent( pEvent );
  356. }
  357. void CMatchSessionOnlineTeamSearch::Update()
  358. {
  359. switch ( m_eState )
  360. {
  361. case STATE_ERROR:
  362. if ( m_flActionTime && Plat_FloatTime() > m_flActionTime )
  363. {
  364. m_flActionTime = 0.0f;
  365. if ( m_pHostSession )
  366. m_pHostSession->Command( KeyValues::AutoDeleteInline( new KeyValues( "Stop" ) ) );
  367. }
  368. return;
  369. case STATE_AWAITING_PEER:
  370. break;
  371. case STATE_LINK_CLIENT:
  372. LinkClient().LinkUpdate();
  373. break;
  374. case STATE_LINK_HOST:
  375. LinkHost().LinkUpdate();
  376. break;
  377. }
  378. CMatchSessionOnlineSearch::Update();
  379. if ( CSysSessionBase *pLinkSysSession = LinkSysSession() )
  380. pLinkSysSession->Update();
  381. }
  382. void CMatchSessionOnlineTeamSearch::Destroy()
  383. {
  384. if ( m_pSysSessionHost )
  385. {
  386. m_pSysSessionHost->Destroy();
  387. m_pSysSessionHost = NULL;
  388. }
  389. if ( m_pSysSessionClient )
  390. {
  391. m_pSysSessionClient->Destroy();
  392. m_pSysSessionClient = NULL;
  393. }
  394. if ( m_pDsSearcher )
  395. {
  396. m_pDsSearcher->Destroy();
  397. m_pDsSearcher = NULL;
  398. }
  399. #ifdef _X360
  400. if ( m_pXlspCommandBatch )
  401. {
  402. m_pXlspCommandBatch->Destroy();
  403. m_pXlspCommandBatch = NULL;
  404. }
  405. if ( m_pXlspConnection )
  406. {
  407. m_pXlspConnection->Destroy();
  408. m_pXlspConnection = NULL;
  409. }
  410. #endif
  411. CMatchSessionOnlineSearch::Destroy();
  412. }
  413. //////////////////////////////////////////////////////////////////////////
  414. //
  415. // Link base implementation
  416. //
  417. //
  418. void CMatchSessionOnlineTeamSearchLinkBase::LinkUpdate()
  419. {
  420. switch ( m_iLinkState )
  421. {
  422. case STATE_SEARCHING_DEDICATED:
  423. Assert( m_pDsSearcher );
  424. if ( m_pDsSearcher )
  425. {
  426. m_pDsSearcher->Update();
  427. if ( m_pDsSearcher->IsFinished() )
  428. {
  429. OnDedicatedSearchFinished();
  430. return;
  431. }
  432. }
  433. break;
  434. case STATE_HOSTING_LISTEN_SERVER:
  435. if ( KeyValues *pServer = m_pHostSession->GetSessionSettings()->FindKey( "server" ) )
  436. {
  437. // Listen server has started and we should notify the link peer
  438. if ( KeyValues *pPeerCommand = new KeyValues( "TeamSearchLink::ListenClient" ) )
  439. {
  440. KeyValues::AutoDelete autodelete( pPeerCommand );
  441. pPeerCommand->SetString( "run", "xuid" );
  442. pPeerCommand->SetUint64( "runxuid", m_xuidLinkPeer );
  443. pPeerCommand->AddSubKey( pServer->MakeCopy() );
  444. pPeerCommand->SetString( "server/server", "externalpeer" ); // for other team it is not a listen server, but externalpeer
  445. pPeerCommand->SetInt( "server/team", 2 ); // other team is nominated team 2
  446. LinkSysSession()->Command( pPeerCommand );
  447. }
  448. m_iLinkState = STATE_LINK_FINISHED;
  449. }
  450. break;
  451. }
  452. }
  453. void CMatchSessionOnlineTeamSearchLinkBase::LinkCommand( KeyValues *pCommand )
  454. {
  455. char const *szCommand = pCommand->GetName();
  456. if ( !Q_stricmp( "TeamSearchLink::Dedicated", szCommand ) ||
  457. !Q_stricmp( "TeamSearchLink::ListenClient", szCommand ) )
  458. {
  459. Assert( m_iLinkState == STATE_WAITING_FOR_PEER_SERVER );
  460. KeyValues *pServerInfo = pCommand->FindKey( "server", false );
  461. if ( !pServerInfo )
  462. {
  463. ResetAndRestartTeamSearch();
  464. return;
  465. }
  466. // Apply host session update packet
  467. ApplyHostSessionUpdatePacket();
  468. // Notify our team about the server
  469. if ( KeyValues *pOurTeamNotify = new KeyValues( CFmtStr( "TeamSearchResult::%s", szCommand + strlen( "TeamSearchLink::" ) ) ) )
  470. {
  471. pOurTeamNotify->AddSubKey( pServerInfo->MakeCopy() );
  472. OnSearchEvent( pOurTeamNotify );
  473. }
  474. return;
  475. }
  476. }
  477. void CMatchSessionOnlineTeamSearchLinkBase::StartHostingListenServer()
  478. {
  479. if ( mm_teamsearch_nostart.GetBool() )
  480. {
  481. // Fake-cancel the session for debugging
  482. m_pHostSession->Command( KeyValues::AutoDeleteInline( new KeyValues( "Cancel" ) ) );
  483. return;
  484. }
  485. m_iLinkState = STATE_HOSTING_LISTEN_SERVER;
  486. // Update host session settings packet
  487. ApplyHostSessionUpdatePacket();
  488. OnSearchEvent( new KeyValues( "TeamSearchResult::ListenHost" ) );
  489. }
  490. void CMatchSessionOnlineTeamSearchLinkBase::StartDedicatedServerSearch()
  491. {
  492. if ( mm_teamsearch_nostart.GetBool() )
  493. {
  494. // Fake-cancel the session for debugging
  495. m_pHostSession->Command( KeyValues::AutoDeleteInline( new KeyValues( "Cancel" ) ) );
  496. return;
  497. }
  498. m_iLinkState = STATE_SEARCHING_DEDICATED;
  499. // Notify everybody that we are searching for dedicated server now
  500. OnSearchEvent( new KeyValues(
  501. "OnMatchSessionUpdate",
  502. "state", "progress",
  503. "progress", "dedicated"
  504. ) );
  505. m_pDsSearcher = new CDsSearcher( m_pSettings, m_pHostSession->GetSessionSystemData()->GetUint64( "xuidReserve" ), NULL );
  506. }
  507. void CMatchSessionOnlineTeamSearchLinkBase::OnDedicatedSearchFinished()
  508. {
  509. Assert( m_pDsSearcher );
  510. CDsSearcher::DsResult_t dsResult = m_pDsSearcher->GetResult();
  511. m_pDsSearcher->Destroy();
  512. m_pDsSearcher = NULL;
  513. if ( !dsResult.m_bDedicated )
  514. {
  515. StartHostingListenServer();
  516. return;
  517. }
  518. //
  519. // Serialize the information about the dedicated server
  520. //
  521. KeyValues *pServerInfo = new KeyValues( "server" );
  522. dsResult.CopyToServerKey( pServerInfo );
  523. pServerInfo->SetUint64( "reservationid", m_pHostSession->GetSessionSystemData()->GetUint64( "xuidReserve" ) );
  524. // Apply host session update packet
  525. ApplyHostSessionUpdatePacket();
  526. //
  527. // Notify the peer team about the server
  528. //
  529. if ( KeyValues *pPeerCommand = new KeyValues( "TeamSearchLink::Dedicated" ) )
  530. {
  531. KeyValues::AutoDelete autodelete( pPeerCommand );
  532. pPeerCommand->SetString( "run", "xuid" );
  533. pPeerCommand->SetUint64( "runxuid", m_xuidLinkPeer );
  534. pServerInfo->SetInt( "team", 2 );
  535. pPeerCommand->AddSubKey( pServerInfo->MakeCopy() );
  536. LinkSysSession()->Command( pPeerCommand );
  537. }
  538. // Notify our team about the server
  539. if ( KeyValues *pOurTeamNotify = new KeyValues( "TeamSearchResult::Dedicated" ) )
  540. {
  541. pOurTeamNotify->SetPtr( "dsresult", &dsResult );
  542. pServerInfo->SetInt( "team", 1 );
  543. pOurTeamNotify->AddSubKey( pServerInfo );
  544. OnSearchEvent( pOurTeamNotify );
  545. }
  546. m_iLinkState = STATE_LINK_FINISHED;
  547. }
  548. void CMatchSessionOnlineTeamSearchLinkBase::StartWaitingForPeerServer()
  549. {
  550. m_iLinkState = STATE_WAITING_FOR_PEER_SERVER;
  551. // Notify everybody that peer is searching for game server
  552. OnSearchEvent( new KeyValues(
  553. "OnMatchSessionUpdate",
  554. "state", "progress",
  555. "progress", "peerserver"
  556. ) );
  557. }
  558. //////////////////////////////////////////////////////////////////////////
  559. //
  560. // Link host implementation
  561. //
  562. //
  563. void CMatchSessionOnlineTeamSearchLinkHost::LinkInit()
  564. {
  565. m_iLinkState = STATE_SUBMIT_STATS;
  566. }
  567. void CMatchSessionOnlineTeamSearchLinkHost::LinkUpdate()
  568. {
  569. switch ( m_iLinkState )
  570. {
  571. case STATE_SUBMIT_STATS:
  572. {
  573. #ifdef _X360
  574. m_pXlspConnection = new CXlspConnection( false );
  575. CUtlVector< KeyValues * > arrCommands;
  576. #endif
  577. if ( KeyValues *pCmd = new KeyValues( "stat_agg" ) )
  578. {
  579. int numSeconds = int( 1 + Plat_FloatTime() - m_flCreationTime );
  580. numSeconds = ClampArrayBounds( numSeconds, 30 * 60 ); // 30 minutes
  581. pCmd->SetInt( "search_team_time", numSeconds );
  582. #ifdef _X360
  583. arrCommands.AddToTail( pCmd );
  584. #elif !defined( NO_STEAM ) && !defined( NO_STEAM_GAMECOORDINATOR )
  585. CGCClientJobUpdateStats *pJob = new CGCClientJobUpdateStats( pCmd );
  586. pJob->StartJob( NULL );
  587. #else
  588. pCmd->deleteThis();
  589. #endif
  590. }
  591. #ifdef _X360
  592. m_pXlspCommandBatch = new CXlspConnectionCmdBatch( m_pXlspConnection, arrCommands );
  593. #endif
  594. }
  595. m_iLinkState = STATE_REPORTING_STATS;
  596. break;
  597. case STATE_REPORTING_STATS:
  598. #ifdef _X360
  599. m_pXlspCommandBatch->Update();
  600. if ( !m_pXlspCommandBatch->IsFinished() )
  601. return;
  602. m_pXlspCommandBatch->Destroy();
  603. m_pXlspCommandBatch = NULL;
  604. m_pXlspConnection->Destroy();
  605. m_pXlspConnection = NULL;
  606. #endif
  607. m_iLinkState = STATE_CONFIRM_JOIN;
  608. break;
  609. case STATE_CONFIRM_JOIN:
  610. if ( KeyValues *pPeerCommand = new KeyValues( "TeamSearchLink::HostConfirmJoinReady" ) )
  611. {
  612. KeyValues::AutoDelete autodelete( pPeerCommand );
  613. pPeerCommand->SetString( "run", "xuid" );
  614. pPeerCommand->SetUint64( "runxuid", m_xuidLinkPeer );
  615. pPeerCommand->AddSubKey( m_pHostSession->GetSessionSettings()->MakeCopy() ); // Submit a copy of our session settings as well
  616. m_pSysSessionHost->Command( pPeerCommand );
  617. }
  618. m_iLinkState = STATE_CONFIRM_JOIN_WAIT;
  619. break;
  620. case STATE_CONFIRM_JOIN_WAIT:
  621. // Waiting for client to tell us how to select server
  622. break;
  623. }
  624. CMatchSessionOnlineTeamSearchLinkBase::LinkUpdate();
  625. }
  626. void CMatchSessionOnlineTeamSearchLinkHost::LinkCommand( KeyValues *pCommand )
  627. {
  628. char const *szCommand = pCommand->GetName();
  629. if ( !Q_stricmp( "TeamSearchLink::TeamLinkSessionUpdate", szCommand ) )
  630. {
  631. if ( KeyValues *pUpdatePkt = pCommand->GetFirstTrueSubKey() )
  632. {
  633. m_pSettings->MergeFrom( pUpdatePkt );
  634. RememberHostSessionUpdatePacket( pUpdatePkt->MakeCopy() );
  635. }
  636. }
  637. else if ( !Q_stricmp( "TeamSearchLink::HostHosting", szCommand ) )
  638. {
  639. // We are going to host
  640. char const *szLinkHostServer = m_pSettings->GetString( "options/server", "" );
  641. if ( !Q_stricmp( szLinkHostServer, "listen" ) )
  642. StartHostingListenServer();
  643. else
  644. StartDedicatedServerSearch();
  645. return;
  646. }
  647. else if ( !Q_stricmp( "TeamSearchLink::ClientHosting", szCommand ) )
  648. {
  649. // Our peer is going to host
  650. StartWaitingForPeerServer();
  651. return;
  652. }
  653. CMatchSessionOnlineTeamSearchLinkBase::LinkCommand( pCommand );
  654. }
  655. //////////////////////////////////////////////////////////////////////////
  656. //
  657. // Link client implementation
  658. //
  659. //
  660. void CMatchSessionOnlineTeamSearchLinkClient::LinkInit()
  661. {
  662. m_iLinkState = STATE_WAITING_FOR_HOST_READY;
  663. }
  664. void CMatchSessionOnlineTeamSearchLinkClient::LinkUpdate()
  665. {
  666. switch ( m_iLinkState )
  667. {
  668. case STATE_WAITING_FOR_HOST_READY:
  669. // We are waiting for host to report statistics and get the merged teamlink lobby ready
  670. return;
  671. case STATE_CONFIRM_JOIN:
  672. // m_pSettings has been set to the aggregate of host settings
  673. // and our members, so we can inspect host's preference of the Server to
  674. // decide who is going to search for the game server
  675. {
  676. char const *szLinkHostServer = m_pSettings->GetString( "options/server", "" );
  677. char const *szOurServer = m_pHostSession->GetSessionSettings()->GetString( "options/server", "" );
  678. if ( Q_stricmp( szLinkHostServer, "listen" ) &&
  679. !Q_stricmp( szOurServer, "listen" ) )
  680. {
  681. // Host doesn't care, but we need listen server
  682. m_pSysSessionClient->Command( KeyValues::AutoDeleteInline( new KeyValues(
  683. "TeamSearchLink::ClientHosting", "run", "host" ) ) );
  684. StartHostingListenServer();
  685. }
  686. else
  687. {
  688. // Let host find the server
  689. m_pSysSessionClient->Command( KeyValues::AutoDeleteInline( new KeyValues(
  690. "TeamSearchLink::HostHosting", "run", "host" ) ) );
  691. StartWaitingForPeerServer();
  692. }
  693. }
  694. return;
  695. }
  696. CMatchSessionOnlineTeamSearchLinkBase::LinkUpdate();
  697. }
  698. void CMatchSessionOnlineTeamSearchLinkClient::LinkCommand( KeyValues *pCommand )
  699. {
  700. char const *szCommand = pCommand->GetName();
  701. if ( !Q_stricmp( "TeamSearchLink::HostConfirmJoinReady", szCommand ) )
  702. {
  703. // Host has sent us full settings of the host session,
  704. // let's try to reconcile and summarize the settings and prepare an update package if needed
  705. KeyValues *pRemoteSettings = pCommand->GetFirstTrueSubKey();
  706. KeyValues *pLocalSettings = m_pHostSession->GetSessionSettings();
  707. if ( KeyValues *pUpdatePkt = g_pMMF->GetMatchTitleGameSettingsMgr()->PrepareTeamLinkForGame( pLocalSettings, pRemoteSettings ) )
  708. {
  709. if ( KeyValues *pHostCmd = new KeyValues( "TeamSearchLink::TeamLinkSessionUpdate", "run", "host" ) )
  710. {
  711. pHostCmd->AddSubKey( pUpdatePkt->MakeCopy() );
  712. m_pSysSessionClient->Command( KeyValues::AutoDeleteInline( pHostCmd ) );
  713. }
  714. m_pSettings->MergeFrom( pUpdatePkt );
  715. RememberHostSessionUpdatePacket( pUpdatePkt );
  716. }
  717. // Host is now ready
  718. if ( m_iLinkState == STATE_WAITING_FOR_HOST_READY )
  719. m_iLinkState = STATE_CONFIRM_JOIN;
  720. return;
  721. }
  722. CMatchSessionOnlineTeamSearchLinkBase::LinkCommand( pCommand );
  723. }